Skip to content

feat(ptu): admin UI for PTU reservations + Usage page column + CSV export - #33302

Closed
yucheng-berri wants to merge 4 commits into
litellm_lit1697_stage3_read_pathfrom
litellm_lit1697_stage4_ui
Closed

feat(ptu): admin UI for PTU reservations + Usage page column + CSV export#33302
yucheng-berri wants to merge 4 commits into
litellm_lit1697_stage3_read_pathfrom
litellm_lit1697_stage4_ui

Conversation

@yucheng-berri

@yucheng-berri yucheng-berri commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Linear ticket

Resolves LIT-1697 (stage 4 of 5)

Stacks on #33266. Merge that first.

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

To reproduce end-to-end you need stages 1-3 applied, a reservation created, and a real Azure gpt-4 request on the same team+day.

# 1. Flip the flag
$ curl -s -X POST http://localhost:4000/config/general_settings \
    -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" \
    -d '{"enable_ptu_cost_attribution": true}' > /dev/null

# 2. Create a reservation from the UI
Open http://localhost:4000/ui/?page=ptu-reservations
Click "+ Create Reservation"
Fill team_id, model=gpt-4, ptu_count=1, cost_per_ptu=200, effective_from=today

# 3. Make a real request as the team's key
$ curl -s -X POST http://localhost:4000/v1/chat/completions \
    -H "Authorization: Bearer $TEAM_KEY" -H "Content-Type: application/json" \
    -d '{"model":"gpt-4","messages":[{"role":"user","content":"hi"}]}' > /dev/null

# 4. Trigger the rollup (or wait until 00:15 UTC)
$ .venv/bin/python scripts/ptu_reservation_backfill.py --date $(date -u +%Y-%m-%d)
[YYYY-MM-DD] reservations=1 rows_written=1
total rows written: 1

# 5. Open the Usage page and view the team pane
Open http://localhost:4000/ui/?page=new_usage
Choose "Team" from the entity dropdown
Observe: Total Spend + Flat Cost + Total Cost stat cards side-by-side

# 6. Export CSV
Click Export → CSV → daily
The downloaded team_usage_daily_YYYY-MM-DD.csv contains "Flat Cost ($)" and
"Total Cost ($)" columns alongside "Spend ($)".

Post screenshots in this PR once staging deploy lands. Locally verified via unit tests + component tests.

Type

New Feature

Changes

Adds the admin UI, the Usage page cost breakdown, and CSV export enrichment for the PTU-reservation feature stack. Everything is additive and gated behind enable_ptu_cost_attribution in general_settings; consumers who don't opt in see zero behavior change.

PTU Reservations admin page (/ptu-reservations):

  • Panel + create modal + close modal. Table columns: team, model, PTU count, cost/PTU, monthly total, effective_from, effective_to, status, actions.
  • Status badge: active (live now), scheduled (starts later), ended (already closed). Only active/scheduled rows show a Close button; ended rows have no action.
  • Feature-flag gating: when off, the page renders a friendly explainer directing operators to general_settings. Fully rendered inside the panel so the nav item behavior stays consistent even without hook plumbing in the sidebar.
  • Role gating: non-admin visitors see a "you need proxy-admin access" message; no data fetches for non-admin.
  • Built with antd + local styled table (tremor is being phased out of new UI code).

Usage page team pane extension:

  • New Flat Cost and Total Cost stat cards next to Total Spend. Non-team entity types (user, org, tag, agent, customer) render exactly as before — the grid stays 5 items wide instead of 7.
  • Feature-flag gated so tenants who haven't enabled PTU tracking see the classic 5-card layout for teams too.

CSV / JSON export enrichment:

  • CSV daily export adds Flat Cost ($) and Total Cost ($) columns when the response includes total_flat_cost. Detection is data-shape driven: spendData.metadata.total_flat_cost presence, not entityType.
  • JSON export summary gains total_flat_cost and total_cost for team exports; other entities' summaries are unchanged.
  • The daily_with_keys and daily_with_models scopes are unchanged — flat cost is a per-team-per-model concept, and folding it into per-key or per-model exports would require sentinel handling in the aggregator that stage 3 already keeps out of api_key breakdowns.

