From e4caa1d998379f60f597af779f702ff307b3605d Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Mon, 3 Aug 2026 20:18:08 -0700 Subject: [PATCH] fix(ui): only check classifier/embedding models the config actually submits missingReferencedModels read complexityRouterConfig.classifier_llm_config and embeddingModel unconditionally, but buildComplexityRouterConfig only includes classifier_llm_config when classifierType is "llm" and embedding_model when semanticMatchingEnabled is on. A dormant selection left over from a toggle no longer in effect (embeddingModel still set after turning semantic matching off, or a classifier_llm_config seeded with model: "" before a caller picks one) was being checked and reported as a missing model that would never actually be submitted, wrongly disabling the button. Gate both fields on the same conditions buildComplexityRouterConfig itself uses. Also hardened getRequiredModels to filter out empty-string models, not just null/undefined, so a not-yet-chosen classifier model can't be misread as a real reference even when its field is legitimately included. --- .../src/components/add_model/add_auto_router_tab.tsx | 10 +++++++--- .../src/lib/autorouter_presets.test.ts | 12 ++++++++++++ ui/litellm-dashboard/src/lib/autorouter_presets.ts | 4 +++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx index 34e52c79739..0a53a9c3dec 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx @@ -201,12 +201,16 @@ const AddAutoRouterTab: React.FC = ({ // Checks the config actually being built, not which preset (if any) it came from: a model that // was available when it entered a tier, whether via a preset or picked by hand, can have gone - // missing since (the caller's access narrowed, or a background refetch never caught it). + // missing since (the caller's access narrowed, or a background refetch never caught it). Only + // includes classifier_llm_config/embedding_model when buildComplexityRouterConfig would actually + // emit them (classifierType === "llm", semanticMatchingEnabled) - otherwise a dormant selection + // left over from a toggle no longer in effect would block submit for a model that never ships. const missingReferencedModels = getMissingModels( { tiers: complexityRouterConfig.tiers, - classifier_llm_config: complexityRouterConfig.classifier_llm_config, - embedding_model: embeddingModel, + classifier_llm_config: + complexityRouterConfig.classifier_type === "llm" ? complexityRouterConfig.classifier_llm_config : undefined, + embedding_model: semanticMatchingEnabled ? embeddingModel : undefined, }, availableModelSet, ); diff --git a/ui/litellm-dashboard/src/lib/autorouter_presets.test.ts b/ui/litellm-dashboard/src/lib/autorouter_presets.test.ts index a2d921292f8..f1036b4c395 100644 --- a/ui/litellm-dashboard/src/lib/autorouter_presets.test.ts +++ b/ui/litellm-dashboard/src/lib/autorouter_presets.test.ts @@ -4,6 +4,7 @@ import { getPresetByKey, getRequiredModelsInPreset, getMissingModelsInPreset, + getRequiredModels, } from "./autorouter_presets"; describe("autorouter_presets", () => { @@ -49,4 +50,15 @@ describe("autorouter_presets", () => { ); expect(getMissingModelsInPreset(preset, new Set(required))).toEqual([]); }); + + // A classifier_llm_config placeholder is seeded with model: "" before a caller picks one; an + // empty string is not a real model reference and must not be reported as an unavailable model. + it("does not treat an empty-string classifier or embedding model as a required model", () => { + const required = getRequiredModels({ + tiers: { SIMPLE: ["gpt-5-nano"], MEDIUM: [], COMPLEX: [], REASONING: [] }, + classifier_llm_config: { model: "" }, + embedding_model: "", + }); + expect(required).toEqual(new Set(["gpt-5-nano"])); + }); }); diff --git a/ui/litellm-dashboard/src/lib/autorouter_presets.ts b/ui/litellm-dashboard/src/lib/autorouter_presets.ts index e95c431dbe6..ad1454fb913 100644 --- a/ui/litellm-dashboard/src/lib/autorouter_presets.ts +++ b/ui/litellm-dashboard/src/lib/autorouter_presets.ts @@ -30,7 +30,9 @@ export const getRequiredModels = ( ): Set => { const { tiers, classifier_llm_config: classifier, embedding_model: embedding } = config; const models = [...tiers.SIMPLE, ...tiers.MEDIUM, ...tiers.COMPLEX, ...tiers.REASONING, classifier?.model, embedding]; - return new Set(models.filter((model): model is string => model != null)); + // Boolean(), not != null: an empty-string placeholder (e.g. classifier_llm_config seeded before a + // model is chosen) is never a real model reference either. + return new Set(models.filter((model): model is string => Boolean(model))); }; export const getMissingModels = (