diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx index ac443f28157..9d554ab2e42 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx @@ -1142,7 +1142,7 @@ describe("ComplexityRouterConfig tier editing", () => { tier_model_params: { sec: { "gpt-4": { reasoning_effort: "high" } }, SIMPLE: { "gpt-3.5-turbo": {} } }, }; const { committed } = renderEditor(withEfforts); - fireEvent.click(screen.getByRole("button", { name: "Use built-in tiers" })); + fireEvent.click(screen.getByRole("button", { name: "Restore defaults" })); const next = committed(); expect(next.custom_tier_set).toBeUndefined(); expect(Object.keys(next.tier_model_params ?? {})).toEqual(["SIMPLE"]); @@ -1161,13 +1161,13 @@ describe("ComplexityRouterConfig tier editing", () => { }, }; const { committed } = renderEditor(renamed); - fireEvent.click(screen.getByRole("button", { name: "Use built-in tiers" })); + fireEvent.click(screen.getByRole("button", { name: "Restore defaults" })); const next = committed(); expect(next.tiers.COMPLEX).toEqual(["gpt-4"]); expect(next.tier_model_params).toEqual({ COMPLEX: { "gpt-4": { reasoning_effort: "high" } } }); }); - it("refuses to restore the built-in tiers when doing so would pass the tier limit", () => { + it("resets a full tier set back to the four built-ins rather than stacking them on top", () => { const nearLimit: ComplexityRouterConfigValue = { ...customValue, custom_tier_set: { @@ -1180,8 +1180,11 @@ describe("ComplexityRouterConfig tier editing", () => { fallback_tier_id: "row-0", }, }; - renderEditor(nearLimit); - expect(screen.getByRole("button", { name: "Restore defaults" })).toBeDisabled(); + const { committed } = renderEditor(nearLimit); + fireEvent.click(screen.getByRole("button", { name: "Restore defaults" })); + const next = committed(); + expect(next.custom_tier_set).toBeUndefined(); + expect(Object.keys(next.tiers)).toEqual(["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"]); }); it("stops describing the heuristic scorer once an edited tier set replaces it", () => { diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx index 0dc576d6ddd..30278681e4f 100644 --- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx +++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx @@ -26,7 +26,6 @@ import { getCustomTierRowsError, isBuiltInTierName, resolveComplexityDefaultModel, - restoredBuiltInRows, sameTierIdentity, tierRowById, tierRowByName, @@ -406,10 +405,6 @@ const ComplexityRouterConfig: React.FC = ({ ); }; - const restorableRows = restoredBuiltInRows(tierRows, value.tiers); - - const restoreDefaultTiers = () => commitTierRows(restorableRows, currentFallbackId); - // Models and params both come from these rows, so the two cannot be keyed differently. const exitToBuiltInTiers = () => { const { custom_tier_set: _dropped, ...rest } = value; @@ -631,27 +626,9 @@ const ComplexityRouterConfig: React.FC = ({ Done - {customTierSet && TIER_ORDER.some((tier) => !tierRows.some((row) => row.id === tier)) && ( - MAX_TIER_COUNT - ? `Restoring the built-in tiers would make ${restorableRows.length} tiers, past the limit of ${MAX_TIER_COUNT}. Remove a tier first` - : undefined - } - > - - - )} {customTierSet && ( )} diff --git a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts index cffc3aa7e5d..54443d40427 100644 --- a/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts +++ b/ui/litellm-dashboard/src/components/add_model/build_complexity_router_config.test.ts @@ -936,7 +936,7 @@ describe("hydrateCustomTierSet", () => { expect(hydrated?.tiers.find((row) => row.id === hydrated.fallback_tier_id)?.name).toBe("SECURITY_REVIEW"); }); - it("mints the canonical key for a built-in name so Restore defaults recognises the row", () => { + it("mints the canonical key for a built-in name so every pointer into the set is a row id", () => { const hydrated = hydrateCustomTierSet({ tier_definitions: [{ name: "SIMPLE" }, { name: "AUDIT", description: "audits" }], tiers: { SIMPLE: ["a"], AUDIT: ["b"] }, diff --git a/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts b/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts index 979a10c82f5..df50f116f60 100644 --- a/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts +++ b/ui/litellm-dashboard/src/components/add_model/tier_rows.test.ts @@ -12,7 +12,6 @@ import { sameTierIdentity, tierRowById, getCustomTierRowsError, - restoredBuiltInRows, tierParamsByRowId, tierRowByName, } from "./tier_rows"; @@ -117,42 +116,6 @@ describe("CUSTOM_TIER_RESTRICTIONS", () => { }); }); -describe("restoredBuiltInRows", () => { - it("brings missing built-ins back in canonical order and leaves custom rows after them", () => { - const restored = restoredBuiltInRows([definedRow("AUDIT"), { ...definedRow("COMPLEX"), id: "COMPLEX" }], tiers); - expect(restored.map((r) => r.id)).toEqual(["SIMPLE", "MEDIUM", "COMPLEX", "REASONING", "audit"]); - }); - - it("keeps the models an already-present built-in row carries rather than the stale record", () => { - const edited = { ...definedRow("SIMPLE", ["edited"]), id: "SIMPLE" }; - expect(restoredBuiltInRows([edited], tiers).find((r) => r.id === "SIMPLE")?.models).toEqual(["edited"]); - }); -}); - -describe("restoredBuiltInRows name collisions", () => { - it("does not restore a built-in slot whose name a custom row already answers to", () => { - const custom: TierRow[] = [ - { id: "uuid-1", name: "SIMPLE", definition: "operator took this name", models: ["a"] }, - { id: "MEDIUM", name: "MEDIUM", definition: "", models: ["b"] }, - ]; - const restored = restoredBuiltInRows(custom, tiers); - const folded = restored.map((row) => row.name.toLowerCase()); - expect(new Set(folded).size).toBe(folded.length); - expect(restored.filter((row) => row.name === "SIMPLE")).toHaveLength(1); - }); - - it("still restores the built-in slots nothing else claims", () => { - const custom: TierRow[] = [{ id: "uuid-1", name: "AUDIT", definition: "d", models: ["a"] }]; - expect(restoredBuiltInRows(custom, tiers).map((row) => row.id)).toEqual([ - "SIMPLE", - "MEDIUM", - "COMPLEX", - "REASONING", - "uuid-1", - ]); - }); -}); - describe("getCustomTierRowsError", () => { it("accepts a complete set", () => { expect(getCustomTierRowsError(set([definedRow("CASUAL"), definedRow("AUDIT")]))).toBeNull(); diff --git a/ui/litellm-dashboard/src/components/add_model/tier_rows.ts b/ui/litellm-dashboard/src/components/add_model/tier_rows.ts index 67eaf37c79d..3a98fc5a042 100644 --- a/ui/litellm-dashboard/src/components/add_model/tier_rows.ts +++ b/ui/litellm-dashboard/src/components/add_model/tier_rows.ts @@ -55,17 +55,6 @@ export const activeTierRows = (value: ActiveTierSet): ActiveTierRow[] => { return rows.map((row) => ({ ...row, params: value.tier_model_params?.[row.id] ?? {} })); }; -// A custom row can answer to a built-in name without carrying its id, and restoring the slot then -// collides on the uniqueness rule, so a claimed name means that slot stays gone. -export const restoredBuiltInRows = (rows: readonly TierRow[], tiers: ComplexityTiers): TierRow[] => [ - ...TIER_ORDER.flatMap((tier) => { - const slot = rows.find((row) => row.id === tier); - if (slot) return [slot]; - return rows.some((row) => sameTierIdentity(row.name, tier)) ? [] : [builtInRow(tier, tiers)]; - }), - ...rows.filter((row) => !(TIER_ORDER as string[]).includes(row.id)), -]; - export const tierRowById = (rows: readonly T[], id: string | undefined): T | undefined => id === undefined ? undefined : rows.find((row) => row.id === id);