Networking layer:

  • ptuReservationListCall, ptuReservationInfoCall, ptuReservationCreateCall, ptuReservationCloseCall.
  • React Query hooks: usePtuReservations, useCreatePtuReservation, useClosePtuReservation, all invalidating on mutation success.
  • useIsPtuCostAttributionEnabled reads /config/list?config_type=general_settings and returns a stable { enabled, isLoading } shape.

Deviation from the admin-entity pattern

Stage 1's rationale for skipping /update still applies; the UI mirrors that by offering only Create + Close. No Edit button on the reservations table.

The panel itself uses antd + a lightweight styled <table> rather than tremor, breaking pattern with the Budgets panel. The codebase is actively migrating off tremor (the eslint rule no-restricted-imports bans it in new files), so this is the right direction going forward rather than a deviation.

Behavior changes

  • New page at /ui/?page=ptu-reservations (via migratedPages, actual URL /ui/ptu-reservations). Feature-flag gated. Nav item visible to any admin role.
  • Team Usage view stat cards reflow from 5 to 7 when the flag is on. Non-team views are untouched.
  • Team CSV export gains 2 columns and JSON summary gains 2 fields when total_flat_cost is present in the response. Consumers of the old CSV shape who parse by header lose nothing (columns are appended, Spend ($) still means per-request); consumers who parse by index will break — flagging for anyone with fragile scripts.
  • No changes to non-team CSV/JSON export shape.
  • No new endpoints called from the UI beyond those shipped in stages 1-3.
  • No changes to budgets, LiteLLM_TeamTable.spend, per-request spend hot path, or backend read/write logic.

Boilerplate note

The panel + modals mirror the Budgets pattern (budgets/_components/) but with antd instead of tremor and a single Close verb instead of Edit/Delete. The overall CRUD scaffolding is still copy-paste; extracting a generic admin-entity panel component is filed as a follow-up rather than blocking this PR (per the pattern discussed in stage 1).

Files changed

  • ui/litellm-dashboard/src/app/(dashboard)/ptu-reservations/page.tsx
  • ui/litellm-dashboard/src/app/(dashboard)/ptu-reservations/_components/ptu_reservation_panel.tsx
  • ui/litellm-dashboard/src/app/(dashboard)/ptu-reservations/_components/ptu_reservation_modal.tsx
  • ui/litellm-dashboard/src/app/(dashboard)/ptu-reservations/_components/close_reservation_modal.tsx
  • ui/litellm-dashboard/src/app/(dashboard)/ptu-reservations/_components/ptu_reservation_panel.test.tsx
  • ui/litellm-dashboard/src/app/(dashboard)/hooks/ptuReservations/useIsPtuCostAttributionEnabled.ts
  • ui/litellm-dashboard/src/app/(dashboard)/hooks/ptuReservations/usePtuReservations.ts
  • ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx: showFlatCost stat cards for team view
  • ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx: mock the new hook so existing tests pass
  • ui/litellm-dashboard/src/components/EntityUsageExport/utils.ts: hasFlatCost detector + Flat Cost / Total Cost columns + metadata summary
  • ui/litellm-dashboard/src/components/EntityUsageExport/utils.test.ts: 4 new tests for the new column + metadata behavior
  • ui/litellm-dashboard/src/components/EntityUsageExport/types.ts: optional total_flat_cost on EntitySpendData.metadata + optional fields on ExportMetadata.summary
  • ui/litellm-dashboard/src/components/UsagePage/types.ts: optional flat_cost on SpendMetrics
  • ui/litellm-dashboard/src/components/networking.tsx: 4 new ptuReservation* calls
  • ui/litellm-dashboard/src/components/leftnav.tsx: PTU Reservations nav item + Coins icon
  • ui/litellm-dashboard/src/utils/migratedPages.ts: ptu-reservations route mapping

Tests

166 UI tests pass across three directly-affected test files:

  • ptu_reservation_panel.test.tsx (6): flag-off render, loading state, empty state, populated table, create-modal open, non-admin denial.
  • utils.test.ts (72): existing suite + 4 new tests — team metadata summary includes total_flat_cost / total_cost, non-team summary omits both, CSV daily rows for team include Flat Cost / Total Cost, non-team daily rows omit both.
  • EntityUsage.test.tsx (25): existing 25 tests still pass after mocking the new hook.
