From b6475bd3435a98e131ad0cf0cc9bae970c3bbf01 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Thu, 6 Aug 2026 15:35:24 -0700 Subject: [PATCH] fix(ui): surface model load failures and align request key with models Throw when both model endpoints fail so ChatUI can show an error instead of an empty list. Use the same resolved key for model listing and chat requests, and drop the fetch_models JSDoc --- .../playground/components/chat_ui/ChatUI.tsx | 5 +++-- .../components/llm_calls/fetch_models.test.ts | 8 ++++++++ .../src/components/llm_calls/fetch_models.tsx | 17 ++++++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx index 26585d45829..5cce2bd04cf 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx @@ -455,7 +455,7 @@ const ChatUI: React.FC = ({ if (!simplified) { void loadModels(); } - void loadMCPServers(userApiKey); + void loadMCPServers(); return () => { cancelled = true; @@ -794,7 +794,8 @@ const ChatUI: React.FC = ({ return; } - const effectiveApiKey = simplified ? accessToken : apiKeySource === "session" ? accessToken : apiKey; + const effectiveApiKey = + simplified || apiKeySource === "session" ? accessToken : debouncedCustomApiKey || apiKey.trim(); if (!effectiveApiKey) { NotificationsManager.fromBackend("Please provide a Virtual Key or select Current UI Session"); diff --git a/ui/litellm-dashboard/src/components/llm_calls/fetch_models.test.ts b/ui/litellm-dashboard/src/components/llm_calls/fetch_models.test.ts index f08143b52f2..d00c17b306a 100644 --- a/ui/litellm-dashboard/src/components/llm_calls/fetch_models.test.ts +++ b/ui/litellm-dashboard/src/components/llm_calls/fetch_models.test.ts @@ -74,4 +74,12 @@ describe("fetchAvailableModels", () => { const models = await fetchAvailableModels("sk-virtual-key"); expect(models).toEqual([{ model_group: "only-from-key", mode: undefined }]); }); + + it("throws when both model endpoints fail", async () => { + mockGet.mockImplementation(async () => { + throw new Error("network down"); + }); + + await expect(fetchAvailableModels("sk-virtual-key")).rejects.toThrow("network down"); + }); }); diff --git a/ui/litellm-dashboard/src/components/llm_calls/fetch_models.tsx b/ui/litellm-dashboard/src/components/llm_calls/fetch_models.tsx index cc74355539f..d1e90fda43d 100644 --- a/ui/litellm-dashboard/src/components/llm_calls/fetch_models.tsx +++ b/ui/litellm-dashboard/src/components/llm_calls/fetch_models.tsx @@ -25,15 +25,10 @@ const dedupeAndSort = (models: ModelGroup[]): ModelGroup[] => { return unique; }; -/** - * Loads models available to the given key. - * - * Prefers OpenAI-compatible `/v1/models` (scoped to the key's access) and enriches - * entries with `mode` from `/model_group/info` when that endpoint is available. - * Falls back to `/model_group/info` alone if `/v1/models` is empty or fails. - */ export const fetchAvailableModels = async (accessToken: string): Promise => { const modeByName = new Map(); + let groupInfoError: unknown; + let listModelsError: unknown; try { const groupInfo = await apiClient.get<{ data?: ModelGroupInfoItem[] }>("/model_group/info", { @@ -46,6 +41,7 @@ export const fetchAvailableModels = async (accessToken: string): Promise