From 851006790cefcbe3d379ad8f14c3d1c46e5b6057 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Tue, 13 Jan 2026 18:16:26 -0800 Subject: [PATCH] Fix model list not showing --- .../components/provider_info_helpers.test.tsx | 301 ++++++++++++++++++ .../src/components/provider_info_helpers.tsx | 16 +- 2 files changed, 309 insertions(+), 8 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx new file mode 100644 index 00000000000..2815cc7bc40 --- /dev/null +++ b/ui/litellm-dashboard/src/components/provider_info_helpers.test.tsx @@ -0,0 +1,301 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + Providers, + getPlaceholder, + getProviderLogoAndName, + getProviderModels, + providerLogoMap, + provider_map, +} from "./provider_info_helpers"; + +describe("provider_info_helpers", () => { + describe("getProviderLogoAndName", () => { + it("should return empty logo and dash display name when providerValue is empty", () => { + const result = getProviderLogoAndName(""); + expect(result).toEqual({ logo: "", displayName: "-" }); + }); + + it("should return empty logo and dash display name when providerValue is undefined", () => { + const result = getProviderLogoAndName(undefined as any); + expect(result).toEqual({ logo: "", displayName: "-" }); + }); + + it("should handle gemini provider value case-insensitively", () => { + const result = getProviderLogoAndName("gemini"); + expect(result.displayName).toBe(Providers.Google_AI_Studio); + expect(result.logo).toBe(providerLogoMap[Providers.Google_AI_Studio]); + }); + + it("should handle GEMINI provider value in uppercase", () => { + const result = getProviderLogoAndName("GEMINI"); + expect(result.displayName).toBe(Providers.Google_AI_Studio); + expect(result.logo).toBe(providerLogoMap[Providers.Google_AI_Studio]); + }); + + it("should map openai provider value to OpenAI display name and logo", () => { + const result = getProviderLogoAndName("openai"); + expect(result.displayName).toBe(Providers.OpenAI); + expect(result.logo).toBe(providerLogoMap[Providers.OpenAI]); + }); + + it("should map anthropic provider value to Anthropic display name and logo", () => { + const result = getProviderLogoAndName("anthropic"); + expect(result.displayName).toBe(Providers.Anthropic); + expect(result.logo).toBe(providerLogoMap[Providers.Anthropic]); + }); + + it("should map azure provider value to Azure display name and logo", () => { + const result = getProviderLogoAndName("azure"); + expect(result.displayName).toBe(Providers.Azure); + expect(result.logo).toBe(providerLogoMap[Providers.Azure]); + }); + + it("should map bedrock provider value to Bedrock display name and logo", () => { + const result = getProviderLogoAndName("bedrock"); + expect(result.displayName).toBe(Providers.Bedrock); + expect(result.logo).toBe(providerLogoMap[Providers.Bedrock]); + }); + + it("should map groq provider value to Groq display name and logo", () => { + const result = getProviderLogoAndName("groq"); + expect(result.displayName).toBe(Providers.Groq); + expect(result.logo).toBe(providerLogoMap[Providers.Groq]); + }); + + it("should handle provider values case-insensitively", () => { + const result = getProviderLogoAndName("OPENAI"); + expect(result.displayName).toBe(Providers.OpenAI); + expect(result.logo).toBe(providerLogoMap[Providers.OpenAI]); + }); + + it("should return provider value as display name when no mapping exists", () => { + const unknownProvider = "unknown_provider"; + const result = getProviderLogoAndName(unknownProvider); + expect(result.displayName).toBe(unknownProvider); + expect(result.logo).toBe(""); + }); + + it("should map provider_map values to valid display names", () => { + const uniqueProviderValues = new Set(Object.values(provider_map)); + uniqueProviderValues.forEach((providerValue) => { + if (providerValue.toLowerCase() !== "gemini") { + const result = getProviderLogoAndName(providerValue); + expect(result.displayName).not.toBe("-"); + expect(result.displayName).toBeTruthy(); + } + }); + }); + }); + + describe("getPlaceholder", () => { + it("should return aiml placeholder for AIML provider", () => { + expect(getPlaceholder(Providers.AIML)).toBe("aiml/flux-pro/v1.1"); + }); + + it("should return gemini-pro placeholder for Vertex_AI provider", () => { + expect(getPlaceholder(Providers.Vertex_AI)).toBe("gemini-pro"); + }); + + it("should return claude-3-opus placeholder for Anthropic provider", () => { + expect(getPlaceholder(Providers.Anthropic)).toBe("claude-3-opus"); + }); + + it("should return claude-3-opus placeholder for Bedrock provider", () => { + expect(getPlaceholder(Providers.Bedrock)).toBe("claude-3-opus"); + }); + + it("should return sagemaker placeholder for SageMaker provider", () => { + expect(getPlaceholder(Providers.SageMaker)).toBe("sagemaker/jumpstart-dft-meta-textgeneration-llama-2-7b"); + }); + + it("should return gemini-pro placeholder for Google_AI_Studio provider", () => { + expect(getPlaceholder(Providers.Google_AI_Studio)).toBe("gemini-pro"); + }); + + it("should return azure_ai placeholder for Azure_AI_Studio provider", () => { + expect(getPlaceholder(Providers.Azure_AI_Studio)).toBe("azure_ai/command-r-plus"); + }); + + it("should return my-deployment placeholder for Azure provider", () => { + expect(getPlaceholder(Providers.Azure)).toBe("my-deployment"); + }); + + it("should return oci placeholder for Oracle provider", () => { + expect(getPlaceholder(Providers.Oracle)).toBe("oci/xai.grok-4"); + }); + + it("should return snowflake placeholder for Snowflake provider", () => { + expect(getPlaceholder(Providers.Snowflake)).toBe("snowflake/mistral-7b"); + }); + + it("should return voyage placeholder for Voyage provider", () => { + expect(getPlaceholder(Providers.Voyage)).toBe("voyage/"); + }); + + it("should return jina_ai placeholder for JinaAI provider", () => { + expect(getPlaceholder(Providers.JinaAI)).toBe("jina_ai/"); + }); + + it("should return volcengine placeholder for VolcEngine provider", () => { + expect(getPlaceholder(Providers.VolcEngine)).toBe("volcengine/"); + }); + + it("should return deepinfra placeholder for DeepInfra provider", () => { + expect(getPlaceholder(Providers.DeepInfra)).toBe("deepinfra/"); + }); + + it("should return fal_ai placeholder for FalAI provider", () => { + expect(getPlaceholder(Providers.FalAI)).toBe("fal_ai/fal-ai/flux-pro/v1.1-ultra"); + }); + + it("should return runwayml placeholder for RunwayML provider", () => { + expect(getPlaceholder(Providers.RunwayML)).toBe("runwayml/gen4_turbo"); + }); + + it("should return watsonx placeholder for Watsonx provider", () => { + expect(getPlaceholder(Providers.Watsonx)).toBe("watsonx/ibm/granite-3-3-8b-instruct"); + }); + + it("should return default gpt-3.5-turbo placeholder for unknown provider", () => { + expect(getPlaceholder("UnknownProvider" as any)).toBe("gpt-3.5-turbo"); + }); + + it("should return default gpt-3.5-turbo placeholder for OpenAI provider", () => { + expect(getPlaceholder(Providers.OpenAI)).toBe("gpt-3.5-turbo"); + }); + }); + + describe("getProviderModels", () => { + const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + afterEach(() => { + consoleSpy.mockClear(); + }); + + it("should return empty array when provider is not provided", () => { + const modelMap = {}; + const result = getProviderModels(undefined as any, modelMap); + expect(result).toEqual([]); + }); + + it("should return empty array when modelMap is not an object type", () => { + const result = getProviderModels(Providers.OpenAI, "not-an-object" as any); + expect(result).toEqual([]); + }); + + it("should return empty array when modelMap is undefined", () => { + const result = getProviderModels(Providers.OpenAI, undefined as any); + expect(result).toEqual([]); + }); + + it("should return empty array when modelMap is empty", () => { + const modelMap = {}; + const result = getProviderModels(Providers.OpenAI, modelMap); + expect(result).toEqual([]); + }); + + it("should return models matching the provider from modelMap", () => { + const modelMap = { + "gpt-3.5-turbo": { litellm_provider: "openai" }, + "gpt-4": { litellm_provider: "openai" }, + "claude-3-opus": { litellm_provider: "anthropic" }, + }; + const result = getProviderModels(Providers.OpenAI, modelMap); + expect(result).toEqual(["gpt-3.5-turbo", "gpt-4"]); + }); + + it("should return models when litellm_provider includes the provider string", () => { + const modelMap = { + "custom-openai-model": { litellm_provider: "custom_openai_endpoint" }, + "another-model": { litellm_provider: "openai" }, + }; + const result = getProviderModels(Providers.OpenAI, modelMap); + expect(result).toContain("custom-openai-model"); + expect(result).toContain("another-model"); + }); + + it("should filter out models with null values", () => { + const modelMap = { + "gpt-3.5-turbo": { litellm_provider: "openai" }, + "null-model": null, + "gpt-4": { litellm_provider: "openai" }, + }; + const result = getProviderModels(Providers.OpenAI, modelMap); + expect(result).toEqual(["gpt-3.5-turbo", "gpt-4"]); + }); + + it("should filter out models without litellm_provider property", () => { + const modelMap = { + "gpt-3.5-turbo": { litellm_provider: "openai" }, + "no-provider-model": { someOtherProperty: "value" }, + }; + const result = getProviderModels(Providers.OpenAI, modelMap); + expect(result).toEqual(["gpt-3.5-turbo"]); + }); + + it("should include both cohere and cohere_chat models for Cohere provider", () => { + const modelMap = { + "cohere-model-1": { litellm_provider: "cohere" }, + "cohere-model-2": { litellm_provider: "cohere_chat" }, + "other-model": { litellm_provider: "openai" }, + }; + const result = getProviderModels(Providers.Cohere, modelMap); + expect(result).toContain("cohere-model-1"); + expect(result).toContain("cohere-model-2"); + expect(result).not.toContain("other-model"); + expect(result.length).toBeGreaterThanOrEqual(2); + }); + + it("should include sagemaker_chat models for SageMaker provider", () => { + const modelMap = { + "sagemaker-model-1": { litellm_provider: "sagemaker_chat" }, + "sagemaker-model-2": { litellm_provider: "sagemaker" }, + "other-model": { litellm_provider: "openai" }, + }; + const result = getProviderModels(Providers.SageMaker, modelMap); + expect(result).toContain("sagemaker-model-1"); + expect(result).not.toContain("other-model"); + }); + + it("should handle modelMap with non-object values", () => { + const modelMap = { + "string-model": "not-an-object", + "number-model": 123, + "valid-model": { litellm_provider: "openai" }, + }; + const result = getProviderModels(Providers.OpenAI, modelMap); + expect(result).toEqual(["valid-model"]); + }); + + it("should log provider key and mapped provider when called", () => { + const modelMap = { "gpt-3.5-turbo": { litellm_provider: "openai" } }; + getProviderModels(Providers.OpenAI, modelMap); + expect(consoleSpy).toHaveBeenCalledWith(`Provider key: ${Providers.OpenAI}`); + expect(consoleSpy).toHaveBeenCalledWith(`Provider mapped to: ${provider_map[Providers.OpenAI]}`); + }); + + it("should return empty array for provider with no matching models", () => { + const modelMap = { + "gpt-3.5-turbo": { litellm_provider: "openai" }, + "claude-3-opus": { litellm_provider: "anthropic" }, + }; + const result = getProviderModels(Providers.Bedrock, modelMap); + expect(result).toEqual([]); + }); + + it("should handle multiple providers correctly", () => { + const modelMap = { + "gpt-3.5-turbo": { litellm_provider: "openai" }, + "claude-3-opus": { litellm_provider: "anthropic" }, + "groq-model": { litellm_provider: "groq" }, + }; + const openaiResult = getProviderModels(Providers.OpenAI, modelMap); + const anthropicResult = getProviderModels(Providers.Anthropic, modelMap); + const groqResult = getProviderModels(Providers.Groq, modelMap); + + expect(openaiResult).toEqual(["gpt-3.5-turbo"]); + expect(anthropicResult).toEqual(["claude-3-opus"]); + expect(groqResult).toContain("groq-model"); + }); + }); +}); diff --git a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx index 0eff895addc..246b91f8647 100644 --- a/ui/litellm-dashboard/src/components/provider_info_helpers.tsx +++ b/ui/litellm-dashboard/src/components/provider_info_helpers.tsx @@ -221,14 +221,14 @@ export const getProviderModels = (provider: Providers, modelMap: any): Array { - if ( - value !== null && - typeof value === "object" && - "litellm_provider" in (value as object) && - ((value as any)["litellm_provider"] === custom_llm_provider || - (value as any)["litellm_provider"].includes(custom_llm_provider)) - ) { - providerModels.push(key); + if (value !== null && typeof value === "object" && "litellm_provider" in (value as object)) { + const litellmProvider = (value as any)["litellm_provider"]; + if ( + litellmProvider === custom_llm_provider || + (typeof litellmProvider === "string" && litellmProvider.includes(custom_llm_provider)) + ) { + providerModels.push(key); + } } }); // Special case for cohere