$ docker run --rm -v .:/repo -w /repo/ui/litellm-dashboard node:20-alpine \
    npx vitest run src/app/\(dashboard\)/ptu-reservations/ \
      src/app/\(dashboard\)/usage/_components/components/EntityUsage/ \
      src/components/EntityUsageExport/
Test Files  10 passed (10)
Tests       166 passed (166)

Prettier + ESLint clean on all changed files. Python lint clean (no backend changes; verified).


Open in Devin Review

@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR delivers stage 4 of the PTU-reservation feature: an admin UI page for managing reservations, two new stat cards on the team Usage view, and CSV/JSON export enrichment — all fully gated behind enable_ptu_cost_attribution in general_settings. Both findings from the prior review have been addressed: generateMetadata now uses the same hasFlatCost(spendData) guard as generateDailyData so CSV and JSON exports stay consistent, and the dead ptuReservationInfoCall export has been removed.

  • New /ptu-reservations page with create + close modals, status badge logic (active / scheduled / ended), feature-flag gating, and role gating (proxy-admin only).
  • Usage page team pane gains Flat Cost and Total Cost stat cards (grid expands 5→7) when the flag is on; all other entity types are untouched.
  • CSV/JSON export appends Flat Cost ($) and Total Cost ($) columns / summary fields only when total_flat_cost is present in the response metadata — data-shape-driven, not entity-type-driven.

Confidence Score: 5/5

Safe to merge — all changes are additive, fully gated behind a feature flag, and the two issues flagged in the prior review are correctly resolved.

Both previously flagged findings are fixed: the CSV/JSON export inconsistency (hasFlatCost guard now applied uniformly in both generateDailyData and generateMetadata) and the unused networking export (removed). The remaining changes introduce a new admin page, stat cards, and networking calls that follow existing codebase patterns and are backed by 166 passing tests. No correctness or data-integrity issues were found on a fresh read of the diff.

No files require special attention.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/components/EntityUsageExport/utils.ts Introduces hasFlatCost guard; now applied consistently to both generateDailyData and generateMetadata — the prior export inconsistency is resolved.
ui/litellm-dashboard/src/components/networking.tsx Adds ptuReservationListCall, ptuReservationCreateCall, ptuReservationCloseCall. The previously flagged dead ptuReservationInfoCall has been removed.
ui/litellm-dashboard/src/app/(dashboard)/ptu-reservations/_components/ptu_reservation_panel.tsx New admin panel with feature-flag gating, role gating, and status badge logic.
ui/litellm-dashboard/src/app/(dashboard)/hooks/ptuReservations/usePtuReservations.ts Clean React Query hooks for list, create, and close mutations. Mutations correctly invalidate ptuReservationKeys.all on success.
ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx Adds Flat Cost and Total Cost stat cards for team entities behind the PTU flag. Grid expands 5→7 only when flag is on.

Reviews (2): Last reviewed commit: "fix(ptu-ui): CI + greptile p1/p2 - align..." | Re-trigger Greptile

Comment on lines +355 to +358
if (entityType === "team") {
const flatCost = spendData.metadata.total_flat_cost ?? 0;
summary.total_flat_cost = flatCost;
summary.total_cost = spendData.metadata.total_spend + flatCost;

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 Inconsistent flat-cost detection between CSV and JSON export

generateDailyData adds flat cost columns to the CSV only when hasFlatCost(spendData) returns true (data-shape driven), but generateMetadata always adds total_flat_cost and total_cost to the JSON summary for any team entity, even when the backend never returned total_flat_cost (it defaults to ?? 0). A team export with PTU disabled will therefore produce a JSON summary containing total_flat_cost: 0 and total_cost === total_spend while the CSV has no flat cost columns at all — two halves of the same export that disagree. The PR description explicitly states "Detection is data-shape driven: spendData.metadata.total_flat_cost presence, not entityType", but generateMetadata violates that contract. Aligning both to hasFlatCost would make the exports consistent.

@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage3_read_path branch from 4454840 to 66aca9a Compare July 15, 2026 17:05
yucheng-berri added a commit that referenced this pull request Jul 15, 2026
…eck; dead code

- generateMetadata now gates total_flat_cost/total_cost on hasFlatCost(spendData)
  same as generateDailyData, so CSV and JSON stay consistent for the same
  request. Addresses greptile P1 on #33302.
- Removes ptuReservationInfoCall from networking.tsx; unused in this stack
  and greptile flagged it as dead code (P2).
- Fixes 'Type PtuReservationListFilters has no properties in common with
  ListParams' next.js build error by adapting the filters shape to the
  createQueryKeys ListParams contract.
- Types summary as ExportMetadata['summary'] instead of Record<string, any>
  so Next.js typecheck accepts it.
- Prettier reformatted ptu_reservation_panel.tsx.
@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage4_ui branch from 893c60e to 2519d21 Compare July 15, 2026 17:05
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai fixed both findings:

  • P1 (metadata gating mismatch): generateMetadata now uses the same hasFlatCost(spendData) check as generateDailyData, so CSV and JSON stay consistent for the same request. If total_flat_cost is absent, neither surface will include the flat-cost fields.
  • P2 (ptuReservationInfoCall unused): removed from networking.tsx.

Also fixed a Next.js typecheck failure surfaced by CI: PtuReservationListFilters didn't match the ListParams shape from createQueryKeys; wrapped filters in the expected { filters: ... } structure. And a Record<string, any> vs ExportMetadata['summary'] mismatch on the summary variable now types correctly.

Rebased the stack onto latest litellm_internal_staging to pick up the httplib2 0.32.0 / setuptools 83.0.0 bumps that unblock osv-scan across the stack.

@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage3_read_path branch from 66aca9a to 69dd1e1 Compare July 15, 2026 17:26
yucheng-berri added a commit that referenced this pull request Jul 15, 2026
…eck; dead code

- generateMetadata now gates total_flat_cost/total_cost on hasFlatCost(spendData)
  same as generateDailyData, so CSV and JSON stay consistent for the same
  request. Addresses greptile P1 on #33302.
- Removes ptuReservationInfoCall from networking.tsx; unused in this stack
  and greptile flagged it as dead code (P2).
- Fixes 'Type PtuReservationListFilters has no properties in common with
  ListParams' next.js build error by adapting the filters shape to the
  createQueryKeys ListParams contract.
- Types summary as ExportMetadata['summary'] instead of Record<string, any>
  so Next.js typecheck accepts it.
- Prettier reformatted ptu_reservation_panel.tsx.
@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage4_ui branch from 2519d21 to fbad97e Compare July 15, 2026 17:26
@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage3_read_path branch from 69dd1e1 to e724ae5 Compare July 15, 2026 17:32
yucheng-berri added a commit that referenced this pull request Jul 15, 2026
…eck; dead code

- generateMetadata now gates total_flat_cost/total_cost on hasFlatCost(spendData)
  same as generateDailyData, so CSV and JSON stay consistent for the same
  request. Addresses greptile P1 on #33302.
- Removes ptuReservationInfoCall from networking.tsx; unused in this stack
  and greptile flagged it as dead code (P2).
- Fixes 'Type PtuReservationListFilters has no properties in common with
  ListParams' next.js build error by adapting the filters shape to the
  createQueryKeys ListParams contract.
- Types summary as ExportMetadata['summary'] instead of Record<string, any>
  so Next.js typecheck accepts it.
- Prettier reformatted ptu_reservation_panel.tsx.
@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage4_ui branch from fbad97e to f80a058 Compare July 15, 2026 17:32
yucheng-berri added a commit that referenced this pull request Jul 15, 2026
…eck; dead code

- generateMetadata now gates total_flat_cost/total_cost on hasFlatCost(spendData)
  same as generateDailyData, so CSV and JSON stay consistent for the same
  request. Addresses greptile P1 on #33302.
- Removes ptuReservationInfoCall from networking.tsx; unused in this stack
  and greptile flagged it as dead code (P2).
- Fixes 'Type PtuReservationListFilters has no properties in common with
  ListParams' next.js build error by adapting the filters shape to the
  createQueryKeys ListParams contract.
- Types summary as ExportMetadata['summary'] instead of Record<string, any>
  so Next.js typecheck accepts it.
- Prettier reformatted ptu_reservation_panel.tsx.
@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage4_ui branch from f80a058 to 4100e34 Compare July 15, 2026 18:29
…port

Adds three UI surfaces gated by enable_ptu_cost_attribution:

1. PTU Reservations admin page under /ptu-reservations. Feature-flagged
   panel with a list table + create modal + close modal. Only proxy admin
   can create/close; non-admin sees a friendly denial. Uses antd
   components (tremor phase-out) and TableIconActionButton for row actions.
2. Usage page > Team view: adds Flat Cost and Total Cost stat cards next
   to Total Spend when the flag is on. Non-team entities are unaffected;
   grid reflows from 5 to 7 items.
3. EntityUsageExport: CSV daily export gains Flat Cost ($) and
   Total Cost ($) columns when the response carries total_flat_cost.
   JSON export summary gains total_flat_cost and total_cost for teams.

Networking:
- ptuReservationListCall / InfoCall / CreateCall / CloseCall in
  networking.tsx.
- usePtuReservations + useCreatePtuReservation + useClosePtuReservation
  React Query hooks mirroring useBudgets.
- useIsPtuCostAttributionEnabled reads /config/list general_settings.

Nav:
- 'PTU Reservations' item next to Budgets under the admin group.
- migratedPages entry so deep-links land on the path-based route.

UI-side detection of flat cost is data-shape driven: exports check for
spendData.metadata.total_flat_cost != null rather than plumbing
entityType through every helper. Backend only populates that field for
team responses today, so the effect is identical without the extra
parameter across the daily-export chain.

Tests (166 passed):
- ptu_reservation_panel.test.tsx: 6 tests — flag-off state, loading,
  empty state, table render, create-modal open, non-admin denial.
- utils.test.ts: 2 new tests covering the flat-cost column shape and
  metadata inclusion for team exports.
- EntityUsage.test.tsx: mocked the new hook so the existing 25 tests
  keep passing without needing a QueryClientProvider wrapper.

No backend or hot-path changes. No CSV export changes for non-team
entities. No changes to budgets or LiteLLM_TeamTable.spend.
…eck; dead code

- generateMetadata now gates total_flat_cost/total_cost on hasFlatCost(spendData)
  same as generateDailyData, so CSV and JSON stay consistent for the same
  request. Addresses greptile P1 on #33302.
- Removes ptuReservationInfoCall from networking.tsx; unused in this stack
  and greptile flagged it as dead code (P2).
- Fixes 'Type PtuReservationListFilters has no properties in common with
  ListParams' next.js build error by adapting the filters shape to the
  createQueryKeys ListParams contract.
- Types summary as ExportMetadata['summary'] instead of Record<string, any>
  so Next.js typecheck accepts it.
- Prettier reformatted ptu_reservation_panel.tsx.
@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage3_read_path branch from e724ae5 to 48d46cc Compare July 15, 2026 20:24
@yucheng-berri
yucheng-berri force-pushed the litellm_lit1697_stage4_ui branch from 4100e34 to 06d8b45 Compare July 15, 2026 20:24
The useIsPtuCostAttributionEnabled hook was reading /config/list?config_type=general_settings, which is built from the fixed ConfigGeneralSettings schema and does not contain enable_ptu_cost_attribution. The flag lives in the ui_settings surface (/get/ui_settings) and is synced into the in-process general_settings dict at runtime. Point the hook at useUISettings so the panel and Usage Flat Cost column reflect the flag state.

Thread enablePtuCostAttribution through SidebarProvider so the PTU Reservations nav item appears and disappears with the flag, matching the enable_chat_ui / enable_projects_ui pattern.
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

Follow-up fix pushed as b69f3601f8. Live QA on the stack surfaced that the PTU Reservations panel showed "disabled" even with the flag on, the Usage Flat Cost column never rendered, and the nav item was not flag-gated. Root cause: useIsPtuCostAttributionEnabled read /config/list?config_type=general_settings, which is built from the fixed ConfigGeneralSettings schema and does not contain enable_ptu_cost_attribution. The flag actually lives under /get/ui_settings. Repointed the hook at useUISettings and threaded enablePtuCostAttribution through SidebarProvider -> leftnav so the nav entry appears and disappears with the flag, matching the enable_chat_ui / enable_projects_ui pattern. Added a targeted regression test on the hook and a nav-gating test.

The Team Usage view's Daily Spend chart previously visualized only per-request
spend, so a team with sizeable PTU flat cost saw the Flat Cost card show a large
aggregate while the per-day bars stayed flat at fractional cents. Backend data
was correct; this was a chart-only readability gap.

Bars now stack Request spend (cyan) on top of Flat cost (violet), with the
legend on so the two series are labeled. The custom tooltip splits into three
lines: Request spend, Flat cost, Total cost. Data is projected to friendly
top-level keys before it hits the chart so the legend reads "Flat cost" and
"Request spend" instead of "metrics.spend" / "metrics.flat_cost".

Scope is Team Usage only, matching where flat_cost is exposed today. Global
Usage / Project Spend view does not surface flat_cost and is intentionally
untouched.

@devin-ai-integration devin-ai-integration Bot left a comment

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.

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +585 to +598
data={[...spendData.results]
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
.map((row) => ({
...row,
"Request spend": row.metrics.spend ?? 0,
"Flat cost": row.metrics.flat_cost ?? 0,
}))}
index="date"
categories={["metrics.spend"]}
colors={["cyan"]}
categories={["Request spend", "Flat cost"]}
colors={["cyan", "violet"]}
stack={true}
valueFormatter={valueFormatterSpend}
yAxisWidth={100}
showLegend={false}
showLegend={true}

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.

🟡 Daily spend chart always shows an empty flat-cost series even when the feature is off

The daily spend chart is rebuilt to always include a second "Flat cost" bar series, a legend, and a relabeled tooltip (categories={["Request spend", "Flat cost"]} at ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx:585-598) without checking the feature flag, so views that should look unchanged now display an extra always-zero cost bar.
Impact: Every non-team usage view, and team views when PTU cost attribution is disabled, show a misleading empty "Flat cost" series and altered tooltip labels instead of the classic single-spend chart.

Feature-flag gating applied to stat cards but not the chart

The stat cards correctly gate on showFlatCost (EntityUsage.tsx:522 and :529), which is entityType === "team" && ptuAttributionEnabled (EntityUsage.tsx:120). The chart block below (EntityUsage.tsx:585-611) is inside the same Cost TabPanel rendered for all entity types, but it unconditionally maps in "Flat cost": row.metrics.flat_cost ?? 0, sets categories to two series, stack={true}, showLegend={true}, and rewrites the tooltip to show "Request spend / Flat cost / Total cost". For non-team entities and for teams with the flag off, flat_cost is absent so the extra series is always $0, contradicting the PR's stated intent that these views render exactly as before. Gating the chart categories/legend/tooltip on showFlatCost restores the prior single-series behavior.

Prompt for agents
The Daily Spend BarChart in EntityUsage.tsx (around lines 584-611) was changed to always render two stacked series ("Request spend" and "Flat cost"), enable the legend, and use a tooltip that shows Request spend / Flat cost / Total cost. Unlike the stat cards above it (which are gated by the showFlatCost boolean = entityType === 'team' && ptuAttributionEnabled), this chart is not gated, so it renders for all entity types and for teams when the PTU cost-attribution flag is off. The result is an always-zero 'Flat cost' bar plus a legend and changed tooltip labels in views that the PR says should be unchanged. Gate the chart's flat-cost behavior on showFlatCost: when showFlatCost is false, keep the original single 'metrics.spend' cyan series with showLegend={false} and the original 'Total Spend' tooltip; when true, use the new stacked two-series chart with legend and the new tooltip. Consider selecting the data map, categories, colors, stack, showLegend, and customTooltip conditionally based on showFlatCost.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@yucheng-berri

Copy link
Copy Markdown
Contributor Author

Closing as superseded. This is part of the v1 PTU design, which stored PTU config in a separate reservation table. The shipped design puts that config on the model deployment instead, merged as #35341, #35343, #35391, #35393 and #36829.

The admin UI landed as #35393, with the PTU inputs moved into the model form rather than a reservations page.

The branch is kept, so nothing here is lost.

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