From d6883d15b0feac1bfe07eaa18414ef14b1a5a7f4 Mon Sep 17 00:00:00 2001
From: Abhimanyu Kapur <38531241+akapur99@users.noreply.github.com>
Date: Sat, 11 Jul 2026 19:08:07 -0700
Subject: [PATCH 3/4] fix(auto_router): flag name field and tier fields
together on empty submit
Clicking Add Auto Router with the name empty returned early with only a
toast, so blank tier selects never got their inline error state. The
empty-name branch now sets showValidationErrors and triggers antd
validation on the name field, so every unfilled mandatory field is
flagged at once. Adds a regression test for the tab component.
---
.../add_model/add_auto_router_tab.test.tsx | 40 +++++++++++++++++++
.../add_model/add_auto_router_tab.tsx | 2 +
2 files changed, 42 insertions(+)
create mode 100644 ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx
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
new file mode 100644
index 00000000000..4713f8c6869
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.test.tsx
@@ -0,0 +1,40 @@
+import { renderWithProviders, screen } from "../../../tests/test-utils";
+import userEvent from "@testing-library/user-event";
+import { vi } from "vitest";
+import { Form } from "antd";
+import AddAutoRouterTab from "./add_auto_router_tab";
+import NotificationManager from "../molecules/notifications_manager";
+
+vi.mock("../networking", () => ({
+ modelAvailableCall: vi.fn().mockResolvedValue({ data: [] }),
+}));
+
+vi.mock("@/components/llm_calls/fetch_models", () => ({
+ fetchAvailableModels: vi.fn().mockResolvedValue([]),
+}));
+
+vi.mock("./handle_add_auto_router_submit", () => ({
+ handleAddAutoRouterSubmit: vi.fn(),
+}));
+
+vi.mock("../molecules/notifications_manager", () => ({
+ default: { fromBackend: vi.fn() },
+}));
+
+const Harness = () => {
+ const [form] = Form.useForm();
+ return
;
+};
+
+describe("AddAutoRouterTab", () => {
+ it("flags every mandatory field when Add Auto Router is clicked with nothing filled", async () => {
+ const user = userEvent.setup();
+ renderWithProviders(
);
+
+ await user.click(screen.getByRole("button", { name: /add auto router/i }));
+
+ expect(await screen.findByText("Auto router name is required")).toBeInTheDocument();
+ expect(screen.getAllByText("This tier is required")).toHaveLength(4);
+ expect(NotificationManager.fromBackend).toHaveBeenCalledWith("Please enter an Auto Router Name");
+ });
+});
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 4e7a72435bf..a74eab0abdd 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
@@ -198,6 +198,8 @@ const AddAutoRouterTab: React.FC
= ({ form, handleOk, acc
const handleAutoRouterSubmit = () => {
const name = form.getFieldValue("auto_router_name");
if (!name) {
+ setShowValidationErrors(true);
+ form.validateFields(["auto_router_name"]).catch(() => undefined);
NotificationManager.fromBackend("Please enter an Auto Router Name");
return;
}
From 0e90f61e48a84ae8fd7610333032a40b22e6c928 Mon Sep 17 00:00:00 2001
From: Abhimanyu Kapur <38531241+akapur99@users.noreply.github.com>
Date: Sat, 11 Jul 2026 19:21:51 -0700
Subject: [PATCH 4/4] fix(auto_router): inline error for missing LLM classifier
model
Selecting the LLM classifier without picking a model only surfaced a
toast on submit; the classifier model select now gets the same red
outline and helper text as the tier and embedding selects once a submit
attempt has failed.
---
.../add_model/ComplexityRouterConfig.test.tsx | 22 +++++++++++++++++++
.../add_model/ComplexityRouterConfig.tsx | 9 ++++++++
2 files changed, 31 insertions(+)
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 0613b0c02ae..a34a8709918 100644
--- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx
+++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx
@@ -226,6 +226,28 @@ describe("ComplexityRouterConfig", () => {
expect(screen.queryByText("This tier is required")).not.toBeInTheDocument();
});
+ it("shows an inline error on the classifier model select when llm is selected without a model", () => {
+ const llmValue: ComplexityRouterConfigValue = {
+ ...defaultValue,
+ classifier_type: "llm",
+ classifier_llm_config: { model: "", timeout_ms: 3000 },
+ };
+ renderWithProviders();
+ fireEvent.click(screen.getByText("Advanced: Classification Method"));
+ expect(screen.getByText("A classifier model is required")).toBeInTheDocument();
+ });
+
+ it("does not show the classifier model error once a classifier model is set", () => {
+ const llmValue: ComplexityRouterConfigValue = {
+ ...defaultValue,
+ classifier_type: "llm",
+ classifier_llm_config: { model: "gpt-3.5-turbo", timeout_ms: 3000 },
+ };
+ renderWithProviders();
+ fireEvent.click(screen.getByText("Advanced: Classification Method"));
+ expect(screen.queryByText("A classifier model is required")).not.toBeInTheDocument();
+ });
+
it("shows a validation error only under unfilled tiers when showValidationErrors is true", () => {
renderWithProviders(
= ({
label: model.model_group,
}));
+ const classifierModelMissing =
+ showValidationErrors && value.classifier_type === "llm" && !value.classifier_llm_config?.model;
+
const handleTierChange = (tier: keyof ComplexityTiers, model: string) => {
onChange({
...value,
@@ -233,7 +236,13 @@ const ComplexityRouterConfig: React.FC = ({
showSearch
style={{ width: "100%" }}
options={modelOptions}
+ status={classifierModelMissing ? "error" : undefined}
/>
+ {classifierModelMissing && (
+
+ A classifier model is required
+
+ )}