diff --git a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx index 74a936369c9..56eb3b93244 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/UsageTab.test.tsx @@ -137,28 +137,34 @@ describe("UsageTab", () => { }); it("sums compression and caching dollars across days into the summary cards", () => { + // Total caching and the LiteLLM-injected share deliberately differ so these + // assertions pin which one each figure uses: the caching headline and the + // Total-saved tile take the injected share, the secondary keeps the total. const { getByText } = renderWith([ day("2026-07-12", { compression_savings_spend: 0.04, prompt_caching_savings_spend: 0.006, + gateway_injected_caching_savings_spend: 0.004, compression_saved_tokens: 40000, }), day("2026-07-13", { compression_savings_spend: 0.1, prompt_caching_savings_spend: 0.01, + gateway_injected_caching_savings_spend: 0.006, compression_saved_tokens: 100000, }), ]); - expect(getByText("$0.1560")).toBeInTheDocument(); + expect(getByText("$0.1500")).toBeInTheDocument(); expect(getByText("$0.1400")).toBeInTheDocument(); + expect(getByText("$0.0100")).toBeInTheDocument(); expect(getByText("$0.0160")).toBeInTheDocument(); expect(getByText("140,000 tokens compressed")).toBeInTheDocument(); }); const twoDays = () => [ - day("2026-07-12", { compression_savings_spend: 0.04, prompt_caching_savings_spend: 0.006 }), - day("2026-07-13", { compression_savings_spend: 0.1, prompt_caching_savings_spend: 0.01 }), + day("2026-07-12", { compression_savings_spend: 0.04, gateway_injected_caching_savings_spend: 0.006 }), + day("2026-07-13", { compression_savings_spend: 0.1, gateway_injected_caching_savings_spend: 0.01 }), ]; it("opens on a running total anchored at $0 at the start of the range", () => { @@ -179,7 +185,7 @@ describe("UsageTab", () => { // synthetic start anchor gives the line a zero origin to climb from. const oneDay = new Date(2026, 6, 24); const { getByTestId } = renderWith( - [day("2026-07-24", { compression_savings_spend: 0.2, prompt_caching_savings_spend: 0.05 })], + [day("2026-07-24", { compression_savings_spend: 0.2, gateway_injected_caching_savings_spend: 0.05 })], { from: oneDay, to: oneDay }, ); @@ -194,8 +200,8 @@ describe("UsageTab", () => { // still read left to right in time, and the running total must climb toward // the newest day, not fall away from it. const newestFirst = [ - day("2026-07-13", { prompt_caching_savings_spend: 0.1 }), - day("2026-07-12", { prompt_caching_savings_spend: 0.04 }), + day("2026-07-13", { gateway_injected_caching_savings_spend: 0.1 }), + day("2026-07-12", { gateway_injected_caching_savings_spend: 0.04 }), ]; const { getByTestId, getByRole } = renderWith(newestFirst); @@ -260,7 +266,7 @@ describe("UsageTab", () => { const { getByRole, getByTestId } = renderWith([ day("2026-07-12", { compression_savings_spend: 0.1, - prompt_caching_savings_spend: 0.02, + gateway_injected_caching_savings_spend: 0.02, autorouter_savings_spend: -0.05, }), ]); @@ -312,7 +318,7 @@ describe("UsageTab", () => { const { getByText, getByTestId } = renderWith([ day("2026-07-12", { compression_savings_spend: 0.1, - prompt_caching_savings_spend: 0.02, + gateway_injected_caching_savings_spend: 0.02, autorouter_savings_spend: -0.05, }), ]); @@ -329,12 +335,12 @@ describe("UsageTab", () => { const { getByText, getByTestId } = renderWith([ day("2026-07-12", { compression_savings_spend: 0.04, - prompt_caching_savings_spend: 0.006, + gateway_injected_caching_savings_spend: 0.006, autorouter_savings_spend: 0.02, }), day("2026-07-13", { compression_savings_spend: 0.1, - prompt_caching_savings_spend: 0.01, + gateway_injected_caching_savings_spend: 0.01, autorouter_savings_spend: 0.05, }), ]); 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 e19ee7e48b0..17cc1586029 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 @@ -11,9 +11,9 @@ import { getToolSpend, ToolSpendResponse } from "@/components/networking"; import { autorouterOf, buildDailyToolSeries, - cachingOf, compressionOf, formatRangeLabel, + gatewayAttributedCachingOf, localIsoDay, MAX_POINTS_WITH_DOTS, SAVINGS_COLORS, @@ -88,7 +88,7 @@ const UsageTab: React.FC = ({ accessToken, activity }) => { .map((d) => ({ date: shortDate(d.date), Compression: compressionOf(d.metrics), - "Prompt caching": cachingOf(d.metrics), + "Prompt caching": gatewayAttributedCachingOf(d.metrics), "Auto-router": autorouterOf(d.metrics), })), [results], @@ -119,9 +119,11 @@ const UsageTab: React.FC = ({ accessToken, activity }) => { SAVINGS_DRIVERS.map(({ name, color }) => ({ driver: name, color, - usd: { Compression: totals.compression, "Prompt caching": totals.caching, "Auto-router": totals.autorouter }[ - name - ], + usd: { + Compression: totals.compression, + "Prompt caching": totals.gatewayAttributedCaching, + "Auto-router": totals.autorouter, + }[name], })).filter((d) => d.usd > 0), [totals], ); diff --git a/ui/litellm-dashboard/src/components/shared/SavingsTiles.tsx b/ui/litellm-dashboard/src/components/shared/SavingsTiles.tsx index a3fc5333057..2a137c36a88 100644 --- a/ui/litellm-dashboard/src/components/shared/SavingsTiles.tsx +++ b/ui/litellm-dashboard/src/components/shared/SavingsTiles.tsx @@ -22,13 +22,14 @@ export const useSavingsTotals = (results: DailyData[]) => const compression = sumOf(compressionOf); const caching = sumOf(cachingOf); const autorouter = sumOf(autorouterOf); + const gatewayAttributedCaching = sumOf(gatewayAttributedCachingOf); return { compression, caching, autorouter, - gatewayAttributedCaching: sumOf(gatewayAttributedCachingOf), + gatewayAttributedCaching, savedTokens: sumOf(savedTokensOf), - total: compression + caching + autorouter, + total: compression + gatewayAttributedCaching + autorouter, }; }, [results]); @@ -41,6 +42,7 @@ const SavingsTiles = ({ results, isLoading }: { results: DailyData[]; isLoading: label="Total saved" value={usd(totals.total)} hint={isLoading ? "Loading..." : "Compression + prompt caching + auto-router"} + info="The sum of the three tiles beside it. Its caching term is the LiteLLM-injected share, so this total is what the gateway itself delivered; caching that clients or providers brought on their own appears only in the caching tile's Total figure." /> { renderTab(); - expect(screen.getByTestId("summary-card-total-saved")).toHaveTextContent("$6.00"); + expect(screen.getByTestId("summary-card-total-saved")).toHaveTextContent("$5.40"); expect(screen.getByTestId("summary-card-compression-savings")).toHaveTextContent("$2.00"); expect(screen.getByTestId("summary-card-compression-savings")).toHaveTextContent("1,000 tokens compressed"); // the card leads with what LiteLLM's own injection earned and carries the total beneath it,