- {model.provider ?
: null}
+ {model.provider ? (
+
+ ) : null}
{model.provider || t("modelsAndEndpoints.modelsTableColumns.unknownProvider")}
diff --git a/ui/litellm-dashboard/src/components/model_dashboard/types.ts b/ui/litellm-dashboard/src/components/model_dashboard/types.ts
index 47c7eab8ba6..2a8ec028aff 100644
--- a/ui/litellm-dashboard/src/components/model_dashboard/types.ts
+++ b/ui/litellm-dashboard/src/components/model_dashboard/types.ts
@@ -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;
diff --git a/ui/litellm-dashboard/src/components/model_info_view.integration.test.tsx b/ui/litellm-dashboard/src/components/model_info_view.integration.test.tsx
index a42db06d251..404ef6e7f20 100644
--- a/ui/litellm-dashboard/src/components/model_info_view.integration.test.tsx
+++ b/ui/litellm-dashboard/src/components/model_info_view.integration.test.tsx
@@ -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(
, { 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: {
diff --git a/ui/litellm-dashboard/src/components/model_info_view.tsx b/ui/litellm-dashboard/src/components/model_info_view.tsx
index efa7caf550d..5a52006632c 100644
--- a/ui/litellm-dashboard/src/components/model_info_view.tsx
+++ b/ui/litellm-dashboard/src/components/model_info_view.tsx
@@ -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({
{t("modelInfoView.overview.provider")}
- {modelData.provider &&
}
+ {modelData.provider && (
+
+ )}
{modelData.provider || t("modelInfoView.notSet")}
diff --git a/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.test.tsx b/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.test.tsx
index 9a9c631890b..b42719510ea 100644
--- a/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.test.tsx
+++ b/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.test.tsx
@@ -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(
);
+
+ 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(
);
+
+ 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(
);
+
+ act(() => {
+ fireEvent.error(screen.getByRole("img", { name: "test logo" }));
+ });
+
+ expect(screen.queryByRole("img")).not.toBeInTheDocument();
+ expect(screen.getByText("t")).toBeInTheDocument();
+
+ rerender(
);
+
+ 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",
diff --git a/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.tsx b/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.tsx
index 4bc11126ae4..eb93dd77e30 100644
--- a/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.tsx
+++ b/ui/litellm-dashboard/src/components/molecules/models/ProviderLogo.tsx
@@ -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
= ({ provider, className = "w-4 h-4" }) => (
-
-);
+export const ProviderLogo: React.FC = ({ provider, logoUrl, className = "w-4 h-4" }) =>
+ logoUrl ? (
+
+ ) : (
+
+ );