From 1ab43be42b231e5a2606c4ff099e35c4dbde4229 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Fri, 24 Jul 2026 23:05:00 -0700 Subject: [PATCH] refactor(ui): migrate cost-optimization to shadcn Replace the antd Tabs, Alert and Collapse on the Cost Optimization page with the shadcn tabs, a token-styled notice, and the shadcn collapsible. Markup only; no behaviour or data-fetching change. Tab panels now use useVisitedTabs with keepMounted so a visited panel keeps its state across tab switches, matching what antd Tabs did. The characterisation tests from the previous commit pass unedited against both the antd and the shadcn markup. Retires the two no-restricted-imports suppressions these files held. --- ui/litellm-dashboard/eslint-suppressions.json | 10 --- .../_components/CostOptimizationView.tsx | 78 ++++++++++--------- .../_components/UsageTab.tsx | 64 ++++++++------- 3 files changed, 73 insertions(+), 79 deletions(-) diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 09d0032e6c9..d486c9e4852 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -215,21 +215,11 @@ "count": 1 } }, - "src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/cost-optimization/_components/PromptCompressionTab.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/app/(dashboard)/cost-optimization/_components/UsageTab.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/cost-tracking/_components/add_margin_form.tsx": { "local/filename-pascal-case": { "count": 1 diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx index 3bab6afee57..11bd0cc109e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx @@ -1,9 +1,10 @@ "use client"; import React from "react"; -import { PiggyBank } from "lucide-react"; -import { Alert, Tabs } from "antd"; +import { Info, PiggyBank } from "lucide-react"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { useVisitedTabs } from "@/hooks/useVisitedTabs"; import UsageTab from "./UsageTab"; import PromptCompressionTab from "./PromptCompressionTab"; import AutorouterTab from "./AutorouterTab"; @@ -18,29 +19,7 @@ interface CostOptimizationViewProps { const CostOptimizationView: React.FC = ({ accessToken, userId, userRole }) => { const activity = useDailyActivityRange(accessToken, userId, userRole); - - const items = [ - { - key: "usage", - label: "Usage", - children: , - }, - { - key: "compression", - label: "Prompt Compression", - children: , - }, - { - key: "autorouter", - label: "Autorouter", - children: , - }, - { - key: "caching", - label: "Prompt Caching", - children: , - }, - ]; + const { onTabChange, hasVisited } = useVisitedTabs("usage"); return (
@@ -54,26 +33,53 @@ const CostOptimizationView: React.FC = ({ accessToken

- +
+ +
+

This is an experimental dashboard

+

Have feedback? Join the discussion{" "} here - - } - /> +

+
+
- + + + + Usage + + + Prompt Compression + + + Autorouter + + + Prompt Caching + + + + + + + + + + + + + + + + ); }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.tsx index 6e2da4456d3..d68ec552156 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.tsx @@ -1,11 +1,12 @@ "use client"; import React, { useEffect, useMemo, useState } from "react"; -import { Collapse } from "antd"; +import { ChevronRight } from "lucide-react"; import { AreaChart, BarChart, DonutChart, DEFAULT_COLOR_CYCLE } from "@/components/shared/charts"; import AdvancedDatePicker from "@/components/shared/advanced_date_picker"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { getToolSpend, ToolSpendResponse } from "@/components/networking"; import { SpendMetrics } from "@/components/UsagePage/types"; import { formatNumberWithCommas } from "@/utils/dataUtils"; @@ -35,38 +36,35 @@ const cachingOf = (m: SpendMetrics): number => m.prompt_caching_savings_spend ?? const savedTokensOf = (m: SpendMetrics): number => m.compression_saved_tokens ?? 0; const MethodologyNote = () => ( - How savings are calculated, - children: ( -
-

- Savings are computed for each request when it is logged, using the provider's reported usage and the - model's pricing, then summed into a daily rollup. Totals below are read from that rollup over the - selected date range, so the numbers never require a scan of raw request logs. -

-

- Compression savings are the tokens Headroom removed before the call, priced at the model's input - rate: compression_saved_tokens * input_cost_per_token -

-

- Prompt caching savings are the tokens the provider served from cache (Anthropic{" "} - cache_read_input_tokens, or OpenAI-style prompt_tokens_details.cached_tokens), - priced at the discount between the normal input rate and the cache-read rate:{" "} - cache_read_input_tokens * max(input_cost_per_token - cache_read_input_token_cost, 0) -

-

- Total saved is the sum of both drivers. Models without a separate cache-read price in the pricing map - contribute zero caching savings rather than erroring. -

-
- ), - }, - ]} - /> + + + + How savings are calculated + + +
+

+ Savings are computed for each request when it is logged, using the provider's reported usage and the + model's pricing, then summed into a daily rollup. Totals below are read from that rollup over the + selected date range, so the numbers never require a scan of raw request logs. +

+

+ Compression savings are the tokens Headroom removed before the call, priced at the model's input rate:{" "} + compression_saved_tokens * input_cost_per_token +

+

+ Prompt caching savings are the tokens the provider served from cache (Anthropic{" "} + cache_read_input_tokens, or OpenAI-style prompt_tokens_details.cached_tokens), + priced at the discount between the normal input rate and the cache-read rate:{" "} + cache_read_input_tokens * max(input_cost_per_token - cache_read_input_token_cost, 0) +

+

+ Total saved is the sum of both drivers. Models without a separate cache-read price in the pricing map + contribute zero caching savings rather than erroring. +

+
+
+
); const SummaryCard = ({ label, value, hint }: { label: string; value: string; hint?: string }) => (