From 0bd1fcd940a265a892255f520c29334a13f2b2d1 Mon Sep 17 00:00:00 2001
From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Sat, 10 Oct 2026 02:13:26 +0000
Subject: [PATCH] fix(ui): friendly fallback group labels, page padding, short
LiteAdmin action title
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
---
.../src/app/(dashboard)/fallbacks/page.tsx | 6 +--
.../Fallbacks/FallbackSelectionForm.test.tsx | 48 ++++++++++++++++---
.../Fallbacks/FallbackSelectionForm.tsx | 4 +-
.../src/components/liteadmin/operations.ts | 17 +++----
4 files changed, 55 insertions(+), 20 deletions(-)
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/fallbacks/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/fallbacks/page.tsx
index 6ec2a98aa79..b93091d1986 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/fallbacks/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/fallbacks/page.tsx
@@ -6,12 +6,10 @@ import Fallbacks from "@/components/Settings/RouterSettings/Fallbacks/Fallbacks"
export default function FallbacksPage() {
const { accessToken, userRole, userId } = useAuthorized();
return (
-
+
Fallbacks
-
- Automatically retry a different model when the primary fails
-
+
Automatically retry a different model when the primary fails
diff --git a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.test.tsx b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.test.tsx
index 3745616c6dc..49ee62c50d9 100644
--- a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.test.tsx
+++ b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.test.tsx
@@ -53,7 +53,11 @@ describe("FallbackSelectionForm", () => {
it("should display tabs when groups exist", () => {
const groups: FallbackGroup[] = [{ id: "1", primaryModel: null, fallbackModels: [] }];
render(
-
,
+
,
);
expect(screen.getByRole("tab", { name: /group 1/i })).toBeInTheDocument();
});
@@ -61,16 +65,36 @@ describe("FallbackSelectionForm", () => {
it("should display primary model as tab label when set", () => {
const groups: FallbackGroup[] = [{ id: "1", primaryModel: "gpt-4", fallbackModels: [] }];
render(
-
,
+
,
);
expect(screen.getByRole("tab", { name: "gpt-4" })).toBeInTheDocument();
});
+ it("should display friendly label for provider wildcard primary", () => {
+ const groups: FallbackGroup[] = [{ id: "1", primaryModel: "openai/*", fallbackModels: [] }];
+ render(
+
,
+ );
+ expect(screen.getByRole("tab", { name: "All OpenAI Models" })).toBeInTheDocument();
+ });
+
it("should call onGroupsChange when add tab button is clicked", async () => {
const user = userEvent.setup();
const groups: FallbackGroup[] = [{ id: "1", primaryModel: null, fallbackModels: [] }];
render(
-
,
+
,
);
const addTabButton = screen.getByRole("button", { name: /add fallback group/i });
@@ -125,7 +149,11 @@ describe("FallbackSelectionForm", () => {
{ id: "2", primaryModel: "gpt-3.5-turbo", fallbackModels: [] },
];
render(
-
,
+
,
);
const removeButtons = screen.getAllByRole("button", { name: /^remove /i });
@@ -142,7 +170,11 @@ describe("FallbackSelectionForm", () => {
it("should render FallbackGroupConfig for each group", () => {
const groups: FallbackGroup[] = [{ id: "1", primaryModel: null, fallbackModels: [] }];
render(
-
,
+
,
);
expect(screen.getByRole("combobox", { name: /primary model/i })).toHaveValue("");
expect(screen.getByText("Primary Model")).toBeInTheDocument();
@@ -151,7 +183,11 @@ describe("FallbackSelectionForm", () => {
it("should display group with primary and fallback models in FallbackGroupConfig", () => {
const groups: FallbackGroup[] = [{ id: "1", primaryModel: "gpt-4", fallbackModels: ["gpt-3.5-turbo"] }];
render(
-
,
+
,
);
expect(screen.getByRole("tab", { name: "gpt-4" })).toBeInTheDocument();
expect(screen.getAllByText("gpt-4").length).toBeGreaterThan(0);
diff --git a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx
index fdeb886ef04..17036c6c770 100644
--- a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx
+++ b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx
@@ -10,7 +10,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Plus, X } from "lucide-react";
import React, { useEffect, useMemo, useState } from "react";
import { toast } from "@/lib/toast";
-import { fallbackChainOptions, primaryModelOptions } from "./fallbackOptions";
+import { fallbackChainOptions, fallbackPrimaryLabel, primaryModelOptions } from "./fallbackOptions";
import { FallbackGroup, FallbackGroupConfig } from "./FallbackGroupConfig";
interface FallbackSelectionFormProps {
@@ -81,7 +81,7 @@ export function FallbackSelectionForm({
};
const groupLabel = (group: FallbackGroup, index: number) =>
- group.primaryModel ? group.primaryModel : `Group ${index + 1}`;
+ group.primaryModel ? fallbackPrimaryLabel(group.primaryModel) : `Group ${index + 1}`;
if (groups.length === 0) {
return (
diff --git a/ui/litellm-dashboard/src/components/liteadmin/operations.ts b/ui/litellm-dashboard/src/components/liteadmin/operations.ts
index dcc57dc72b1..fbb9571c696 100644
--- a/ui/litellm-dashboard/src/components/liteadmin/operations.ts
+++ b/ui/litellm-dashboard/src/components/liteadmin/operations.ts
@@ -109,7 +109,11 @@ const fallbackSetFields = {
model: text.describe(
"The primary model group, or a provider wildcard like openai/* meaning all models from that provider.",
),
- fallbacks: z.array(fallbackTarget).min(1).max(10),
+ fallbacks: z
+ .array(fallbackTarget)
+ .min(1)
+ .max(10)
+ .describe("Concrete model groups to try in order. Never a wildcard."),
};
const fallbackDeleteFields = { model: text };
@@ -359,15 +363,12 @@ export function createLiteAdminOperations(context: OperationContext) {
query: { ...a, start_date: `${a.start_date} 00:00:00`, end_date: `${a.end_date} 23:59:59` },
}),
),
- operation("read", "fallback")(
- "fallbacks_list",
- "List configured router fallbacks",
- object({}),
- async () => ({ fallbacks: fallbackEntries(await readRouterSettings()) }),
- ),
+ operation("read", "fallback")("fallbacks_list", "List configured router fallbacks", object({}), async () => ({
+ fallbacks: fallbackEntries(await readRouterSettings()),
+ })),
operation("write", "fallback")(
"fallback_set",
- "Set the fallback chain for a primary model. The model may be a provider wildcard like openai/* meaning all models from that provider; wildcards are allowed as model but never inside fallbacks",
+ "Set the fallback chain for a model",
object(fallbackSetFields),
async (a) => {
const settings = await readRouterSettings();