Skip to content

feat(ui): rebuild the auto-router benchmarks tab around savings and cache behaviour - #35488

Closed
tin-berri wants to merge 1 commit into
litellm_lit4712_autorouter_session_rollupfrom
litellm_lit4712_autorouter_benchmarks_ui
Closed

feat(ui): rebuild the auto-router benchmarks tab around savings and cache behaviour#35488
tin-berri wants to merge 1 commit into
litellm_lit4712_autorouter_session_rollupfrom
litellm_lit4712_autorouter_benchmarks_ui

Conversation

@tin-berri

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • The benchmarks tab was a flat row of four equal stat tiles followed by a wide per-router table, so the two things an operator opens it for (is the router saving money, is the cache working) had no more weight than anything else
  • The cache numbers were spread across nine table columns of percentages with no sense of how many turns sat behind each one, and the warming estimate was a single net figure with its two sides buried in a footnote
  • Savings rendered $0.00 for any router that declares no counterfactual baseline, which reads as "the router saved nothing" rather than "nothing was measured"

How it solves it:

  • Estimated savings leads as the leftmost tile, with its delta badge and the routed-versus-baseline comparison that produced it; the three session-shape tiles follow as context
  • Two cards replace the table: hit rate over a stacked share-of-turns bar with a three-bucket breakdown, and a warming estimate that shows rescued writes and replay cost separately against a break-even marker
  • A router with no baseline reads "Not measured" instead of a confident zero

Relevant issues

Linear ticket

Resolves LIT-4712

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

Rendered against a live proxy holding 30 days of real auto-routed traffic (99 sessions, 3,145 turns across three routers). Savings reads "Not measured" because all three are complexity routers, which declare no baseline; that is the honest state rather than a zero.

The three buckets sum to the headline turn count: 2,560 + 146 + 439 = 3,145.

To reproduce:

  1. python litellm/proxy/proxy_cli.py --config <a config with at least one auto-router> --port 4715
  2. curl -X POST "http://localhost:4715/auto_router/benchmarks/backfill?start_date=<30d ago>&end_date=<today>" -H "Authorization: Bearer $LITELLM_MASTER_KEY" to populate the rollup from existing traffic
  3. cd ui/litellm-dashboard && NEXT_PUBLIC_BASE_URL=http://localhost:4715 npm run dev
  4. Open http://localhost:3000/cost-optimization/ and click the Auto-Router Benchmarks tab

Type

🆕 New Feature

Changes

autoRouterBenchmarks.ts holds the API types and toView, the pure fold that combines however many routers are in view into the one shape the tab renders. Every rate is recomputed there from summed counts rather than averaged across routers, because averaging weights a router with three turns the same as one with three thousand; the tests pin that a 1000-turn router at 90% and a 10-turn router at 0% read as 89.1%, not 45%.

The baseline label is deliberately three-valued. One shared baseline names it, several name none of them ("each router's own baseline"), and no baseline at all returns null, which is what drives the "Not measured" tile.

Marks follow the house chart specs: a 2px surface gap separates the stacked segments rather than a stroke, the fills are three steps of one neutral ramp, and every bucket carries its own swatch and label in the table below so identity never rests on colour alone. Values and labels wear text tokens, never the mark colour. The bucket table doubles as the table view for the bar.

useAutoRouterBenchmarks computes its window once per mount so the query key stays stable and the request is not refired on every parent render.

QA runbook

  1. With at least one auto-router configured and backfilled, confirm the tile order reads estimated savings, avg turns per session, avg session length, avg tokens per session
  2. Confirm the three bucket turn counts sum to the "turns total" figure beside the stacked bar
  3. Switch the selector from "All auto-routers" to a single router; the tiles and both cards should scope to it, and the label should never show __all__
  4. On a deployment whose routers declare no baseline, confirm the savings tile reads "Not measured" rather than $0.00
  5. On a router that costs more than its baseline, confirm the tile shows a negative dollar figure and a +N% badge in the destructive tone
  6. Toggle dark mode and confirm both cards, the stacked bar and the break-even marker stay legible

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

@greptile-apps

greptile-apps Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds a new Auto-Router Benchmarks dashboard tab that aggregates router savings, session statistics, cache hit buckets, and cache-warming estimates.

  • Adds the benchmark API wrapper and React Query hook
  • Adds aggregation and formatting helpers with unit tests
  • Adds savings, cache-hit, and warming cards with router filtering
  • Integrates the tab into the cost-optimization view

Confidence Score: 4/5

The aggregate savings and mixed-TTL warming calculations need correction before merging because they can show operators materially misleading benchmark values

The all-router fold combines spend from routers without baselines into a labeled measured comparison, and it can pair a five-minute break-even percentage with a one-hour TTL when routers use different cache regimes

