diff --git a/ui/litellm-dashboard/src/components/DashboardHeader.tsx b/ui/litellm-dashboard/src/components/DashboardHeader.tsx
index dd9b9be42ec..044f677cb28 100644
--- a/ui/litellm-dashboard/src/components/DashboardHeader.tsx
+++ b/ui/litellm-dashboard/src/components/DashboardHeader.tsx
@@ -45,7 +45,16 @@ export function DashboardHeader({
- {showWorkerSelector &&
}
+ {showWorkerSelector && (
+ <>
+
+
+
+
+
+
+ >
+ )}
diff --git a/ui/litellm-dashboard/src/components/DeploymentHealth/DeploymentHealth.tsx b/ui/litellm-dashboard/src/components/DeploymentHealth/DeploymentHealth.tsx
index 43311ce2508..2a16a442bb0 100644
--- a/ui/litellm-dashboard/src/components/DeploymentHealth/DeploymentHealth.tsx
+++ b/ui/litellm-dashboard/src/components/DeploymentHealth/DeploymentHealth.tsx
@@ -51,7 +51,6 @@ export const DeploymentHealthIcon = ({ handle }: { handle: DeploymentHealthHandl
);
};
-/** Keeps ignored warnings reachable when the header has no active alerts. */
export const DeploymentHealthLink = ({ handle }: { handle: DeploymentHealthHandle }) => {
const { canView } = useDeploymentHealthAccess();
if (!canView) return null;
diff --git a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.integration.test.tsx b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.integration.test.tsx
index 8786d7df631..64bb8f15585 100644
--- a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.integration.test.tsx
+++ b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.integration.test.tsx
@@ -162,7 +162,7 @@ describe("ViewSwitcher", () => {
);
it.each(["/ui/moyai", "/ui/moyai/sessions/one"])(
- "should show Moyai's icon at %s and let users switch to Chat",
+ "should identify Moyai at %s and let users switch to Chat",
async (pathname) => {
state.pathname = pathname;
getUiSettingsMock.mockResolvedValue({ values: { enable_chat_ui: true } });
@@ -170,7 +170,7 @@ describe("ViewSwitcher", () => {
renderSwitcher();
const trigger = screen.getByRole("button", { name: "Switch workspace, current: Moyai" });
- expect(within(trigger).getByAltText("")).toHaveAttribute("src", expect.stringContaining("moyai-head.svg"));
+ expect(trigger).toHaveTextContent("Moyai");
await user.click(trigger);
await user.click(await screen.findByRole("menuitem", { name: "Chat Chat UI for using LLMs, MCPs, Agents" }));
expect(assignSpy).toHaveBeenCalledWith("/ui/chat");
diff --git a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx
index b7ca47494ec..0dd83094e8f 100644
--- a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx
+++ b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx
@@ -1,6 +1,5 @@
import { useTerminology } from "@/contexts/TerminologyContext";
import React from "react";
-import Image from "next/image";
import { usePathname } from "next/navigation";
import { Button } from "@/components/ui/button";
import {
@@ -9,17 +8,15 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
-import { Check, ChevronsUpDown, LayoutGrid, MessageCircle } from "lucide-react";
+import { Check, ChevronsUpDown } from "lucide-react";
import { usePluginMode } from "@/contexts/PluginModeContext";
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
import { uiHref } from "@/utils/uiHref";
import { cn } from "@/lib/cva.config";
-import moyaiHead from "../../../public/assets/moyai/moyai-head.svg";
const GATEWAY = "ai-gateway";
const CHAT = "chat";
const MOYAI = "moyai";
-type WorkspaceIconName = typeof GATEWAY | typeof CHAT | typeof MOYAI;
function isWorkspaceRoute(pathname: string, workspace: string): boolean {
const routes = [uiHref(workspace)];
@@ -27,45 +24,15 @@ function isWorkspaceRoute(pathname: string, workspace: string): boolean {
return routes.some((route) => pathname === route || pathname.startsWith(`${route}/`));
}
-function activeWorkspaceFor(
+function activeWorkspaceLabel(
isChatRoute: boolean,
isMoyaiRoute: boolean,
pluginLabel: string | undefined,
gatewayLabel: string,
-): { label: string; icon: WorkspaceIconName } {
- if (isChatRoute) return { label: "Chat", icon: CHAT };
- if (isMoyaiRoute) return { label: "Moyai", icon: MOYAI };
- return { label: pluginLabel ?? gatewayLabel, icon: GATEWAY };
-}
-
-function WorkspaceIcon({ workspace }: { workspace: WorkspaceIconName }) {
- if (workspace === CHAT) {
- return (
-
-
-
- );
- }
- if (workspace === MOYAI) {
- return (
-
-
-
- );
- }
- return (
-
-
-
- );
+): string {
+ if (isChatRoute) return "Chat";
+ if (isMoyaiRoute) return "Moyai";
+ return pluginLabel ?? gatewayLabel;
}
interface ViewSwitcherItem {
@@ -95,7 +62,7 @@ export default function ViewSwitcher({ className, contentClassName }: ViewSwitch
const isStandaloneRoute = isChatRoute || isMoyaiRoute;
const gatewayLabel = term("AI Gateway");
- const { label: activeLabel, icon: activeIcon } = activeWorkspaceFor(
+ const activeLabel = activeWorkspaceLabel(
isChatRoute,
isMoyaiRoute,
plugins.find((p) => p.name === mode)?.display_name,
@@ -166,15 +133,9 @@ export default function ViewSwitcher({ className, contentClassName }: ViewSwitch
+
}
>
-
{activeLabel}
diff --git a/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.test.tsx b/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.test.tsx
index ba21aae488c..6b30e1292a2 100644
--- a/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.test.tsx
+++ b/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.test.tsx
@@ -163,4 +163,22 @@ describe("WorkerDropdown", () => {
});
expect(screen.getByText("Worker 3")).toBeInTheDocument();
});
+
+ it("lets users search and switch workers from the compact picker", async () => {
+ mockUseWorker.mockReturnValue({
+ isControlPlane: true,
+ selectedWorker: workers[1],
+ workers,
+ });
+ const user = userEvent.setup();
+
+ render(
);
+ await user.click(screen.getByRole("combobox", { name: "Switch worker, current: Worker 2" }));
+ expect(await screen.findByRole("option", { name: "Worker 2" })).toHaveAttribute("aria-disabled", "true");
+ await user.type(screen.getByRole("combobox", { name: "Search workers" }), "Worker 3");
+ expect(screen.queryByRole("option", { name: "Worker 1" })).not.toBeInTheDocument();
+ await user.click(screen.getByRole("option", { name: "Worker 3" }));
+
+ expect(mockOnWorkerSwitch).toHaveBeenCalledExactlyOnceWith("w3");
+ });
});
diff --git a/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.tsx b/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.tsx
index 432bab8c9ef..2d2393cf7bb 100644
--- a/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.tsx
+++ b/ui/litellm-dashboard/src/components/Navbar/WorkerDropdown/WorkerDropdown.tsx
@@ -2,6 +2,8 @@
import React from "react";
import { Server } from "lucide-react";
+import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox";
+import { Button } from "@/components/ui/button";
import {
Combobox,
ComboboxContent,
@@ -15,6 +17,7 @@ import { useWorker } from "@/hooks/useWorker";
interface WorkerDropdownProps {
onWorkerSwitch: (workerId: string) => void;
+ compact?: boolean;
}
interface WorkerOption {
@@ -23,7 +26,7 @@ interface WorkerOption {
disabled: boolean;
}
-const WorkerDropdown: React.FC
= ({ onWorkerSwitch }) => {
+const WorkerDropdown: React.FC = ({ onWorkerSwitch, compact = false }) => {
const { isControlPlane, selectedWorker, workers } = useWorker();
if (!isControlPlane || !selectedWorker) return null;
@@ -45,12 +48,23 @@ const WorkerDropdown: React.FC = ({ onWorkerSwitch }) => {
}
}}
>
-
-
-
-
-
-
+ {compact ? (
+ }
+ aria-label={`Switch worker, current: ${selectedWorker.name}`}
+ title={selectedWorker.name}
+ >
+
+
+ ) : (
+
+
+
+
+
+ )}
+
+ {compact && }
No matching workers
{(option: WorkerOption) => (
diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx
index ed8c0230d54..488113d726e 100644
--- a/ui/litellm-dashboard/src/components/leftnav.tsx
+++ b/ui/litellm-dashboard/src/components/leftnav.tsx
@@ -734,7 +734,7 @@ const Sidebar_: React.FC = ({