+ {settings.routing_strategy != null && (
+
+ Routing Strategy: {String(settings.routing_strategy)}
+
+ )}
+ {settings.num_retries != null &&
Number of Retries: {String(settings.num_retries)}
}
+ {settings.allowed_fails != null &&
Allowed Failures: {String(settings.allowed_fails)}
}
+ {settings.cooldown_time != null &&
Cooldown Time: {String(settings.cooldown_time)}s
}
+ {settings.timeout != null &&
Timeout: {String(settings.timeout)}s
}
+ {settings.retry_after != null &&
Retry After: {String(settings.retry_after)}s
}
+ {Boolean(settings.enable_tag_filtering) &&
Tag Filtering: Enabled
}
+ {fallbacks.length > 0 && (
+
+
Fallbacks:
+
+ {fallbacks.map(([model, targets]) => (
+
+ {model}
+ ->
+ {Array.isArray(targets) ? targets.join(", ") : String(targets)}
+
+ ))}
+
+
+ )}
+
+ );
+}
diff --git a/ui/litellm-dashboard/src/components/common_components/routerSettingsPayload.test.ts b/ui/litellm-dashboard/src/components/common_components/routerSettingsPayload.test.ts
new file mode 100644
index 00000000000..37d694e5540
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/common_components/routerSettingsPayload.test.ts
@@ -0,0 +1,47 @@
+import { describe, expect, it } from "vitest";
+import { hasRouterSettings, routerSettingsUpdate } from "./routerSettingsPayload";
+
+describe("hasRouterSettings", () => {
+ it("should treat unset, empty and all-null settings as absent", () => {
+ expect(hasRouterSettings(undefined)).toBe(false);
+ expect(hasRouterSettings(null)).toBe(false);
+ expect(hasRouterSettings({})).toBe(false);
+ expect(
+ hasRouterSettings({ num_retries: null, fallbacks: [], model_group_alias: {}, enable_tag_filtering: false }),
+ ).toBe(false);
+ });
+
+ it("should detect configured settings", () => {
+ expect(hasRouterSettings({ num_retries: 3 })).toBe(true);
+ expect(hasRouterSettings({ fallbacks: [{ "gpt-4": ["gpt-4o"] }] })).toBe(true);
+ expect(hasRouterSettings({ enable_tag_filtering: true })).toBe(true);
+ expect(hasRouterSettings({ num_retries: 0 })).toBe(true);
+ });
+});
+
+describe("routerSettingsUpdate", () => {
+ const fallbacks = [{ "gpt-4": ["gpt-4o"] }];
+
+ it("should send the edited settings when the user configured something", () => {
+ expect(routerSettingsUpdate({ fallbacks }, null)).toEqual({ fallbacks });
+ });
+
+ it("should send the edited settings unchanged so an unrelated edit keeps stored fallbacks", () => {
+ expect(routerSettingsUpdate({ fallbacks, num_retries: 2 }, { fallbacks, num_retries: 2 })).toEqual({
+ fallbacks,
+ num_retries: 2,
+ });
+ });
+
+ it("should send cleared settings so removing every fallback reaches the server", () => {
+ expect(routerSettingsUpdate({ fallbacks: null, num_retries: null }, { fallbacks })).toEqual({
+ fallbacks: null,
+ num_retries: null,
+ });
+ });
+
+ it("should leave the field off when nothing is stored and nothing was configured", () => {
+ expect(routerSettingsUpdate({ fallbacks: null, num_retries: null }, {})).toBeUndefined();
+ expect(routerSettingsUpdate(undefined, { fallbacks })).toBeUndefined();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/common_components/routerSettingsPayload.ts b/ui/litellm-dashboard/src/components/common_components/routerSettingsPayload.ts
new file mode 100644
index 00000000000..7caec880449
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/common_components/routerSettingsPayload.ts
@@ -0,0 +1,28 @@
+import { RouterSettingsAccordionValue } from "./RouterSettingsAccordion";
+
+export type RouterSettings = RouterSettingsAccordionValue["router_settings"];
+
+const isMeaningfulRouterSetting = (value: unknown): boolean => {
+ if (value === null || value === undefined || value === "" || value === false) return false;
+ if (Array.isArray(value)) return value.length > 0;
+ if (typeof value === "object") return Object.keys(value).length > 0;
+ return true;
+};
+
+export const hasRouterSettings = (settings: Record