mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(autorouter): show only eligible spend in savings comparisons
This commit is contained in:
parent
f996803d0b
commit
f96bfdd66d
4 changed files with 40 additions and 53 deletions
|
|
@ -179,8 +179,7 @@ describe("AutoRouterBenchmarksTab", () => {
|
|||
expect(screen.getByText("Total estimated savings")).toBeInTheDocument();
|
||||
expect(screen.getByText("$2,174.59")).toBeInTheDocument();
|
||||
expect(screen.queryByText("-86%")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("$359.86")).toBeInTheDocument();
|
||||
expect(screen.getByText("Unavailable")).toBeInTheDocument();
|
||||
expect(screen.getAllByRole("definition").map((node) => node.textContent)).toEqual(["Unavailable", "Unavailable"]);
|
||||
expect(screen.getByText("$23.13")).toBeInTheDocument();
|
||||
expect(screen.getByText("Estimated baseline spend")).toBeInTheDocument();
|
||||
});
|
||||
|
|
@ -237,7 +236,7 @@ describe("AutoRouterBenchmarksTab", () => {
|
|||
expect(screen.getAllByText("$10,126.28").length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it.each([null, undefined])("keeps totals when the classification breakdown is %s", (classifier_cost) => {
|
||||
it.each([null, undefined])("keeps recorded savings when the comparison is unavailable: %s", (classifier_cost) => {
|
||||
const partialCosts: Partial<Totals> = {
|
||||
classifier_cost,
|
||||
cost_coverage: "partial",
|
||||
|
|
@ -250,8 +249,7 @@ describe("AutoRouterBenchmarksTab", () => {
|
|||
|
||||
expect(screen.getAllByText("Unavailable")).toHaveLength(2);
|
||||
expect(screen.queryByText(/\/ 1K turns/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText("$359.86")).toBeInTheDocument();
|
||||
expect(screen.getByText("$353.71")).toBeInTheDocument();
|
||||
expect(screen.queryByText("LLM spend")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("$2,174.59")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
|||
import { Separator } from "@/components/ui/separator";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { SimpleTooltip, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { ApiError } from "@/lib/http/client";
|
||||
import { formatNumberWithCommas } from "@/utils/dataUtils";
|
||||
|
||||
|
|
@ -53,15 +53,17 @@ const Metric: React.FC<{ label: string; value: string; hint?: string }> = ({ lab
|
|||
</Card>
|
||||
);
|
||||
|
||||
const SpendRow: React.FC<{ label: string; value: string; hint?: string; subdued?: boolean }> = ({
|
||||
const SpendRow: React.FC<{ label: string; value: string; hint?: string; subdued?: boolean; tooltip?: string }> = ({
|
||||
label,
|
||||
value,
|
||||
hint,
|
||||
subdued,
|
||||
tooltip,
|
||||
}) => (
|
||||
<dl className="flex flex-wrap items-baseline justify-between gap-x-6 gap-y-1 py-2">
|
||||
<dt className="flex min-w-0 flex-wrap items-baseline gap-x-2 text-sm text-muted-foreground">
|
||||
{label}
|
||||
{tooltip && <SimpleTooltip content={tooltip} />}
|
||||
{hint && <span className="text-xs">{hint}</span>}
|
||||
</dt>
|
||||
<dd
|
||||
|
|
@ -72,9 +74,13 @@ const SpendRow: React.FC<{ label: string; value: string; hint?: string; subdued?
|
|||
</dl>
|
||||
);
|
||||
|
||||
const CostBreakdown: React.FC<{ view: BenchmarkView; partialComparison: boolean }> = ({ view, partialComparison }) => {
|
||||
const CostBreakdown: React.FC<{ view: BenchmarkView }> = ({ view }) => {
|
||||
const stats = view.stats;
|
||||
const costTurns = viewGroup(view) ? stats.turns : stats.cost_requests;
|
||||
const eligibleSpend = stats.savings_estimated_turns > 0 ? stats.savings_estimated_actual_spend : stats.spend;
|
||||
const actualSpend = stats.baseline_spend == null ? null : eligibleSpend;
|
||||
const fullComparison =
|
||||
actualSpend != null && (stats.savings_estimated_turns === 0 || stats.savings_estimated_turns === costTurns);
|
||||
const llmSpend =
|
||||
stats.llm_spend === undefined && stats.spend != null && stats.classifier_cost != null
|
||||
? stats.spend - stats.classifier_cost
|
||||
|
|
@ -83,29 +89,25 @@ const CostBreakdown: React.FC<{ view: BenchmarkView; partialComparison: boolean
|
|||
<div className="flex flex-col justify-center border-t p-6 md:border-t-0 md:border-l">
|
||||
<SpendRow
|
||||
label="Actual auto-router spend"
|
||||
value={stats.spend == null ? "Unavailable" : usd(stats.spend)}
|
||||
hint={stats.cost_coverage === "partial" ? "Partial" : undefined}
|
||||
value={actualSpend == null ? "Unavailable" : usd(actualSpend)}
|
||||
tooltip="Actual spend and baseline spend cover only requests with a recorded baseline estimate. Requests without an estimate are excluded from the savings comparison. Actual spend includes classification costs."
|
||||
/>
|
||||
<div className="mb-3 border-l-2 pl-4">
|
||||
<SpendRow subdued label="LLM spend" value={llmSpend == null ? "Unavailable" : usd(llmSpend)} />
|
||||
<SpendRow
|
||||
subdued
|
||||
label="Classification cost"
|
||||
value={stats.classifier_cost == null ? "Unavailable" : usd(stats.classifier_cost)}
|
||||
hint={
|
||||
stats.classifier_cost == null || costTurns == null
|
||||
? undefined
|
||||
: classificationRatePer1kTurns(stats.classifier_cost, costTurns)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{stats.cost_coverage === "partial" && (
|
||||
<p className="mb-3 text-xs text-muted-foreground">Some request costs are unavailable</p>
|
||||
{fullComparison && (
|
||||
<div className="mb-3 border-l-2 pl-4">
|
||||
<SpendRow subdued label="LLM spend" value={llmSpend == null ? "Unavailable" : usd(llmSpend)} />
|
||||
<SpendRow
|
||||
subdued
|
||||
label="Classification cost"
|
||||
value={stats.classifier_cost == null ? "Unavailable" : usd(stats.classifier_cost)}
|
||||
hint={
|
||||
stats.classifier_cost == null || costTurns == null
|
||||
? undefined
|
||||
: classificationRatePer1kTurns(stats.classifier_cost, costTurns)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Separator />
|
||||
{partialComparison && stats.baseline_spend != null && (
|
||||
<SpendRow label="Actual spend on estimated requests" value={usd(stats.savings_estimated_actual_spend)} />
|
||||
)}
|
||||
<SpendRow
|
||||
label="Estimated baseline spend"
|
||||
value={stats.baseline_spend == null ? "Unavailable" : usd(stats.baseline_spend)}
|
||||
|
|
@ -144,13 +146,13 @@ const HeroCard: React.FC<{ view: BenchmarkView }> = ({ view }) => {
|
|||
</div>
|
||||
{partialComparison && (
|
||||
<p className="text-center text-xs text-muted-foreground">
|
||||
{stats.savings_estimated_turns.toLocaleString()} of {comparisonRequests?.toLocaleString()} requests have
|
||||
savings estimates
|
||||
Savings based on {stats.savings_estimated_turns.toLocaleString()} of{" "}
|
||||
{comparisonRequests?.toLocaleString()} requests
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<CostBreakdown view={view} partialComparison={partialComparison} />
|
||||
<CostBreakdown view={view} />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -413,12 +413,7 @@ describe("UserInfoView auto-router usage", () => {
|
|||
).toBeInTheDocument();
|
||||
expect(screen.getByText("$3.00")).toBeInTheDocument();
|
||||
expect(screen.getByText("Estimated baseline spend")).toBeInTheDocument();
|
||||
expect(screen.getAllByRole("definition").map((node) => node.textContent)).toEqual([
|
||||
"Unavailable",
|
||||
"Unavailable",
|
||||
"Unavailable",
|
||||
"Unavailable",
|
||||
]);
|
||||
expect(screen.getAllByRole("definition").map((node) => node.textContent)).toEqual(["Unavailable", "Unavailable"]);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ describe("KeyAutoRouterUsageTab", () => {
|
|||
vi.stubGlobal("fetch", fetchMock);
|
||||
});
|
||||
|
||||
it("renders this key's daily savings and selected-router session costs", async () => {
|
||||
it("compares only eligible request costs and explains the excluded usage", async () => {
|
||||
const user = userEvent.setup();
|
||||
const activity = {
|
||||
dateValue: { from: new Date(2025, 0, 1), to: new Date(2025, 0, 31) },
|
||||
|
|
@ -101,12 +101,7 @@ describe("KeyAutoRouterUsageTab", () => {
|
|||
renderWithProviders(<KeyAutoRouterUsageTab accessToken="test-token" keyToken="key-hash-1" activity={activity} />);
|
||||
|
||||
expect(await screen.findByText("$12.75")).toBeInTheDocument();
|
||||
expect(screen.getAllByRole("definition").map((node) => node.textContent)).toEqual([
|
||||
"Unavailable",
|
||||
"Unavailable",
|
||||
"Unavailable",
|
||||
"Unavailable",
|
||||
]);
|
||||
expect(screen.getAllByRole("definition").map((node) => node.textContent)).toEqual(["Unavailable", "Unavailable"]);
|
||||
expect(screen.getByText("$0.5000")).toBeInTheDocument();
|
||||
expect(screen.queryByText("-50%")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("All auto-routers")).toBeInTheDocument();
|
||||
|
|
@ -123,15 +118,12 @@ describe("KeyAutoRouterUsageTab", () => {
|
|||
expect(screen.getByText("Whole sessions overlapping the selected dates")).toBeInTheDocument();
|
||||
expect(screen.queryByText("$12.75")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("-50%")).toBeInTheDocument();
|
||||
expect(screen.getByText("1 of 2 requests have savings estimates")).toBeInTheDocument();
|
||||
expect(screen.getByText("Actual spend on estimated requests")).toBeInTheDocument();
|
||||
expect(screen.getAllByRole("definition").map((node) => node.textContent)).toEqual([
|
||||
"$100.00",
|
||||
"$99.75",
|
||||
"$0.2500",
|
||||
"$1.00",
|
||||
"$2.00",
|
||||
]);
|
||||
expect(screen.getByText("Savings based on 1 of 2 requests")).toBeInTheDocument();
|
||||
expect(screen.getAllByRole("definition").map((node) => node.textContent)).toEqual(["$1.00", "$2.00"]);
|
||||
await user.hover(screen.getByLabelText("question-circle"));
|
||||
expect(
|
||||
await screen.findByText(/Requests without an estimate are excluded from the savings comparison/),
|
||||
).toBeVisible();
|
||||
expect(screen.getByText("Auto-router prompt caching")).toBeInTheDocument();
|
||||
expect(screen.getAllByText("50.0%").length).toBeGreaterThan(0);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue