mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
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.
This commit is contained in:
parent
2cb1a2a0c0
commit
1ab43be42b
3 changed files with 73 additions and 79 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<CostOptimizationViewProps> = ({ accessToken, userId, userRole }) => {
|
||||
const activity = useDailyActivityRange(accessToken, userId, userRole);
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: "usage",
|
||||
label: "Usage",
|
||||
children: <UsageTab accessToken={accessToken} activity={activity} />,
|
||||
},
|
||||
{
|
||||
key: "compression",
|
||||
label: "Prompt Compression",
|
||||
children: <PromptCompressionTab accessToken={accessToken} />,
|
||||
},
|
||||
{
|
||||
key: "autorouter",
|
||||
label: "Autorouter",
|
||||
children: <AutorouterTab accessToken={accessToken} userId={userId} userRole={userRole} />,
|
||||
},
|
||||
{
|
||||
key: "caching",
|
||||
label: "Prompt Caching",
|
||||
children: <PromptCachingTab accessToken={accessToken} activity={activity} />,
|
||||
},
|
||||
];
|
||||
const { onTabChange, hasVisited } = useVisitedTabs("usage");
|
||||
|
||||
return (
|
||||
<div className="w-full space-y-6 p-6">
|
||||
|
|
@ -54,26 +33,53 @@ const CostOptimizationView: React.FC<CostOptimizationViewProps> = ({ accessToken
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
message="This is an experimental dashboard"
|
||||
description={
|
||||
<span>
|
||||
<div role="alert" className="flex gap-2.5 rounded-lg border bg-card px-4 py-3 text-sm">
|
||||
<Info className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">This is an experimental dashboard</p>
|
||||
<p className="mt-0.5 text-muted-foreground">
|
||||
Have feedback? Join the discussion{" "}
|
||||
<a
|
||||
href="https://github.com/BerriAI/litellm/discussions/32172"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 underline"
|
||||
className="underline underline-offset-4 hover:text-foreground"
|
||||
>
|
||||
here
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs defaultActiveKey="usage" items={items} />
|
||||
<Tabs defaultValue="usage" onValueChange={onTabChange}>
|
||||
<TabsList variant="line" className="h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="usage" className="flex-none rounded-none px-4 py-2">
|
||||
Usage
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="compression" className="flex-none rounded-none px-4 py-2">
|
||||
Prompt Compression
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="autorouter" className="flex-none rounded-none px-4 py-2">
|
||||
Autorouter
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="caching" className="flex-none rounded-none px-4 py-2">
|
||||
Prompt Caching
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent keepMounted={hasVisited("usage")} value="usage" className="pt-4">
|
||||
<UsageTab accessToken={accessToken} activity={activity} />
|
||||
</TabsContent>
|
||||
<TabsContent keepMounted={hasVisited("compression")} value="compression" className="pt-4">
|
||||
<PromptCompressionTab accessToken={accessToken} />
|
||||
</TabsContent>
|
||||
<TabsContent keepMounted={hasVisited("autorouter")} value="autorouter" className="pt-4">
|
||||
<AutorouterTab accessToken={accessToken} userId={userId} userRole={userRole} />
|
||||
</TabsContent>
|
||||
<TabsContent keepMounted={hasVisited("caching")} value="caching" className="pt-4">
|
||||
<PromptCachingTab accessToken={accessToken} activity={activity} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 = () => (
|
||||
<Collapse
|
||||
ghost
|
||||
items={[
|
||||
{
|
||||
key: "methodology",
|
||||
label: <span className="text-sm font-medium">How savings are calculated</span>,
|
||||
children: (
|
||||
<div className="space-y-3 text-sm text-muted-foreground">
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
Compression savings are the tokens Headroom removed before the call, priced at the model's input
|
||||
rate: <code>compression_saved_tokens * input_cost_per_token</code>
|
||||
</p>
|
||||
<p>
|
||||
Prompt caching savings are the tokens the provider served from cache (Anthropic{" "}
|
||||
<code>cache_read_input_tokens</code>, or OpenAI-style <code>prompt_tokens_details.cached_tokens</code>),
|
||||
priced at the discount between the normal input rate and the cache-read rate:{" "}
|
||||
<code>cache_read_input_tokens * max(input_cost_per_token - cache_read_input_token_cost, 0)</code>
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Collapsible>
|
||||
<CollapsibleTrigger className="group flex items-center gap-1.5 rounded-md py-1 text-sm font-medium text-foreground hover:text-muted-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none">
|
||||
<ChevronRight className="size-4 shrink-0 transition-transform group-data-[panel-open]:rotate-90" />
|
||||
How savings are calculated
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="space-y-3 pt-2 pl-[22px] text-sm text-muted-foreground">
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
Compression savings are the tokens Headroom removed before the call, priced at the model's input rate:{" "}
|
||||
<code>compression_saved_tokens * input_cost_per_token</code>
|
||||
</p>
|
||||
<p>
|
||||
Prompt caching savings are the tokens the provider served from cache (Anthropic{" "}
|
||||
<code>cache_read_input_tokens</code>, or OpenAI-style <code>prompt_tokens_details.cached_tokens</code>),
|
||||
priced at the discount between the normal input rate and the cache-read rate:{" "}
|
||||
<code>cache_read_input_tokens * max(input_cost_per_token - cache_read_input_token_cost, 0)</code>
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
|
||||
const SummaryCard = ({ label, value, hint }: { label: string; value: string; hint?: string }) => (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue