mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
This commit is contained in:
parent
e790d9bbb2
commit
14683b502b
7 changed files with 90 additions and 7 deletions
|
|
@ -132,6 +132,22 @@ describe("AllModelsTable", () => {
|
|||
expect(onModelIdClick).toHaveBeenCalledWith("model-1");
|
||||
});
|
||||
|
||||
it("should display the model metadata logo for a gateway-routed model", () => {
|
||||
const logoUrl = "https://cdn.example.com/gateway.svg";
|
||||
render(
|
||||
<AllModelsTable
|
||||
{...baseProps}
|
||||
data={[
|
||||
makeModel({
|
||||
model_info: { ...baseProps.data[0].model_info, logo_url: logoUrl },
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("img", { name: "openai logo" })).toHaveAttribute("src", logoUrl);
|
||||
});
|
||||
|
||||
it("opens the team detail from the team ID cell", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onTeamIdClick = vi.fn();
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ function ModelInformationCell({ model, displayName }: { model: ModelData; displa
|
|||
}
|
||||
>
|
||||
{model.provider ? (
|
||||
<ProviderLogo provider={model.provider} className="size-6 shrink-0" />
|
||||
<ProviderLogo provider={model.provider} logoUrl={model.model_info.logo_url} className="size-6 shrink-0" />
|
||||
) : (
|
||||
<span className="flex size-6 shrink-0 items-center justify-center rounded-md bg-muted text-xs text-muted-foreground">
|
||||
-
|
||||
|
|
@ -88,7 +88,9 @@ function ModelInformationCell({ model, displayName }: { model: ModelData; displa
|
|||
<HoverCardContent align="start" className="w-80">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
{model.provider ? <ProviderLogo provider={model.provider} className="size-4 shrink-0" /> : null}
|
||||
{model.provider ? (
|
||||
<ProviderLogo provider={model.provider} logoUrl={model.model_info.logo_url} className="size-4 shrink-0" />
|
||||
) : null}
|
||||
<span className="truncate text-xs text-muted-foreground">
|
||||
{model.provider || t("modelsAndEndpoints.modelsTableColumns.unknownProvider")}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export interface PerSecondCostTier {
|
|||
|
||||
export interface ModelInfo {
|
||||
id: string;
|
||||
logo_url?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
created_by: string;
|
||||
|
|
|
|||
|
|
@ -205,6 +205,22 @@ describe("ModelInfoView", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("should display the model metadata logo in the provider details", async () => {
|
||||
const logoUrl = "https://cdn.example.com/gateway.svg";
|
||||
mockUseModelsInfo.mockReturnValue({
|
||||
data: {
|
||||
data: [{ ...defaultModelData, model_info: { ...defaultModelData.model_info, logo_url: logoUrl } }],
|
||||
},
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} />, { wrapper });
|
||||
|
||||
expect(await screen.findByRole("img", { name: "openai logo" })).toHaveAttribute("src", logoUrl);
|
||||
expect(screen.getByRole("heading", { name: "openai" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows Kubernetes pods for discovery-enabled models", async () => {
|
||||
mockUseModelsInfo.mockReturnValue({
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import {
|
|||
tagListCall,
|
||||
testConnectionRequest,
|
||||
} from "./networking";
|
||||
import { Logo } from "@/components/molecules/logo/Logo";
|
||||
import { ProviderLogo } from "@/components/molecules/models/ProviderLogo";
|
||||
import { ModelPricingSummary } from "@/components/molecules/models/ModelPricingSummary";
|
||||
import UpdateModelCredentialsModal from "./update_model_credentials_modal";
|
||||
import ModelInfoEditForm, { type ModelEditSubmitValues, type TouchedPricingField } from "./ModelInfoEditForm";
|
||||
|
|
@ -626,7 +626,13 @@ export default function ModelInfoView({
|
|||
<Card className="block p-6">
|
||||
<p className="text-sm">{t("modelInfoView.overview.provider")}</p>
|
||||
<div className="mt-2 flex items-center space-x-2">
|
||||
{modelData.provider && <Logo provider={modelData.provider} className="w-4 h-4" />}
|
||||
{modelData.provider && (
|
||||
<ProviderLogo
|
||||
provider={modelData.provider}
|
||||
logoUrl={modelData.model_info.logo_url}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
)}
|
||||
<h3 className="text-lg font-medium">{modelData.provider || t("modelInfoView.notSet")}</h3>
|
||||
</div>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,15 @@ import { ProviderLogo } from "./ProviderLogo";
|
|||
import * as providerInfoHelpers from "../../provider_info_helpers";
|
||||
|
||||
vi.mock("../../provider_info_helpers");
|
||||
vi.mock("@/lib/serverRootPath", () => ({ serverRootPath: "/litellm" }));
|
||||
|
||||
describe("ProviderLogo", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(providerInfoHelpers.getProviderLogoAndName).mockReturnValue({
|
||||
logo: "/assets/provider-logo.svg",
|
||||
displayName: "Test Provider",
|
||||
});
|
||||
});
|
||||
|
||||
it("should render the component", () => {
|
||||
|
|
@ -20,6 +25,39 @@ describe("ProviderLogo", () => {
|
|||
expect(screen.getByText("t")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["https://cdn.example.com/gateway.svg", "https://cdn.example.com/gateway.svg"],
|
||||
["/ui/assets/logos/gateway.svg", "/litellm/ui/assets/logos/gateway.svg"],
|
||||
])("should display a custom logo from %s", (logoUrl, expectedSrc) => {
|
||||
render(<ProviderLogo provider="test" logoUrl={logoUrl} />);
|
||||
|
||||
expect(screen.getByRole("img", { name: "test logo" })).toHaveAttribute("src", expectedSrc);
|
||||
});
|
||||
|
||||
it.each([undefined, null, ""])("should use the provider logo when the override is %s", (logoUrl) => {
|
||||
render(<ProviderLogo provider="test" logoUrl={logoUrl} />);
|
||||
|
||||
expect(screen.getByRole("img", { name: "test logo" })).toHaveAttribute("src", "/assets/provider-logo.svg");
|
||||
});
|
||||
|
||||
it("should recover when a failed custom logo is replaced", () => {
|
||||
const { rerender } = render(<ProviderLogo provider="test" logoUrl="https://cdn.example.com/broken.svg" />);
|
||||
|
||||
act(() => {
|
||||
fireEvent.error(screen.getByRole("img", { name: "test logo" }));
|
||||
});
|
||||
|
||||
expect(screen.queryByRole("img")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("t")).toBeInTheDocument();
|
||||
|
||||
rerender(<ProviderLogo provider="test" logoUrl="https://cdn.example.com/gateway.svg" />);
|
||||
|
||||
expect(screen.getByRole("img", { name: "test logo" })).toHaveAttribute(
|
||||
"src",
|
||||
"https://cdn.example.com/gateway.svg",
|
||||
);
|
||||
});
|
||||
|
||||
it("should show fallback when image fails to load", () => {
|
||||
vi.mocked(providerInfoHelpers.getProviderLogoAndName).mockReturnValue({
|
||||
logo: "/path/to/logo.png",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,13 @@ import { Logo } from "@/components/molecules/logo/Logo";
|
|||
|
||||
interface ProviderLogoProps {
|
||||
provider: string;
|
||||
logoUrl?: string | null;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ProviderLogo: React.FC<ProviderLogoProps> = ({ provider, className = "w-4 h-4" }) => (
|
||||
<Logo provider={provider} className={className} />
|
||||
);
|
||||
export const ProviderLogo: React.FC<ProviderLogoProps> = ({ provider, logoUrl, className = "w-4 h-4" }) =>
|
||||
logoUrl ? (
|
||||
<Logo src={logoUrl} label={provider} className={className} />
|
||||
) : (
|
||||
<Logo provider={provider} className={className} />
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue