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 e30604419fe..91201b51663 100644
--- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx
+++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.test.tsx
@@ -1094,6 +1094,25 @@ describe("ComplexityRouterConfig tier editing", () => {
expect(screen.queryByRole("button", { name: "Edit tiers" })).not.toBeInTheDocument();
});
+ it("surfaces the caller's orphaned-rule verdict while editing, so Done is not a silent exit", () => {
+ renderEditor(customValue, { keywordRulesError: "Keyword rule(s) 1 route to a tier this router no longer has" });
+ expect(
+ screen.getByText("Keyword rule(s) 1 route to a tier this router no longer has", { exact: false }),
+ ).toBeInTheDocument();
+ });
+
+ it("keeps the orphaned-rule verdict out of the collapsed view, where the submit tooltip owns it", () => {
+ renderWithProviders(
+ ,
+ );
+ expect(screen.queryByText("route to a tier this router no longer has", { exact: false })).not.toBeInTheDocument();
+ });
+
it("renders the four built-in tiers before any edit, unchanged", () => {
renderWithProviders();
expect(screen.getByRole("button", { name: "Edit tiers" })).toBeInTheDocument();
diff --git a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx
index 8dc4898f1ed..153afa0b586 100644
--- a/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx
+++ b/ui/litellm-dashboard/src/components/add_model/ComplexityRouterConfig.tsx
@@ -197,10 +197,11 @@ const TierSetToolbar: React.FC<{
isCustomSet: boolean;
rowCount: number;
rowsError: string | null;
+ keywordRulesError: string | null | undefined;
onEditingChange: ((editing: boolean) => void) | undefined;
onAdd: () => void;
onRestore: () => void;
-}> = ({ editing, isCustomSet, rowCount, rowsError, onEditingChange, onAdd, onRestore }) => (
+}> = ({ editing, isCustomSet, rowCount, rowsError, keywordRulesError, onEditingChange, onAdd, onRestore }) => (
<>
{editing ? (
@@ -234,6 +235,11 @@ const TierSetToolbar: React.FC<{
and an edited set requires the LLM classification method
)}
+ {editing && keywordRulesError && (
+
+ {keywordRulesError}. Edit the rules under Advanced: Keyword/Semantic Matching, or bring the tier back
+
+ )}
>
);
@@ -419,6 +425,8 @@ interface ComplexityRouterConfigProps {
// rules or semantic matching, so it renders this component without them.
keywordTierRules?: KeywordTierRule[];
onKeywordTierRulesChange?: (rules: KeywordTierRule[]) => void;
+ /** getKeywordTierRulesError's verdict, owned by the caller: importing it here would be an import cycle. */
+ keywordRulesError?: string | null;
semanticMatchingEnabled?: boolean;
onSemanticMatchingEnabledChange?: (enabled: boolean) => void;
embeddingModel?: string;
@@ -569,6 +577,7 @@ const ComplexityRouterConfig: React.FC
= ({
onCustomTechnicalKeywordsChange,
keywordTierRules = [],
onKeywordTierRulesChange,
+ keywordRulesError,
semanticMatchingEnabled = false,
onSemanticMatchingEnabledChange,
embeddingModel,
@@ -739,6 +748,7 @@ const ComplexityRouterConfig: React.FC = ({
isCustomSet={Boolean(customTierSet)}
rowCount={tierRows.length}
rowsError={tierRowsError}
+ keywordRulesError={keywordRulesError}
onEditingChange={onEditingTiersChange}
onAdd={addCustomTier}
onRestore={exitToBuiltInTiers}
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 79fa2dab4f8..01cb41bcb95 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
@@ -283,6 +283,25 @@ describe("AddAutoRouterTab", () => {
expect(screen.queryByText("At least one keyword is required")).not.toBeInTheDocument();
});
+ it("shows the orphaned-rule reason in the tier editor when a rule's tier is removed", async () => {
+ const user = userEvent.setup();
+ vi.mocked(getMissingTiersError).mockReturnValue(null);
+
+ renderWithProviders();
+
+ await user.type(screen.getByPlaceholderText(/smart_router/i), "orphan-rule-router");
+ expandDetailedConfiguration();
+ await user.click(screen.getByText("Advanced: Keyword/Semantic Matching"));
+ await user.click(screen.getByRole("button", { name: /add keyword rule/i }));
+ await addKeyword(user, screen.getByText("Keywords 1").closest("div") as HTMLElement, "invoice");
+
+ await user.click(screen.getByRole("button", { name: "Edit tiers" }));
+ await user.click(screen.getByRole("button", { name: "Remove the COMPLEX tier" }));
+
+ expect(await screen.findByText(/route to a tier this router no longer has/)).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: /add auto router/i })).toBeDisabled();
+ });
+
it("marks only the offending keyword row, leaving a filled one alone", async () => {
const user = userEvent.setup();
vi.mocked(getMissingTiersError).mockReturnValue(null);
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 0249b21762a..4e5e5e8d460 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
@@ -581,6 +581,10 @@ const AddAutoRouterTab: React.FC = ({
onCustomTechnicalKeywordsChange={setCustomTechnicalKeywords}
keywordTierRules={keywordTierRules}
onKeywordTierRulesChange={setKeywordTierRules}
+ keywordRulesError={getKeywordTierRulesError(
+ keywordTierRules,
+ activeTierRows(complexityRouterConfig),
+ )}
semanticMatchingEnabled={semanticMatchingEnabled}
onSemanticMatchingEnabledChange={setSemanticMatchingEnabled}
embeddingModel={embeddingModel}
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 184a4795b12..425d5d51f06 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
@@ -687,6 +687,10 @@ const EditAutoRouterModal: React.FC = ({
onCustomTechnicalKeywordsChange={setCustomTechnicalKeywords}
keywordTierRules={keywordTierRules}
onKeywordTierRulesChange={setKeywordTierRules}
+ keywordRulesError={getKeywordTierRulesError(
+ keywordTierRules,
+ activeTierRows(complexityRouterConfig),
+ )}
semanticMatchingEnabled={semanticMatchingEnabled}
onSemanticMatchingEnabledChange={setSemanticMatchingEnabled}
embeddingModel={embeddingModel}