From cf1450e05046374c791a8e8fb274d3e236b36fca Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Thu, 27 Aug 2026 20:38:50 -0700 Subject: [PATCH] refactor(ui): extract tier editor sections to clear new lint warnings --- .../add_model/ClassificationMethodConfig.tsx | 86 +++-- .../add_model/ComplexityRouterConfig.tsx | 341 +++++++++++------- .../edit_auto_router_modal.tsx | 7 +- 3 files changed, 262 insertions(+), 172 deletions(-) diff --git a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx index 45e69039332..34839bb01d6 100644 --- a/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ClassificationMethodConfig.tsx @@ -147,6 +147,54 @@ interface ClassificationMethodConfigProps { defaultModel?: string; } +const ClassifierTypeRadios: React.FC<{ + value: ComplexityRouterConfigValue; + classifierType: ClassifierType; + onTypeChange: (classifierType: ClassifierType) => void; +}> = ({ value, classifierType, onTypeChange }) => { + const scorerLocked = Boolean(value.custom_tier_set); + const scorerLockedReason = restrictedBy(value, "heuristicClassifier")?.reason; + return ( + onTypeChange(classifierType as ClassifierType)} + className="w-full" + > +
+ + + + + + + +
+
+ ); +}; + const ClassificationMethodConfig: React.FC = ({ value, onChange, @@ -271,43 +319,7 @@ const ClassificationMethodConfig: React.FC = ({ return ( <> - handleClassifierTypeChange(classifierType as ClassifierType)} - className="w-full" - > -
- - - - - - - -
-
+ {classifierType === "heuristic_first" && (
diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index 2a0b768385c..22f45574583 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -164,6 +164,177 @@ const rowOrigin = (row: TierRow, editing: boolean): string => { return isBuiltInTierName(row.name) ? "built-in" : "custom"; }; +const defaultModelPlaceholderFor = (derivedDefaultModel: string | undefined, isCustomSet: boolean): string => { + if (derivedDefaultModel) return `Derived from tiers: ${derivedDefaultModel}`; + return isCustomSet ? "Add a model to your fallback tier" : "Add a model to the Simple or Medium tier"; +}; + +const builtInTierInfo = (rowId: string): { label: string; description: string; examples: string } | undefined => { + const builtIn = TIER_ORDER.find((tier) => tier === rowId); + return builtIn ? TIER_DESCRIPTIONS[builtIn] : undefined; +}; + +const TierConfigIntro: React.FC<{ value: ComplexityRouterConfigValue }> = ({ value }) => ( + <> + + {heuristicScoringRole(value) === "never" + ? "The complexity router classifies each request with your classifier model and routes it to that tier. Configure which model(s) handle each tier." + : "The complexity router automatically classifies requests by complexity using rule-based scoring (no API calls, <1ms latency). Configure which model(s) handle each tier."} + + + + {restrictedBy(value, "displayNames")?.reason ?? + "Rename a tier to use your own vocabulary in the dashboard and your spend logs. Renaming doesn't change how requests are classified, and callers never see these names."} + {!value.custom_tier_set && + usesLlmClassifier(value.classifier_type) && + " Your classifier model reads these names, so clearer ones can sharpen its choices."} + + +); + +const TierSetToolbar: React.FC<{ + editing: boolean; + isCustomSet: boolean; + rowCount: number; + rowsError: string | null; + onEditingChange: ((editing: boolean) => void) | undefined; + onAdd: () => void; + onRestore: () => void; +}> = ({ editing, isCustomSet, rowCount, rowsError, onEditingChange, onAdd, onRestore }) => ( + <> +
+ {editing ? ( + <> + + + + + {isCustomSet && ( + + )} + + ) : ( + onEditingChange && ( + + ) + )} +
+ {editing && ( + + Add or remove tiers to define your own set. Every custom tier needs a definition the LLM classifier routes on, + and an edited set requires the LLM classification method + + )} + +); + +const FallbackTierField: React.FC<{ + rows: readonly TierRow[]; + fallbackTierId: string; + onValueChange: (rowId: string) => void; +}> = ({ rows, fallbackTierId, onValueChange }) => ( +
+
+ Fallback Tier + + + +
+ activeTierName(row)).map((row) => ({ value: row.id, label: activeTierName(row) }))} + value={fallbackTierId || null} + onValueChange={onValueChange} + placeholder="Pick the tier classifier failures route to" + /> +
+); + +const TierRowHeader: React.FC<{ + row: TierRow; + index: number; + rowCount: number; + label: string; + description: string | undefined; + editing: boolean; + isCustomSet: boolean; + onRemove: () => void; +}> = ({ row, index, rowCount, label, description, editing, isCustomSet, onRemove }) => ( +
+ {label} Tier + + + + + Tier {index + 1} of {rowCount} · {rowOrigin(row, isCustomSet)} + + {editing && ( + + )} +
+); + +const TierRowEditFields: React.FC<{ + row: TierRow; + index: number; + definitionMissing: boolean; + onPatch: (patch: Partial>) => void; +}> = ({ row, index, definitionMissing, onPatch }) => ( + <> + onPatch({ name: event.target.value })} + placeholder="Tier name, e.g. SECURITY_REVIEW" + aria-label={`Name for tier ${index + 1}`} + maxLength={MAX_TIER_NAME_CHARS} + className="mb-2" + /> +