Files Needing Attention: ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/autoRouterBenchmarks.ts and AutoRouterBenchmarksTab.tsx

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/autoRouterBenchmarks.ts Adds the central aggregation fold, but mixed measured/unmeasured baselines and mixed TTLs produce misleading combined metrics
ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/AutoRouterBenchmarksTab.tsx Adds the benchmark dashboard UI; it exposes the incorrect mixed-TTL aggregate as a single break-even/TTL pair
ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/useAutoRouterBenchmarks.ts Adds a stable thirty-day React Query request window for the benchmark endpoint
ui/litellm-dashboard/src/components/networking.tsx Adds an API-client wrapper for the auto-router benchmark endpoint
ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/_components/CostOptimizationView.tsx Registers the new Auto-Router Benchmarks tab in the cost-optimization view

Reviews (1): Last reviewed commit: "feat(ui): rebuild the auto-router benchm..." | Re-trigger Greptile

* against the same one; otherwise naming one of them would misattribute the rest.
*/
const combineBaselineLabel = (groups: readonly AutoRouterGroupBenchmark[]): string | null => {
const baselines = new Set(groups.map((g) => g.baseline_model).filter((m): m is string => Boolean(m)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Mixed baselines distort savings

When the all-router selection includes both measured and unmeasured routers, combineBaselineLabel drops the null baselines while toView still includes those routers' spend, causing the displayed percentage to understate measured savings and attribute the comparison to a baseline that does not cover all included traffic

Knowledge Base Used: Admin dashboard (ui/litellm-dashboard)

return_hit_rate_pct: rate(returnHits, returnTurns),
stale_miss_share_pct: rate(staleMisses, returnTurns - returnHits),
warming_savable_miss_pct: rate(savableMisses, turns - hits),
warming_break_even_pct: Math.max(...caches.map((c) => c.warming_break_even_pct)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Mixed TTL break-even mismatch

When the selection contains both five-minute and one-hour TTL routers, the independent maxima pair the five-minute 9% break-even with the one-hour TTL, causing the warming card to display a break-even regime that does not exist

Knowledge Base Used: Admin dashboard (ui/litellm-dashboard)

@tin-berri
tin-berri force-pushed the litellm_lit4712_autorouter_benchmarks_ui branch from 19fb0d1 to ebc9d1f Compare August 1, 2026 20:15
@tin-berri
tin-berri force-pushed the litellm_lit4712_autorouter_benchmarks_ui branch from ebc9d1f to f1d4b84 Compare August 1, 2026 20:50
@tin-berri
tin-berri requested a review from a team August 1, 2026 21:31
@tin-berri
tin-berri force-pushed the litellm_lit4712_autorouter_benchmarks_ui branch from f1d4b84 to 5b7f20a Compare August 1, 2026 21:31
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

@tin-berri
tin-berri force-pushed the litellm_lit4712_autorouter_benchmarks_ui branch from 56ee9b4 to 756504b Compare August 4, 2026 04:20
…s earned over

Renders the per-session rollup as a tab on the cost-optimization page. Savings is
the single hero figure on the view, paired in one card with the session count and
turn count that number was earned over, because a dollar figure with no
denominator beside it is not something an operator can size.

That pairing makes two derived numbers worth showing, both computed from the
response the tab already fetches: savings per session, and how many auto-routers
are in scope. Neither needs a backend change, and both move with the router
selector.

Beneath it the three session-shape metrics, then the prompt-caching section: hit
rate over a stacked share-of-turns bar with a three-bucket breakdown, and a
warming estimate showing rescued writes and replay cost separately against the
break-even for the TTL in use.

Every rate is recomputed from summed counts whenever more than one router is in
view. Averaging the routers' own rates would weight a router with three turns the
same as one with three thousand, which lands the blended figure nowhere near
either.

Savings reads "Not measured" rather than $0.00 when no baseline is configured,
since autorouter_savings_baseline_model is what the driver needs and a confident
zero against real spend is worse than saying nothing.

Built on the base shadcn primitives (Card, CardHeader, CardAction, Separator,
Table) so padding, header layout and table density come from the design system
rather than being restated per block. The tab test renders a real component tree
and stubs only the network, so it is named for the integration tier; the fold it
exercises is unit-tested separately against its own module.
@tin-berri
tin-berri force-pushed the litellm_lit4712_autorouter_benchmarks_ui branch from 756504b to b457caa Compare August 4, 2026 04:31
@tin-berri

Copy link
Copy Markdown
Contributor Author

Superseded by a replacement PR. GitHub would not let me retarget the base off the closed #35402 branch ("Cannot change the base branch because the pull request is part of a stack"), so this is reopened on top of the rebased rollup instead. Same code, rebased onto litellm_internal_staging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant