Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* @vitest-environment jsdom */
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";

const { mockPush } = vi.hoisted(() => ({ mockPush: vi.fn() }));
vi.mock("next/navigation", () => ({ useRouter: () => ({ push: mockPush }) }));
vi.mock("@/components/networking", () => ({ serverRootPath: "" }));

import { createTabRoutes } from "@/utils/tabRoutes";
import { TabRouteBar } from "./TabRouteBar";

const routes = createTabRoutes("caching", ["health", "settings"] as const);
const TABS = [
{ key: "analytics", label: "Cache Analytics" },
{ key: "health", label: "Cache Health" },
{ key: "settings", label: "Cache Settings" },
];

const renderBar = (activeKey = "analytics") =>
render(<TabRouteBar routes={routes} baseTabKey="analytics" activeKey={activeKey} tabs={TABS} />);

describe("TabRouteBar", () => {
beforeEach(() => {
mockPush.mockClear();
});

it("renders each tab as an anchor with its trailing-slash href (base tab maps to the root)", () => {
const { getByRole } = renderBar();
expect(getByRole("tab", { name: "Cache Analytics" })).toHaveAttribute("href", "/ui/caching/");
expect(getByRole("tab", { name: "Cache Health" })).toHaveAttribute("href", "/ui/caching/health/");
expect(getByRole("tab", { name: "Cache Settings" })).toHaveAttribute("href", "/ui/caching/settings/");
});

it("marks the active tab selected from activeKey", () => {
const { getByRole } = renderBar("health");
expect(getByRole("tab", { name: "Cache Health" })).toHaveAttribute("aria-selected", "true");
expect(getByRole("tab", { name: "Cache Analytics" })).toHaveAttribute("aria-selected", "false");
});

it("soft-navigates on a plain left click (preventing the full-page anchor load)", async () => {
const user = userEvent.setup();
const { getByRole } = renderBar();
await user.click(getByRole("tab", { name: "Cache Health" }));
expect(mockPush).toHaveBeenCalledWith("/ui/caching/health/");
});

it("lets the browser handle a modifier-click so open-in-new-tab works", async () => {
const user = userEvent.setup();
const { getByRole } = renderBar();
await user.keyboard("[ControlLeft>]");
await user.click(getByRole("tab", { name: "Cache Health" }));
await user.keyboard("[/ControlLeft]");
expect(mockPush).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use client";

import type { MouseEvent } from "react";
import { useRouter } from "next/navigation";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import type { TabRoutes } from "@/utils/tabRoutes";

export interface TabRouteItem {
key: string;
label: string;
}

interface TabRouteBarProps {
routes: Pick<TabRoutes<string>, "tabHref">;
baseTabKey: string;
activeKey: string;
tabs: readonly TabRouteItem[];
className?: string;
}

export function TabRouteBar({ routes, baseTabKey, activeKey, tabs, className }: TabRouteBarProps) {
const router = useRouter();

const navigate = (href: string) => (event: MouseEvent<HTMLAnchorElement>) => {
const commandModifier = event.metaKey || event.ctrlKey;
const otherModifier = event.shiftKey || event.altKey;
if (commandModifier || otherModifier) {
return;
}
event.preventDefault();
router.push(href);
};

return (
<Tabs value={activeKey} className={className}>
<TabsList variant="line">
{tabs.map(({ key, label }) => {
const href = routes.tabHref(key === baseTabKey ? "" : key);
return (
<TabsTrigger key={key} value={key} render={<a href={href} onClick={navigate(href)} />}>
{label}
</TabsTrigger>
);
})}
</TabsList>
</Tabs>
);
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

import AutorouterTab from "@/app/(dashboard)/cost-optimization/_components/AutorouterTab";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";

export default function AutorouterPage() {
const { accessToken, userId, userRole } = useAuthorized();
return <AutorouterTab accessToken={accessToken} userId={userId} userRole={userRole} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";

import PromptCachingTab from "@/app/(dashboard)/cost-optimization/_components/PromptCachingTab";
import { useDailyActivityRange } from "@/app/(dashboard)/cost-optimization/_components/useDailyActivityRange";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";

export default function PromptCachingPage() {
const { accessToken, userId, userRole } = useAuthorized();
const activity = useDailyActivityRange(accessToken, userId, userRole);
return <PromptCachingTab accessToken={accessToken} activity={activity} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

import PromptCompressionTab from "@/app/(dashboard)/cost-optimization/_components/PromptCompressionTab";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";

export default function PromptCompressionPage() {
const { accessToken } = useAuthorized();
return <PromptCompressionTab accessToken={accessToken} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* @vitest-environment jsdom */
import { act, render } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import CostOptimizationLayout from "./layout";

const { mockPush, navState } = vi.hoisted(() => ({
mockPush: vi.fn(),
navState: { pathname: "/cost-optimization" },
}));
vi.mock("next/navigation", () => ({
usePathname: () => navState.pathname,
useRouter: () => ({ push: mockPush }),
}));

vi.mock("@/components/networking", () => ({ serverRootPath: "" }));

const renderLayout = () =>
render(
<CostOptimizationLayout>
<div data-testid="tab-content">CHILD</div>
</CostOptimizationLayout>,
);

describe("CostOptimizationLayout", () => {
beforeEach(() => {
navState.pathname = "/cost-optimization";
mockPush.mockClear();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(global as any).ResizeObserver = class {
observe() {}
unobserve() {}
disconnect() {}
};
});

it("renders the four tabs and the active tab's page content", () => {
const { getByRole, getByTestId } = renderLayout();
for (const name of ["Usage", "Prompt Compression", "Autorouter", "Prompt Caching"]) {
expect(getByRole("tab", { name })).toBeInTheDocument();
}
expect(getByTestId("tab-content")).toHaveTextContent("CHILD");
});

it("marks the base route's Usage tab active", () => {
const { getByRole } = renderLayout();
expect(getByRole("tab", { name: "Usage" })).toHaveAttribute("aria-selected", "true");
});

it("derives the active tab from a nested pathname", () => {
navState.pathname = "/ui/cost-optimization/compression";
const { getByRole } = renderLayout();
expect(getByRole("tab", { name: "Prompt Compression" })).toHaveAttribute("aria-selected", "true");
expect(getByRole("tab", { name: "Usage" })).toHaveAttribute("aria-selected", "false");
});

it("redirects to the base cost-optimization path when the tab slug is unknown", 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: "" },
});
navState.pathname = "/cost-optimization/bogus";
await act(async () => {
renderLayout();
});
expect(replaceMock).toHaveBeenCalledWith(expect.stringMatching(/\/cost-optimization\/$/));
Object.defineProperty(window, "location", { configurable: true, value: originalLocation });
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use client";

import type { ReactNode } from "react";
import { PiggyBank, Info } from "lucide-react";
import { costOptimizationRoutes } from "@/app/(dashboard)/cost-optimization/tabRoutes";
import { useTabRouting } from "@/app/(dashboard)/hooks/useTabRouting";
import { TabRouteBar } from "@/app/(dashboard)/components/TabRouteBar";

const BASE_TAB_KEY = "usage";

const TABS = [
{ key: BASE_TAB_KEY, label: "Usage" },
{ key: "compression", label: "Prompt Compression" },
{ key: "autorouter", label: "Autorouter" },
{ key: "caching", label: "Prompt Caching" },
] as const;

export default function CostOptimizationLayout({ children }: { children: ReactNode }) {
const { activeKey } = useTabRouting({
routes: costOptimizationRoutes,
baseTabKey: BASE_TAB_KEY,
visibleKeys: costOptimizationRoutes.slugs,
});

return (
<div className="w-full space-y-6 p-6">
<div>
<div className="flex items-center gap-2">
<PiggyBank className="size-6 text-emerald-600" strokeWidth={1.75} />
<h1 className="text-xl font-semibold text-foreground">Cost Optimization</h1>
</div>
<p className="mt-1 text-sm text-muted-foreground">
Track and configure the mechanisms that save you money: prompt compression, prompt caching, and auto routing
</p>
</div>

<div className="flex items-start gap-2 rounded-md border border-blue-200 bg-blue-50 p-3 text-sm text-blue-800 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-200">
<Info className="mt-0.5 size-4 shrink-0" />
<span>
This is an experimental dashboard. Have feedback? Join the discussion{" "}
<a
href="https://github.com/BerriAI/litellm/discussions/32172"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
here
</a>
</span>
</div>

<TabRouteBar routes={costOptimizationRoutes} baseTabKey={BASE_TAB_KEY} activeKey={activeKey} tabs={TABS} />

<div>{children}</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"use client";

import CostOptimizationView from "./_components/CostOptimizationView";
import UsageTab from "./_components/UsageTab";
import { useDailyActivityRange } from "./_components/useDailyActivityRange";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";

export default function CostOptimizationPage() {
const { accessToken, userId, userRole } = useAuthorized();
return <CostOptimizationView accessToken={accessToken} userId={userId} userRole={userRole} />;
const activity = useDailyActivityRange(accessToken, userId, userRole);
return <UsageTab accessToken={accessToken} activity={activity} />;
}
Loading
Loading