diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json
index 09d0032e6c9..ae09a893194 100644
--- a/ui/litellm-dashboard/eslint-suppressions.json
+++ b/ui/litellm-dashboard/eslint-suppressions.json
@@ -1061,7 +1061,17 @@
"count": 1
}
},
- "src/app/(dashboard)/models-and-endpoints/layout.tsx": {
+ "src/app/(dashboard)/models-and-endpoints/page.tsx": {
+ "no-restricted-imports": {
+ "count": 1
+ }
+ },
+ "src/app/(dashboard)/models-and-endpoints/panels/AddModelPanel.tsx": {
+ "no-restricted-imports": {
+ "count": 1
+ }
+ },
+ "src/app/(dashboard)/models-and-endpoints/panels/LlmCredentialsPanel.tsx": {
"no-restricted-imports": {
"count": 1
}
@@ -4340,4 +4350,4 @@
"count": 1
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/layout.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/layout.test.tsx
deleted file mode 100644
index d47d8b9a04e..00000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/layout.test.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-/* @vitest-environment jsdom */
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { act, render } from "@testing-library/react";
-import { beforeEach, describe, expect, it, vi } from "vitest";
-import ModelsAndEndpointsLayout from "./layout";
-
-const { mockPush, mockReplace, navState } = vi.hoisted(() => ({
- mockPush: vi.fn(),
- mockReplace: vi.fn(),
- navState: { pathname: "/models-and-endpoints", search: "" },
-}));
-vi.mock("next/navigation", () => ({
- usePathname: () => navState.pathname,
- useRouter: () => ({ push: mockPush, replace: mockReplace }),
- useSearchParams: () => new URLSearchParams(navState.search),
-}));
-
-vi.mock("@/components/networking", () => ({ serverRootPath: "" }));
-
-vi.mock("@/components/molecules/cost_optimization_feedback_banner", () => ({ default: () => null }));
-vi.mock("@/components/model_info_view", () => ({
- default: ({ modelId }: { modelId: string }) =>
model:{modelId}
,
-}));
-vi.mock("@/components/team/TeamInfo", () => ({
- default: ({ teamId }: { teamId: string }) => team:{teamId}
,
-}));
-
-const mockUseAuthorized = vi.fn();
-vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ default: () => mockUseAuthorized() }));
-vi.mock("@/app/(dashboard)/hooks/teams/useTeams", () => ({ useTeams: () => ({ data: [] }) }));
-vi.mock("@/app/(dashboard)/hooks/uiSettings/useUISettings", () => ({
- useUISettings: () => ({ data: { values: {} } }),
-}));
-vi.mock("@/app/(dashboard)/models-and-endpoints/useModelDashboardData", () => ({
- useModelDashboardData: () => ({
- availableModelGroups: [],
- availableModelAccessGroups: [],
- allModelsOnProxy: [],
- isLoading: false,
- }),
-}));
-
-const renderLayout = () => {
- const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 } } });
- return render(
-
-
- CHILD
-
- ,
- );
-};
-
-describe("ModelsAndEndpointsLayout", () => {
- beforeEach(() => {
- navState.pathname = "/models-and-endpoints";
- navState.search = "";
- mockPush.mockClear();
- mockReplace.mockClear();
- mockUseAuthorized.mockReturnValue({
- accessToken: "123",
- token: "123",
- userRole: "Admin",
- userId: "123",
- premiumUser: false,
- });
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (global as any).ResizeObserver = class {
- observe() {}
- unobserve() {}
- disconnect() {}
- };
- });
-
- it("renders the admin tab bar and the active tab's page content", () => {
- const { getByRole, getByTestId } = renderLayout();
- expect(getByRole("tab", { name: "LLM Credentials" })).toBeInTheDocument();
- expect(getByRole("tab", { name: "Health Status" })).toBeInTheDocument();
- expect(getByTestId("tab-content")).toHaveTextContent("CHILD");
- });
-
- it("navigates to a tab's path when its tab is clicked", async () => {
- const { getByRole } = renderLayout();
- await act(async () => {
- getByRole("tab", { name: "Health Status" }).click();
- });
- expect(mockPush).toHaveBeenCalledWith(expect.stringMatching(/\/models-and-endpoints\/health\/$/));
- });
-
- it("redirects to the base models path when the tab path is not permitted for the role", async () => {
- const replaceMock = vi.fn();
- const originalLocation = window.location;
- Object.defineProperty(window, "location", {
- configurable: true,
- value: { replace: replaceMock, assign: vi.fn(), href: "http://localhost/", pathname: "/", search: "" },
- });
- mockUseAuthorized.mockReturnValue({
- accessToken: "123",
- token: "123",
- userRole: "Internal User",
- userId: "123",
- premiumUser: false,
- });
- navState.pathname = "/models-and-endpoints/llm-credentials";
- await act(async () => {
- renderLayout();
- });
- expect(replaceMock).toHaveBeenCalledWith(expect.stringMatching(/\/models-and-endpoints\/$/));
- Object.defineProperty(window, "location", { configurable: true, value: originalLocation });
- });
-
- it("renders the model detail overlay from ?model and hides the tabs and page content", () => {
- navState.search = "model=abc-123";
- const { getByTestId, queryByTestId, queryByRole } = renderLayout();
- expect(getByTestId("model-info")).toHaveTextContent("model:abc-123");
- expect(queryByTestId("tab-content")).toBeNull();
- expect(queryByRole("tab", { name: "Health Status" })).toBeNull();
- });
-
- it("renders the team detail overlay from ?team", () => {
- navState.search = "team=team-9";
- const { getByTestId, queryByTestId } = renderLayout();
- expect(getByTestId("team-info")).toHaveTextContent("team:team-9");
- expect(queryByTestId("tab-content")).toBeNull();
- });
-});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/layout.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/layout.tsx
deleted file mode 100644
index e855ebeb5c8..00000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/layout.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-"use client";
-
-import type { ReactNode } from "react";
-import { useMemo, useState } from "react";
-import { Tabs } from "antd";
-import { RefreshIcon } from "@heroicons/react/outline";
-import { useQueryClient } from "@tanstack/react-query";
-import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
-import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
-import { useTabRouting } from "@/app/(dashboard)/hooks/useTabRouting";
-import { all_admin_roles, internalUserRoles, isProxyAdminRole, isUserTeamAdminForAnyTeam } from "@/utils/roles";
-import CostOptimizationFeedbackBanner from "@/components/molecules/cost_optimization_feedback_banner";
-import ModelInfoView from "@/components/model_info_view";
-import TeamInfoView from "@/components/team/TeamInfo";
-import { modelsRoutes, type ModelTabSlug } from "@/app/(dashboard)/models-and-endpoints/tabRoutes";
-import { useModelDetailRouting } from "@/app/(dashboard)/models-and-endpoints/detailNavigation";
-import { useModelDashboardData } from "@/app/(dashboard)/models-and-endpoints/useModelDashboardData";
-
-const BASE_TAB_KEY = "all-models";
-
-const TAB_LABELS: Record = {
- add: "Add Model",
- "llm-credentials": "LLM Credentials",
- "pass-through": "Pass-Through Endpoints",
- health: "Health Status",
- "retry-settings": "Model Retry Settings",
- "model-group-alias": "Model Group Alias",
- "price-data": "Price Data Reload",
-};
-
-export default function ModelsAndEndpointsLayout({ children }: { children: ReactNode }) {
- const { accessToken, userRole, userId: userID, premiumUser } = useAuthorized();
- const { data: teams, isLoading: teamsLoading } = useTeams();
- const { data: uiSettings, isLoading: uiSettingsLoading } = useUISettings();
- const queryClient = useQueryClient();
- const { modelId, teamId, close } = useModelDetailRouting();
- const { availableModelAccessGroups, allModelsOnProxy } = useModelDashboardData();
-
- const [lastRefreshed, setLastRefreshed] = useState("");
-
- const isProxyAdmin = userRole && isProxyAdminRole(userRole);
- const isInternalUser = userRole && internalUserRoles.includes(userRole);
- const isUserTeamAdmin = userID && isUserTeamAdminForAnyTeam(teams ?? null, userID);
- const addModelDisabledForInternalUsers =
- isInternalUser && uiSettings?.values?.disable_model_add_for_internal_users === true;
- const shouldHideAddModelTab = !isProxyAdmin && (addModelDisabledForInternalUsers || !isUserTeamAdmin);
- const isAdmin = all_admin_roles.includes(userRole);
-
- const visibleSlugs = useMemo>(
- () => [
- "",
- ...(shouldHideAddModelTab ? [] : (["add"] as const)),
- ...(isAdmin
- ? (["llm-credentials", "pass-through", "health", "retry-settings", "model-group-alias", "price-data"] as const)
- : []),
- ],
- [shouldHideAddModelTab, isAdmin],
- );
-
- const tabRoutingConfig = {
- routes: modelsRoutes,
- baseTabKey: BASE_TAB_KEY,
- visibleKeys: visibleSlugs.filter(Boolean),
- ready: !teamsLoading && !uiSettingsLoading,
- };
- const { activeKey, onTabChange } = useTabRouting(tabRoutingConfig);
-
- const allModelsLabel = isAdmin ? "All Models" : "Your Models";
- const tabItems = visibleSlugs.map((slug) => {
- const key = slug || BASE_TAB_KEY;
- return {
- key,
- label: slug ? TAB_LABELS[slug] : allModelsLabel,
- children: key === activeKey ? children : null,
- };
- });
-
- const handleRefreshClick = () => {
- setLastRefreshed(new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }));
- queryClient.invalidateQueries({ queryKey: ["models", "list"] });
- };
-
- const invalidateModels = () => queryClient.invalidateQueries({ queryKey: ["models", "list"] });
-
- if (teamId) {
- return (
-
-
-
- );
- }
-
- return (
-
-
-
-
-
Model Management
- {isAdmin ? (
-
Add and manage models for the proxy
- ) : (
-
Add models for teams you are an admin for.
- )}
-
-
-
-
-
- {modelId ? (
-
- ) : (
-
- {lastRefreshed && Last Refreshed: {lastRefreshed}}
-
-
- ),
- }}
- />
- )}
-
-
- );
-}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx
new file mode 100644
index 00000000000..b9dd09d5a71
--- /dev/null
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.test.tsx
@@ -0,0 +1,100 @@
+/* @vitest-environment jsdom */
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { render } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import ModelsAndEndpointsPage from "./page";
+
+vi.mock("./panels/AllModelsPanel", () => ({ default: () => }));
+vi.mock("./panels/AddModelPanel", () => ({ default: () => }));
+vi.mock("./panels/LlmCredentialsPanel", () => ({ default: () => }));
+vi.mock("./panels/PassThroughPanel", () => ({ default: () => }));
+vi.mock("./panels/HealthStatusPanel", () => ({ default: () => }));
+vi.mock("./panels/ModelRetrySettingsPanel", () => ({ default: () => }));
+vi.mock("./panels/ModelGroupAliasPanel", () => ({ default: () => }));
+vi.mock("./panels/PriceDataPanel", () => ({ default: () => }));
+
+const detailState = { modelId: null as string | null, teamId: null as string | null };
+vi.mock("./detailNavigation", () => ({
+ useModelDetailRouting: () => ({ ...detailState, close: vi.fn(), openModel: vi.fn(), openTeam: vi.fn() }),
+}));
+
+vi.mock("@/components/molecules/cost_optimization_feedback_banner", () => ({ default: () => null }));
+vi.mock("@/components/model_info_view", () => ({
+ default: ({ modelId }: { modelId: string }) => model:{modelId}
,
+}));
+vi.mock("@/components/team/TeamInfo", () => ({
+ default: ({ teamId }: { teamId: string }) => team:{teamId}
,
+}));
+
+const mockUseAuthorized = vi.fn();
+vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ default: () => mockUseAuthorized() }));
+vi.mock("@/app/(dashboard)/hooks/teams/useTeams", () => ({ useTeams: () => ({ data: [] }) }));
+vi.mock("@/app/(dashboard)/hooks/uiSettings/useUISettings", () => ({
+ useUISettings: () => ({ data: { values: {} } }),
+}));
+vi.mock("./useModelDashboardData", () => ({
+ useModelDashboardData: () => ({ availableModelAccessGroups: [], allModelsOnProxy: [], availableModelGroups: [] }),
+}));
+
+const ADMIN = { accessToken: "at", token: "t", userRole: "Admin", userId: "u1", premiumUser: false };
+const NON_ADMIN = { accessToken: "at", token: "t", userRole: "Internal User", userId: "u1", premiumUser: false };
+
+const renderPage = () => {
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 } } });
+ return render(
+
+
+ ,
+ );
+};
+
+describe("ModelsAndEndpointsPage", () => {
+ beforeEach(() => {
+ detailState.modelId = null;
+ detailState.teamId = null;
+ mockUseAuthorized.mockReturnValue(ADMIN);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (global as any).ResizeObserver = class {
+ observe() {}
+ unobserve() {}
+ disconnect() {}
+ };
+ });
+
+ it("renders the admin tab bar and the All Models panel by default", () => {
+ const { getByRole, getByTestId } = renderPage();
+ expect(getByRole("tab", { name: "All Models" })).toBeInTheDocument();
+ expect(getByRole("tab", { name: "LLM Credentials" })).toBeInTheDocument();
+ expect(getByRole("tab", { name: "Health Status" })).toBeInTheDocument();
+ expect(getByTestId("panel-all-models")).toBeInTheDocument();
+ });
+
+ it("switches tabs in-memory, mounting only the active panel", async () => {
+ const user = userEvent.setup();
+ const { getByRole, getByTestId, queryByTestId } = renderPage();
+ await user.click(getByRole("tab", { name: "Health Status" }));
+ expect(getByTestId("panel-health")).toBeInTheDocument();
+ expect(queryByTestId("panel-all-models")).toBeNull();
+ });
+
+ it("renders the model detail overlay from the ?model drill-in and hides the tabs", () => {
+ detailState.modelId = "abc-123";
+ const { getByTestId, queryByRole } = renderPage();
+ expect(getByTestId("model-info")).toHaveTextContent("model:abc-123");
+ expect(queryByRole("tab", { name: "All Models" })).toBeNull();
+ });
+
+ it("renders the team detail overlay from the ?team drill-in", () => {
+ detailState.teamId = "team-9";
+ const { getByTestId } = renderPage();
+ expect(getByTestId("team-info")).toHaveTextContent("team:team-9");
+ });
+
+ it("hides admin-only tabs for a non-admin user", () => {
+ mockUseAuthorized.mockReturnValue(NON_ADMIN);
+ const { queryByRole } = renderPage();
+ expect(queryByRole("tab", { name: "LLM Credentials" })).toBeNull();
+ expect(queryByRole("tab", { name: "Health Status" })).toBeNull();
+ });
+});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx
index 546309cfcc1..cb173367459 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx
@@ -1,23 +1,185 @@
"use client";
-import { useState } from "react";
-import AllModelsTab from "@/app/(dashboard)/models-and-endpoints/components/AllModelsTab";
-import { useModelDashboardData } from "@/app/(dashboard)/models-and-endpoints/useModelDashboardData";
+import { useMemo, useState } from "react";
+import { Tabs } from "antd";
+import { RefreshIcon } from "@heroicons/react/outline";
+import { useQueryClient } from "@tanstack/react-query";
+import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
+import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
+import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
+import { all_admin_roles, internalUserRoles, isProxyAdminRole, isUserTeamAdminForAnyTeam } from "@/utils/roles";
+import CostOptimizationFeedbackBanner from "@/components/molecules/cost_optimization_feedback_banner";
+import ModelInfoView from "@/components/model_info_view";
+import TeamInfoView from "@/components/team/TeamInfo";
import { useModelDetailRouting } from "@/app/(dashboard)/models-and-endpoints/detailNavigation";
+import { useModelDashboardData } from "@/app/(dashboard)/models-and-endpoints/useModelDashboardData";
+import AllModelsPanel from "@/app/(dashboard)/models-and-endpoints/panels/AllModelsPanel";
+import AddModelPanel from "@/app/(dashboard)/models-and-endpoints/panels/AddModelPanel";
+import LlmCredentialsPanel from "@/app/(dashboard)/models-and-endpoints/panels/LlmCredentialsPanel";
+import PassThroughPanel from "@/app/(dashboard)/models-and-endpoints/panels/PassThroughPanel";
+import HealthStatusPanel from "@/app/(dashboard)/models-and-endpoints/panels/HealthStatusPanel";
+import ModelRetrySettingsPanel from "@/app/(dashboard)/models-and-endpoints/panels/ModelRetrySettingsPanel";
+import ModelGroupAliasPanel from "@/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel";
+import PriceDataPanel from "@/app/(dashboard)/models-and-endpoints/panels/PriceDataPanel";
-export default function AllModelsPage() {
- const [selectedModelGroup, setSelectedModelGroup] = useState(null);
- const { availableModelGroups, availableModelAccessGroups } = useModelDashboardData();
- const { openModel, openTeam } = useModelDetailRouting();
+type ModelTabSlug =
+ | "add"
+ | "llm-credentials"
+ | "pass-through"
+ | "health"
+ | "retry-settings"
+ | "model-group-alias"
+ | "price-data";
+
+const BASE_TAB_KEY = "all-models";
+
+const TAB_LABELS: Record = {
+ add: "Add Model",
+ "llm-credentials": "LLM Credentials",
+ "pass-through": "Pass-Through Endpoints",
+ health: "Health Status",
+ "retry-settings": "Model Retry Settings",
+ "model-group-alias": "Model Group Alias",
+ "price-data": "Price Data Reload",
+};
+
+const renderPanel = (key: string) => {
+ switch (key) {
+ case BASE_TAB_KEY:
+ return ;
+ case "add":
+ return ;
+ case "llm-credentials":
+ return ;
+ case "pass-through":
+ return ;
+ case "health":
+ return ;
+ case "retry-settings":
+ return ;
+ case "model-group-alias":
+ return ;
+ case "price-data":
+ return ;
+ default:
+ return null;
+ }
+};
+
+export default function ModelsAndEndpointsPage() {
+ const { accessToken, userRole, userId: userID, premiumUser } = useAuthorized();
+ const { data: teams } = useTeams();
+ const { data: uiSettings } = useUISettings();
+ const queryClient = useQueryClient();
+ const { modelId, teamId, close } = useModelDetailRouting();
+ const { availableModelAccessGroups, allModelsOnProxy } = useModelDashboardData();
+
+ const [activeKey, setActiveKey] = useState(BASE_TAB_KEY);
+ const [lastRefreshed, setLastRefreshed] = useState("");
+
+ const isProxyAdmin = userRole && isProxyAdminRole(userRole);
+ const isInternalUser = userRole && internalUserRoles.includes(userRole);
+ const isUserTeamAdmin = userID && isUserTeamAdminForAnyTeam(teams ?? null, userID);
+ const addModelDisabledForInternalUsers =
+ isInternalUser && uiSettings?.values?.disable_model_add_for_internal_users === true;
+ const shouldHideAddModelTab = !isProxyAdmin && (addModelDisabledForInternalUsers || !isUserTeamAdmin);
+ const isAdmin = all_admin_roles.includes(userRole);
+
+ const visibleSlugs = useMemo>(
+ () => [
+ "",
+ ...(shouldHideAddModelTab ? [] : (["add"] as const)),
+ ...(isAdmin
+ ? (["llm-credentials", "pass-through", "health", "retry-settings", "model-group-alias", "price-data"] as const)
+ : []),
+ ],
+ [shouldHideAddModelTab, isAdmin],
+ );
+
+ const allModelsLabel = isAdmin ? "All Models" : "Your Models";
+ const tabItems = visibleSlugs.map((slug) => {
+ const key = slug || BASE_TAB_KEY;
+ return {
+ key,
+ label: slug ? TAB_LABELS[slug] : allModelsLabel,
+ children: key === activeKey ? renderPanel(key) : null,
+ };
+ });
+
+ const handleRefreshClick = () => {
+ setLastRefreshed(new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }));
+ queryClient.invalidateQueries({ queryKey: ["models", "list"] });
+ };
+
+ const invalidateModels = () => queryClient.invalidateQueries({ queryKey: ["models", "list"] });
+
+ if (teamId) {
+ return (
+
+
+
+ );
+ }
return (
-
+
+
+
+
+
Model Management
+ {isAdmin ? (
+
Add and manage models for the proxy
+ ) : (
+
Add models for teams you are an admin for.
+ )}
+
+
+
+
+
+ {modelId ? (
+
+ ) : (
+
+ {lastRefreshed && Last Refreshed: {lastRefreshed}}
+
+
+ ),
+ }}
+ />
+ )}
+
+
);
}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/add/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/AddModelPanel.tsx
similarity index 98%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/add/page.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/AddModelPanel.tsx
index 7e60ca58fc3..26dcc60d717 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/add/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/AddModelPanel.tsx
@@ -13,7 +13,7 @@ import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { vertexCredentialsUploadProps } from "@/app/(dashboard)/models-and-endpoints/vertexCredentialsUpload";
-export default function AddModelPage() {
+export default function AddModelPanel() {
const { accessToken, userRole } = useAuthorized();
const [form] = Form.useForm();
const queryClient = useQueryClient();
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/AllModelsPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/AllModelsPanel.tsx
new file mode 100644
index 00000000000..9d40ea32185
--- /dev/null
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/AllModelsPanel.tsx
@@ -0,0 +1,23 @@
+"use client";
+
+import { useState } from "react";
+import AllModelsTab from "@/app/(dashboard)/models-and-endpoints/components/AllModelsTab";
+import { useModelDashboardData } from "@/app/(dashboard)/models-and-endpoints/useModelDashboardData";
+import { useModelDetailRouting } from "@/app/(dashboard)/models-and-endpoints/detailNavigation";
+
+export default function AllModelsPanel() {
+ const [selectedModelGroup, setSelectedModelGroup] = useState(null);
+ const { availableModelGroups, availableModelAccessGroups } = useModelDashboardData();
+ const { openModel, openTeam } = useModelDetailRouting();
+
+ return (
+
+ );
+}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/health/page.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/HealthStatusPanel.test.tsx
similarity index 93%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/health/page.test.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/HealthStatusPanel.test.tsx
index 677796957cc..e0f35b5f3b8 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/health/page.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/HealthStatusPanel.test.tsx
@@ -1,7 +1,7 @@
/* @vitest-environment jsdom */
import { render } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
-import HealthStatusPage from "./page";
+import HealthStatusPanel from "./HealthStatusPanel";
vi.mock("next/navigation", () => ({
usePathname: () => "/models-and-endpoints/health",
@@ -27,7 +27,7 @@ vi.mock("@/app/(dashboard)/hooks/models/useModelCostMap", () => ({ useModelCostM
vi.mock("@/app/(dashboard)/hooks/teams/useTeams", () => ({ useTeams: () => ({ data: [] }) }));
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ default: () => ({ accessToken: "123" }) }));
-describe("HealthStatusPage", () => {
+describe("HealthStatusPanel", () => {
beforeEach(() => {
mockHealthCheckComponent.mockClear();
});
@@ -44,7 +44,7 @@ describe("HealthStatusPage", () => {
isLoading: false,
});
- render();
+ render();
expect(mockHealthCheckComponent).toHaveBeenCalled();
const props = mockHealthCheckComponent.mock.calls[0][0];
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/health/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/HealthStatusPanel.tsx
similarity index 98%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/health/page.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/HealthStatusPanel.tsx
index 8942db5f002..451088c87d8 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/health/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/HealthStatusPanel.tsx
@@ -13,7 +13,7 @@ import { useModelDetailRouting } from "@/app/(dashboard)/models-and-endpoints/de
const HEALTH_PAGE_SIZE = 50;
-export default function HealthStatusPage() {
+export default function HealthStatusPanel() {
const { accessToken } = useAuthorized();
const { data: teams } = useTeams();
const { data: modelCostMapData } = useModelCostMap();
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/llm-credentials/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/LlmCredentialsPanel.tsx
similarity index 87%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/llm-credentials/page.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/LlmCredentialsPanel.tsx
index 207ced5be0d..6112e6bffbd 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/llm-credentials/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/LlmCredentialsPanel.tsx
@@ -4,7 +4,7 @@ import { Form } from "antd";
import CredentialsPanel from "@/components/model_add/CredentialsPanel";
import { vertexCredentialsUploadProps } from "@/app/(dashboard)/models-and-endpoints/vertexCredentialsUpload";
-export default function LlmCredentialsPage() {
+export default function LlmCredentialsPanel() {
const [form] = Form.useForm();
return ;
}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/model-group-alias/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.tsx
similarity index 95%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/model-group-alias/page.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.tsx
index c06de353ddf..ec808984c34 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/model-group-alias/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelGroupAliasPanel.tsx
@@ -5,7 +5,7 @@ import ModelGroupAliasSettings from "@/components/model_group_alias_settings";
import { getCallbacksCall } from "@/components/networking";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-export default function ModelGroupAliasPage() {
+export default function ModelGroupAliasPanel() {
const { accessToken, userId: userID, userRole } = useAuthorized();
const [modelGroupAlias, setModelGroupAlias] = useState<{ [key: string]: string }>({});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/retry-settings/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelRetrySettingsPanel.tsx
similarity index 98%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/retry-settings/page.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelRetrySettingsPanel.tsx
index 6442be3e54d..57e32b5624c 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/retry-settings/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/ModelRetrySettingsPanel.tsx
@@ -22,7 +22,7 @@ interface RouterSettings {
num_retries?: number | null;
}
-export default function ModelRetrySettingsPage() {
+export default function ModelRetrySettingsPanel() {
const { accessToken, userId: userID, userRole } = useAuthorized();
const { availableModelGroups } = useModelDashboardData();
const updateRetryPolicy = useUpdateRetryPolicy(accessToken);
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/pass-through/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/PassThroughPanel.tsx
similarity index 89%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/pass-through/page.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/PassThroughPanel.tsx
index 4ba7b8b260b..7ae7e976651 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/pass-through/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/PassThroughPanel.tsx
@@ -3,7 +3,7 @@
import PassThroughSettings from "@/components/PassThroughSettings/PassThroughSettings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
-export default function PassThroughPage() {
+export default function PassThroughPanel() {
const { accessToken, userRole, userId: userID, premiumUser } = useAuthorized();
return (
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/price-data/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/PriceDataPanel.tsx
similarity index 79%
rename from ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/price-data/page.tsx
rename to ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/PriceDataPanel.tsx
index b8f385be13f..506f22957fc 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/price-data/page.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/panels/PriceDataPanel.tsx
@@ -2,6 +2,6 @@
import PriceDataManagementTab from "@/app/(dashboard)/models-and-endpoints/components/PriceDataManagementTab";
-export default function PriceDataPage() {
+export default function PriceDataPanel() {
return ;
}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/tabRoutes.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/tabRoutes.test.ts
deleted file mode 100644
index 920bd3dc156..00000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/tabRoutes.test.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/* @vitest-environment jsdom */
-import { describe, expect, it, vi } from "vitest";
-
-vi.mock("@/components/networking", () => ({ serverRootPath: "" }));
-
-import { MODEL_TAB_SLUGS, modelTabHref, slugFromPathname } from "./tabRoutes";
-
-describe("slugFromPathname", () => {
- it("returns empty string for the base path with or without a trailing slash", () => {
- expect(slugFromPathname("/models-and-endpoints")).toBe("");
- expect(slugFromPathname("/models-and-endpoints/")).toBe("");
- });
-
- it("extracts the tab slug from dev and proxy-mounted (/ui) paths", () => {
- expect(slugFromPathname("/models-and-endpoints/add")).toBe("add");
- expect(slugFromPathname("/ui/models-and-endpoints/llm-credentials/")).toBe("llm-credentials");
- });
-
- it("returns the raw segment for an unknown tab so the view can redirect to base", () => {
- expect(slugFromPathname("/ui/models-and-endpoints/bogus")).toBe("bogus");
- });
-
- it("returns empty string when the models base segment is not in the path", () => {
- expect(slugFromPathname("/teams")).toBe("");
- });
-});
-
-describe("modelTabHref", () => {
- it("builds the trailing-slash base href for the empty slug", () => {
- expect(modelTabHref("")).toBe("/ui/models-and-endpoints/");
- });
-
- it("builds a trailing-slash href for every tab slug (required by static export)", () => {
- for (const slug of MODEL_TAB_SLUGS) {
- expect(modelTabHref(slug)).toBe(`/ui/models-and-endpoints/${slug}/`);
- }
- });
-});
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/tabRoutes.ts b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/tabRoutes.ts
deleted file mode 100644
index e56a664df45..00000000000
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/tabRoutes.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { createTabRoutes } from "@/utils/tabRoutes";
-
-export const modelsRoutes = createTabRoutes("models-and-endpoints", [
- "add",
- "llm-credentials",
- "pass-through",
- "health",
- "retry-settings",
- "model-group-alias",
- "price-data",
-] as const);
-
-export type ModelTabSlug = (typeof modelsRoutes.slugs)[number];
-
-export const MODELS_BASE_SEGMENT = modelsRoutes.baseSegment;
-export const MODEL_TAB_SLUGS = modelsRoutes.slugs;
-export const modelTabHref = modelsRoutes.tabHref;
-export const slugFromPathname = modelsRoutes.slugFromPathname;