From 0917457b9b1b1c6020f3bd2ce3f560291c6192eb Mon Sep 17 00:00:00 2001
From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Wed, 29 Jul 2026 15:08:32 +0000
Subject: [PATCH] feat(ui): show the model family logo in the models table
---
.../components/ModelsTableColumns.tsx | 3 +-
.../molecules/models/ModelLogo.test.tsx | 19 +++++++++
.../components/molecules/models/ModelLogo.tsx | 13 ++++++
.../components/provider_info_helpers.test.tsx | 40 +++++++++++++++++++
.../src/components/provider_info_helpers.tsx | 20 ++++++++++
5 files changed, 94 insertions(+), 1 deletion(-)
create mode 100644 ui/litellm-dashboard/src/components/molecules/models/ModelLogo.test.tsx
create mode 100644 ui/litellm-dashboard/src/components/molecules/models/ModelLogo.tsx
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelsTableColumns.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelsTableColumns.tsx
index 93ee6d9f0ab..ada8c52d798 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelsTableColumns.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/ModelsTableColumns.tsx
@@ -3,6 +3,7 @@
import { ColumnDef } from "@tanstack/react-table";
import { Copy, Info, Loader2, Pencil, RefreshCw, Trash2 } from "lucide-react";
+import { ModelLogo } from "@/components/molecules/models/ModelLogo";
import { ProviderLogo } from "@/components/molecules/models/ProviderLogo";
import { ModelData } from "@/components/model_dashboard/types";
import { DataTableSortHeader } from "@/components/shared/DataTable";
@@ -52,7 +53,7 @@ function ModelInformationCell({ model, displayName }: { model: ModelData; displa
}
>
{model.provider ? (
-
+
) : (
-
diff --git a/ui/litellm-dashboard/src/components/molecules/models/ModelLogo.test.tsx b/ui/litellm-dashboard/src/components/molecules/models/ModelLogo.test.tsx
new file mode 100644
index 00000000000..fe6fa9da271
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/molecules/models/ModelLogo.test.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+import { describe, it, expect } from "vitest";
+import { screen, render } from "@testing-library/react";
+import { ModelLogo } from "./ModelLogo";
+import { getProviderLogoAndName } from "../../provider_info_helpers";
+
+describe("ModelLogo", () => {
+ it("renders the model family logo instead of the aggregating provider logo", () => {
+ render();
+ const img = screen.getByRole("img", { name: "openrouter logo" });
+ expect(img.getAttribute("src")).toContain("qwen");
+ });
+
+ it("renders the provider logo when the model names no known family", () => {
+ render();
+ const img = screen.getByRole("img", { name: "openrouter logo" });
+ expect(img.getAttribute("src")).toBe(getProviderLogoAndName("openrouter").logo);
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/molecules/models/ModelLogo.tsx b/ui/litellm-dashboard/src/components/molecules/models/ModelLogo.tsx
new file mode 100644
index 00000000000..e661e4dfea0
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/molecules/models/ModelLogo.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { Logo } from "@/components/molecules/logo/Logo";
+import { getModelLogo } from "@/components/provider_info_helpers";
+
+interface ModelLogoProps {
+ model: string | undefined;
+ provider: string;
+ className?: string;
+}
+
+export const ModelLogo: React.FC = ({ model, provider, className = "w-4 h-4" }) => (
+
+);
diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx
index 777cdc62987..4bc414de3f5 100644
--- a/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx
+++ b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx
@@ -1,6 +1,7 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
Providers,
+ getModelLogo,
getPlaceholder,
getProviderLogoAndName,
getProviderModels,
@@ -152,6 +153,45 @@ describe("provider_info_helpers", () => {
});
});
+ describe("getModelLogo", () => {
+ it.each([
+ ["scaleway/mistral-small-3.2-24b-instruct-2506", "mistral"],
+ ["scaleway/qwen3-235b-a22b-instruct-2507", "qwen"],
+ ["scaleway/gemma-3-27b-it", "google"],
+ ["scaleway/llama-3.3-70b-instruct", "meta_llama"],
+ ["scaleway/gpt-oss-120b", "openai_small"],
+ ["scaleway/whisper-large-v3", "openai_small"],
+ ["scaleway/deepseek-r1-distill-llama-70b", "deepseek"],
+ ["openrouter/anthropic/claude-sonnet-4.5", "anthropic"],
+ ["openrouter/moonshotai/kimi-k2", "moonshot"],
+ ["bedrock/mistral.mistral-large-2407-v1:0", "mistral"],
+ ["bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0", "anthropic"],
+ ["bedrock/meta.llama3-3-70b-instruct-v1:0", "meta_llama"],
+ ["together_ai/deepseek-ai/DeepSeek-V3", "deepseek"],
+ ["groq/openai/gpt-oss-20b", "openai_small"],
+ ])("resolves %s to the %s family logo", (model, expectedLogoFragment) => {
+ expect(getModelLogo(model, "openrouter")).toContain(expectedLogoFragment);
+ });
+
+ it("falls back to the provider logo when the model names no known family", () => {
+ expect(getModelLogo("scaleway/some-unknown-model", "openrouter")).toBe(getProviderLogoAndName("openrouter").logo);
+ });
+
+ it("falls back to the provider logo when there is no model", () => {
+ expect(getModelLogo(undefined, "bedrock")).toBe(getProviderLogoAndName("bedrock").logo);
+ });
+
+ it("keeps the provider logo for a bare deployment id, so azure deployments stay on the azure mark", () => {
+ const azureLogo = getProviderLogoAndName("azure").logo;
+ expect(getModelLogo("gpt-4o", "azure")).toBe(azureLogo);
+ expect(getModelLogo("my-deployment", "azure")).toBe(azureLogo);
+ });
+
+ it("keeps the provider logo for first-party providers whose models are unprefixed", () => {
+ expect(getModelLogo("claude-sonnet-4-5", "anthropic")).toBe(getProviderLogoAndName("anthropic").logo);
+ });
+ });
+
describe("getPlaceholder", () => {
it("should return aiml placeholder for AIML provider", () => {
expect(getPlaceholder(Providers.AIML)).toBe("aiml/flux-pro/v1.1");
diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx
index fa6b3c79230..cc42c17ae2a 100644
--- a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx
+++ b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx
@@ -400,6 +400,26 @@ export const getProviderLogoAndName = (providerValue: string): { logo: string; d
return { logo, displayName };
};
+const modelFamilyLogoMap: ReadonlyArray = [
+ [/[/.:](mistral|ministral|codestral|pixtral|devstral|voxtral|magistral)/i, mistralLogo.src],
+ [/[/.:](qwen|qwq)/i, qwenLogo.src],
+ [/[/.:](gemma|gemini)/i, googleLogo.src],
+ [/[/.:](llama)/i, metaLlamaLogo.src],
+ [/[/.:](deepseek)/i, deepseekLogo.src],
+ [/[/.:](claude)/i, anthropicLogo.src],
+ [/[/.:](gpt|whisper|o[134](-|$))/i, openaiSmallLogo.src],
+ [/[/.:](command)/i, cohereLogo.src],
+ [/[/.:](grok)/i, xaiLogo.src],
+ [/[/.:](kimi|moonshot)/i, moonshotLogo.src],
+ [/[/.:](minimax)/i, minimaxLogo.src],
+ [/[/.:](nova-)/i, bedrockLogo.src],
+];
+
+export const getModelLogo = (model: string | undefined, provider: string): string => {
+ const familyLogo = model ? modelFamilyLogoMap.find(([motif]) => motif.test(model))?.[1] : undefined;
+ return resolveLogoSrc(familyLogo) ?? getProviderLogoAndName(provider).logo;
+};
+
export const getPlaceholder = (selectedProvider: string): string => {
if (selectedProvider === Providers.AIML) {
return "aiml/flux-pro/v1.1";