From e49b3f05014b300179ab93e85f0dd42229d0bc04 Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Fri, 21 Aug 2026 12:26:26 -0700 Subject: [PATCH] refactor(ui): single normalizer for model group mapping --- .../src/components/llm_calls/fetch_models.tsx | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) 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 ee58a776684..0827a006ed7 100644 --- a/ui/litellm-dashboard/src/components/llm_calls/fetch_models.tsx +++ b/ui/litellm-dashboard/src/components/llm_calls/fetch_models.tsx @@ -19,11 +19,18 @@ interface AvailableModel { supported_reasoning_efforts?: string[] | null; } +const toModelGroup = (item: AvailableModel): ModelGroup => ({ + model_group: item.model_group || item.id || item.model_name || "", + ...(item.mode && { mode: item.mode }), + ...(item.supports_reasoning === true && { supports_reasoning: true }), + ...(item.supported_reasoning_efforts && { supported_reasoning_efforts: item.supported_reasoning_efforts }), +}); + /** * /models carries no capability metadata, so the team-allowed names are joined against - * /model_group/info; a name without a group entry keeps every capability field absent (unknown). - * The group-info endpoint serves team-scoped tokens (row-filtered, verified live), and a failed - * fetch is console.error'd by fetchAvailableModels before the empty fallback here. + * /model_group/info (one extra parallel request; the endpoint has no team filter of its own, and it + * serves team-scoped tokens row-filtered, verified live). A name without a group entry keeps every + * capability field absent; a failed group fetch is console.error'd by fetchAvailableModels. */ export const fetchAvailableModelsForTeam = async (accessToken: string, teamId: string): Promise => { const [response, groups] = await Promise.all([ @@ -44,21 +51,11 @@ export const fetchAvailableModelsForTeam = async (accessToken: string, teamId: s export const fetchAvailableModels = async (accessToken: string): Promise => { try { const fetchedModels = await modelHubCall(accessToken); - - if (fetchedModels?.data.length > 0) { - const models: ModelGroup[] = fetchedModels.data - .map((item: AvailableModel) => ({ - model_group: item.model_group || item.id || item.model_name || "", - mode: item.mode || undefined, - supports_reasoning: item.supports_reasoning === true || undefined, - supported_reasoning_efforts: item.supported_reasoning_efforts ?? undefined, - })) - .filter((model: ModelGroup) => model.model_group !== ""); - - models.sort((a, b) => a.model_group.localeCompare(b.model_group)); - return Array.from(new Map(models.map((model) => [model.model_group, model])).values()); - } - return []; + const models: ModelGroup[] = (fetchedModels?.data ?? []) + .map(toModelGroup) + .filter((model: ModelGroup) => model.model_group !== "") + .sort((a: ModelGroup, b: ModelGroup) => a.model_group.localeCompare(b.model_group)); + return Array.from(new Map(models.map((model) => [model.model_group, model])).values()); } catch (error) { console.error("Error fetching model info:", error); throw error;