From 7c1745cc7178df28434ddd39b4e6861f64826331 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Mon, 7 Sep 2026 13:55:27 -0700 Subject: [PATCH] feat(ui): make automatic auto-router setup discoverable and show what it configured (#40146) * fix(ui): expand Detailed Configuration after automatic auto-router setup * feat(ui): promote automatic auto-router setup to a callout banner * test(ui): read tier chips through testing-library queries to stay in lint budget * style(ui): drop explanatory comments per repo convention * test(ui): reject unexpected automatic tier models --- .../add_model/add_auto_router_tab.test.tsx | 43 +++++++++++++++---- .../add_model/add_auto_router_tab.tsx | 23 +++++----- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx index 5605a993ded..014854ac712 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx @@ -60,6 +60,20 @@ const optionByLabel = (label: string): HTMLElement | undefined => const isOptionDisabled = (option: HTMLElement): boolean => option.getAttribute("aria-disabled") === "true"; +const tierChips = (tier: string): HTMLElement => { + const placeholder = `Select model(s) for ${tier.toLowerCase()} queries`; + const chips = screen + .getAllByRole("toolbar") + .find((candidate) => within(candidate).queryByLabelText(placeholder) !== null); + if (!chips) throw new Error(`No tier row found for "${tier}"`); + return chips; +}; + +const expectTierModel = (tier: string, model: string): void => { + const chips = within(tierChips(tier)).getAllByLabelText(/.+/, { selector: '[data-slot="combobox-chip"]' }); + expect(chips.map((chip) => chip.getAttribute("aria-label"))).toEqual([model]); +}; + const selectTemplate = async (label: string): Promise => { await userEvent.click(optionByLabel(label)!); }; @@ -188,11 +202,10 @@ describe("AddAutoRouterTab", () => { const button = await screen.findByTestId("configure-automatically-button"); await userEvent.click(button); - expect( - screen.getByText( - /Simple: gpt-5.6-luna.*Medium: claude-sonnet-5.*Complex: claude-opus-5.*Reasoning: claude-opus-5/, - ), - ).toBeInTheDocument(); + expectTierModel("Simple", "gpt-5.6-luna"); + expectTierModel("Medium", "claude-sonnet-5"); + expectTierModel("Complex", "claude-opus-5"); + expectTierModel("Reasoning", "claude-opus-5"); expect(toast.success).not.toHaveBeenCalledWith(expect.stringContaining("Configured with")); }); @@ -209,9 +222,23 @@ describe("AddAutoRouterTab", () => { const button = await screen.findByTestId("configure-automatically-button"); await userEvent.click(button); - expect( - screen.getByText(/Simple: gpt-5.6-luna.*Medium: claude-sonnet-5.*Complex: gpt-5.6-sol.*Reasoning: gpt-5.6-sol/), - ).toBeInTheDocument(); + expectTierModel("Simple", "gpt-5.6-luna"); + expectTierModel("Medium", "claude-sonnet-5"); + expectTierModel("Complex", "gpt-5.6-sol"); + expectTierModel("Reasoning", "gpt-5.6-sol"); + }); + + it("opens Detailed Configuration on the tiers automatic setup just filled in", async () => { + const simpleModel = "gpt-5.6-luna"; + mockFetchAvailableModels.mockResolvedValue([...ALL_FAMILY_MODELS, { model_group: simpleModel, mode: "chat" }]); + renderWithProviders(); + + expect(screen.queryByText("Complexity Tier Configuration")).not.toBeInTheDocument(); + + await userEvent.click(await screen.findByTestId("configure-automatically-button")); + + expect(screen.getByText("Complexity Tier Configuration")).toBeInTheDocument(); + expectTierModel("Simple", simpleModel); }); // Nothing is filled in, so there is nothing to submit. The button reports that itself instead of 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 60780f9cbe0..2d0d6bc2fd8 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 @@ -207,10 +207,6 @@ const AddAutoRouterTab: React.FC = ({ const [isSubmitting, setIsSubmitting] = useState(false); const [selectedPreset, setSelectedPreset] = useState(undefined); - // Closed by default: a caller opens it deliberately, either by clicking it or by choosing Custom - // (which expands it automatically, since there's nothing else to show them their config from). A - // preset re-collapses it after prefilling, offering the same "here's what got filled in, expand to - // change it" affordance. A caller can always toggle it manually at any point. const [detailsExpanded, setDetailsExpanded] = useState(false); const [isRoutingTestVisible, setIsRoutingTestVisible] = useState(false); @@ -335,7 +331,7 @@ const AddAutoRouterTab: React.FC = ({ if (automaticRouterConfig === null) return; setSelectedPreset(undefined); applyPrefill({ ...buildEmptyPrefill(), complexityRouterConfig: automaticRouterConfig }); - setDetailsExpanded(false); + setDetailsExpanded(true); toast.success("Automatic setup created", { description: tierConfigSummary(automaticRouterConfig) }); }; @@ -543,14 +539,15 @@ const AddAutoRouterTab: React.FC = ({ {!automaticSetupLoading && automaticRouterConfig && ( - +
+
+

Not sure where to start?

+

Let us pick models for each complexity tier.

+
+ +
)}