diff --git a/litellm/router_strategy/complexity_router/config.py b/litellm/router_strategy/complexity_router/config.py index d917c1b8041..1d03c56050a 100644 --- a/litellm/router_strategy/complexity_router/config.py +++ b/litellm/router_strategy/complexity_router/config.py @@ -56,11 +56,9 @@ DEFAULT_CLASSIFICATION_RUBRIC: Final[ClassificationRubric] = ClassificationRubri LLM_CLASSIFIER_TYPES: Final[frozenset[str]] = frozenset({"llm", "heuristic_first", "hybrid"}) -# The ladder as it has always shipped. NON_REASONING is absent because it is opt-in: an existing -# router must not gain a rubric bullet, a wire label, or a rung it never configured, and the -# heuristic_v2 artifact is trained on exactly these four classes. Read the active ladder off the -# config (`tier_names`, `active_tier_severity_order`) rather than this constant wherever the -# operator's `enable_non_reasoning_tier` can reach. +# Excludes NON_REASONING so an existing router keeps the ladder, rubric and wire labels it already +# has, and so heuristic_v2 keeps mapping onto the four classes its artifact is trained on. Anywhere +# `enable_non_reasoning_tier` can reach, read the ladder off the config instead. TIER_SEVERITY_ORDER: Final[tuple[ComplexityTier, ...]] = ( ComplexityTier.SIMPLE, ComplexityTier.MEDIUM, diff --git a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx index 468dcf5b411..5ec9f72c2a1 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx @@ -237,9 +237,9 @@ const ClassifierTypeRadios: React.FC<{ }; /** - * The NON_REASONING keys a classifier switch should carry forward, or clear. Leaving the flag set - * under a classifier that cannot emit the tier produces a config the backend refuses on save, and - * the switch is disabled there, so the operator would have no way to undo it. + * The NON_REASONING keys a classifier switch carries forward, or clears. Only the LLM classifier + * can emit the tier, and the switch is disabled elsewhere, so a flag left set under another + * classifier would be an unsaveable config the operator could not undo. */ export const nonReasoningTierFields = ( classifierType: ClassifierType, @@ -303,9 +303,6 @@ const ClassificationMethodConfig: React.FC = ({ : undefined, hybrid_boundary_margin: classifierType === "hybrid" ? value.hybrid_boundary_margin ?? DEFAULT_HYBRID_BOUNDARY_MARGIN : undefined, - // Only the LLM classifier can produce NON_REASONING, and the backend rejects the flag - // beside any other type. Clearing it here (with the tier's own pool) is what keeps a - // switch away from LLM from stranding a config that can never be saved. ...nonReasoningTierFields(classifierType, value), }; onChange(nextValue); diff --git a/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx index c0d26874357..622a460fb79 100644 --- a/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx +++ b/ui/litellm-dashboard/src/components/add_model/NonReasoningTierToggle.tsx @@ -15,8 +15,7 @@ const NonReasoningTierToggle: React.FC<{ onChange: (value: ComplexityRouterConfigValue) => void; available: boolean; }> = ({ value, onChange, available }) => { - // Turning it off drops the tier's key rather than leaving an empty pool, which the backend - // rejects; turning it back on restores whatever pool the form still held. + // Off drops the tier's key rather than leaving the empty pool the backend rejects. const handleToggle = (enabled: boolean): void => { const { NON_REASONING: existingPool, ...keptTiers } = value.tiers; const next: ComplexityRouterConfigValue = { diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx index 283fe978790..1ef713cea02 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx @@ -136,10 +136,8 @@ export const hydrateComplexityRouterConfig = ( parsedConfig: StoredComplexityRouterConfig, complexityRouterDefaultModel: string | null | undefined, ): ComplexityRouterConfigValue => { - // `tiers` is rewritten wholesale on save, so a stored tier this misses is deleted from the - // router by any edit at all, including one made for an unrelated reason. NON_REASONING is - // therefore read back from the stored config rather than assumed absent, and the toggle follows - // what is actually stored so the round-trip cannot silently turn the tier off. + // `tiers` is rewritten wholesale on save, so a stored tier this misses is deleted by any edit, + // including one made for an unrelated reason. Hence reading both back rather than assuming four. const storedNonReasoning: string[] = normalizeTierModels(parsedConfig.tiers?.NON_REASONING); const enable_non_reasoning_tier: boolean = parsedConfig.enable_non_reasoning_tier === true || storedNonReasoning.length > 0;