Skip to content

refactor(ui): give each Cost Optimization tab its own route - #34424

Open
ryan-crabbe-berri wants to merge 3 commits into
litellm_internal_stagingfrom
litellm_cost_optimization_per_tab_routing
Open

refactor(ui): give each Cost Optimization tab its own route#34424
ryan-crabbe-berri wants to merge 3 commits into
litellm_internal_stagingfrom
litellm_cost_optimization_per_tab_routing

Conversation

@ryan-crabbe-berri

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Cost Optimization tabs shared one URL, so links/refresh always landed on Usage
  • All four tabs lived in one component behind an antd tab bar

How it solves it:

  • Each tab gets its own prerendered path under /cost-optimization
  • A shared layout drives the tab bar from the pathname
  • The wrapper component is split into one small page per tab

Relevant issues

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • 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 (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

UI-only change; steps to verify against a live proxy (captured at commit 8c3125d).

  1. Build the dashboard and serve it through the proxy, then open http://localhost:4000/ui/?page=cost-optimization
  2. Click through Usage, Prompt Compression, Autorouter and Prompt Caching; the address bar becomes /ui/cost-optimization, /ui/cost-optimization/compression, /ui/cost-optimization/autorouter and /ui/cost-optimization/caching as you switch
  3. Hard-refresh the browser on /ui/cost-optimization/compression (and each of the others); the page reloads straight onto that tab instead of snapping back to Usage, and the Cost Optimization sidebar item stays highlighted
  4. Confirm the static export produced a real HTML file per tab: out/cost-optimization/index.html, out/cost-optimization/compression/index.html, out/cost-optimization/autorouter/index.html, out/cost-optimization/caching/index.html

Screenshots of each tab and of a hard-refresh deep link to be attached.

Type

🧹 Refactoring

Changes

Cost Optimization moves from a single tabbed wrapper to path-based routing, the same pattern used for Models + Endpoints (#34327) and Caching (#34415).

tabRoutes.ts is the source of truth: the tab slugs plus costOptimizationTabHref and slugFromPathname, which map a slug to a trailing-slash URL and read the active slug back off the pathname. layout.tsx renders the shared header, the experimental banner and the tab bar, derives the active tab from usePathname() so no tab state is stored, and pushes to a tab's path on click; an unknown slug redirects to the base route. Each tab is its own page.tsx (Usage at the index, then compression, autorouter, caching) that pulls only the props it needs. Build-time-known tab paths prerender to static HTML, so deep links and hard-loads work with no nginx change; there is no runtime-unknown id here, so nothing needs query params.

The old CostOptimizationView wrapper is deleted, the tab bar is rebuilt on the shadcn Tabs primitive, and the antd Alert becomes a plain banner, so the page adds no new antd usage.

A second commit fixes a shared shell helper that every nested tab route depends on: legacyKeyForPathname matched the full relative path against a single route segment, so a path like /cost-optimization/compression resolved to no key and the shell fell back to the default page, leaving the sidebar item unhighlighted. It now matches on the first path segment, which fixes highlighting for every migrated page with nested routes. This is the same one-line shell fix that is also in #34415; the two edits are byte-identical, so they merge cleanly whichever lands first.

Tests: tabRoutes.test.ts locks the slug/href mapping, layout.test.tsx covers rendering the tab bar, the active base tab, routing on click and the unknown-slug redirect, and migratedPages.test.ts gains a regression for nested-route sidebar resolution.

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 Jul 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Refactors Cost Optimization into independently routable tabs.

  • Adds a shared route-aware tab bar and Cost Optimization layout.
  • Adds separate pages for Usage, Prompt Compression, Autorouter, and Prompt Caching.
  • Updates migrated-page resolution so nested routes retain the correct sidebar selection.
  • Adds routing, navigation, and sidebar regression tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failures remain.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/app/(dashboard)/components/TabRouteBar.tsx Adds reusable anchor-backed tabs with client-side navigation and native modifier-click behavior.
ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/layout.tsx Introduces the shared Cost Optimization header, banner, route-derived tab selection, and nested-page layout.
ui/litellm-dashboard/src/app/(dashboard)/cost-optimization/tabRoutes.ts Defines the Cost Optimization route slugs through the shared tab-routing utility.
ui/litellm-dashboard/src/utils/migratedPages.ts Resolves nested migrated routes by their first route segment so the dashboard shell retains the parent sidebar selection.

Reviews (2): Last reviewed commit: "refactor(ui): adopt shared tab-routing h..." | Re-trigger Greptile

@codspeed-hq

codspeed-hq Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_cost_optimization_per_tab_routing (5fad308) with litellm_internal_staging (43e7b96)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (a507394) during the generation of this report, so 43e7b96 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Split the Cost Optimization page's four tabs (Usage, Prompt Compression,
Autorouter, Prompt Caching) into their own prerendered paths under
/cost-optimization, mirroring the Models + Endpoints and Caching per-tab
routing. A shared layout renders the header, the experimental banner and
the tab bar, deriving the active tab from the pathname; each tab is its
own page.tsx that pulls only the props it needs, so deep links and
hard-loads to /cost-optimization/compression, /autorouter and /caching
resolve to real static HTML with no nginx change.

The former CostOptimizationView is removed. The tab bar is rebuilt on the
shadcn Tabs primitive and the antd Alert becomes a plain banner, so the
page adds no new antd usage.
legacyKeyForPathname matched the full relative path against a single
route segment, so a nested tab route like /cost-optimization/compression
resolved to no key and the shell fell back to the default page, leaving
the Cost Optimization nav item unhighlighted. Match on the first path
segment instead, which fixes every migrated page with nested tab routes.
…n Cost Optimization

Replace the page's hand-written tabRoutes.ts and layout routing engine
with createTabRoutes + useTabRouting + the shared <TabRouteBar>, keeping
the header and experimental banner inline. Rebasing onto staging also
picked up the new shared useDailyActivityRange hook: the Usage and Prompt
Caching tabs now take an `activity` prop, so each of those routes calls
the hook itself and passes it through (each tab route mounts alone, so
per-route data is correct). The per-page tabRoutes.test.ts is dropped in
favor of the central factory test.
@ryan-crabbe-berri
ryan-crabbe-berri force-pushed the litellm_cost_optimization_per_tab_routing branch from 8c3125d to 5fad308 Compare July 23, 2026 23:53
@ryan-crabbe-berri

Copy link
Copy Markdown
Contributor Author

Rebased onto staging now that the shared tab-routing helpers (#34435) are merged. This branch now uses createTabRoutes + useTabRouting + a new shared <TabRouteBar> (shadcn tab bar rendered as real anchors, so left-click soft-navigates and middle-click / cmd-click opens in a new tab) instead of the per-page copy-paste. Behavior is unchanged aside from that added middle-click support.

Also folded in staging drift: Cost Optimization gained a shared useDailyActivityRange hook feeding the Usage and Prompt Caching tabs (and spend-by-tool / cache-leakage views). The per-tab pages were updated to the new activity prop shape; each of those two routes calls the hook itself.

@ryan-crabbe-berri

Copy link
Copy Markdown
Contributor Author

@greptileai re review

@devin-ai-integration

Copy link
Copy Markdown
Contributor

QA (Devin) — before/after after the shared tab-routing helper refactor

Re-QA'd the Cost Optimization per-tab routing locally (next dev, routing-only — no live backend, so charts render empty; route paths are at the root, e.g. /cost-optimization, not /ui/cost-optimization).

AFTER results (this PR): each tab has its own URL — clicking a tab and deep-linking / hard-refreshing both land on the exact nested tab (e.g. /cost-optimization/autorouter/), and the sidebar "Cost Optimization" item stays highlighted on nested routes. BEFORE: switching sub-tabs kept the single /cost-optimization/ URL and the nested routes 404'd.

BEFORE — Usage, URL /cost-optimization/ AFTER — /cost-optimization/autorouter/ deep-link selects Autorouter
before after

Two things to flag (shared across all 4 routing PRs):

  1. A garbage nested slug (e.g. /cost-optimization/bogus/) shows the Next.js 404 page rather than redirecting to the base tab — the useTabRouting redirect can't fire because a truly-missing route never mounts the section layout (the redirect only helps routes that exist but are hidden from visibleKeys). Product call whether a bare 404 is acceptable here.
  2. Minor non-fatal console warning from Base UI ("a component that acts as a button expected a native <button>") introduced by rendering <a> inside TabsTrigger. Tabs still work; nativeButton={false} would silence it.

Full report + recording: https://app.devin.ai/sessions/f98c3131bcf746c88756b609bae3e724

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.

2 participants