Skip to content

Wire GraphQL-sourced historical pricing breakdown into conversation-details surfaces - #15160

Draft
Xavientois wants to merge 5 commits into
masterfrom
saga/pricing-transparency-token-breakdown-20260813-1907/m3t3-warp-historical-detail-wiring
Draft

Wire GraphQL-sourced historical pricing breakdown into conversation-details surfaces#15160
Xavientois wants to merge 5 commits into
masterfrom
saga/pricing-transparency-token-breakdown-20260813-1907/m3t3-warp-historical-detail-wiring

Conversation

@Xavientois

Copy link
Copy Markdown
Contributor

Summary

Closes the two documented gaps that task 2.1 (#15148) left open for Milestone 3: the Settings usage-history detail view and the conversation-details panel's reload-after-restart (non-streaming, GraphQL-sourced) path both now render the per-category "PRICING BREAKDOWN" section, sourced from task 3.1's GraphQL aggregate fields (ConversationUsageMetadata.totalTokenCost/totalPlatformCostInCents, warp-server PR #14986 — note: totalInferenceCost was renamed to totalTokenCost mid-task per direct user request).

Changes

  • crates/graphql/src/api/queries/get_conversation_usage.rs: added totalTokenCost/totalPlatformCostInCents to the usage-history GraphQL query + a charged_usage_totals_from_aggregate helper converting them into the existing ChargedUsageTotals shape.
  • crates/graphql/src/api/ai.rs: same aggregate fields added to the conversation-restore query (list_ai_conversations), reusing the shared helper.
  • app/src/workspaces/gql_convert.rs: ConversationUsageInfo::from(&ConversationUsage) (Settings usage-history path) now passes through the real charged_usage instead of None.
  • app/src/ai/agent/conversation.rs: set_server_metadata (the conversation-details panel's historical/restore path) now populates total_charged_usage, mirroring the existing never-regress guard used for total_provider_cost_in_cents so a stale async snapshot can't clobber a fresher live total.
  • crates/warp_graphql_schema/api/schema.graphql: added the new server-side types/fields (TokenCostBreakdown, ChargedUsageEntry, etc. from tasks 3.1/3.2) since they haven't merged to warp-server's develop yet and cynic validates queries against this local schema at build time.

No new UI code — both surfaces reuse the FeatureFlag::PricingTransparency-gated "PRICING BREAKDOWN" rendering already shipped in #15148.

Validation

  • cargo test -p warp_graphql --lib: 9 passed
  • cargo nextest run -p warp -p warp_graphql --lib --no-fail-fast: 6423 passed (1 pre-existing leaky test, unrelated)
  • cargo clippy -p warp_graphql --lib --tests --all-features -- -D warnings: clean
  • cargo clippy -p warp --lib --tests -- -D warnings: clean (note: --all-features on warp fails to compile due to pre-existing, unrelated issues in warp_completer and execution_profiles migration code on this base branch, not touched by this PR)
  • cargo fmt -p warp -p warp_graphql -- --check: clean

Evidence (side-by-side: live vs. historical breakdown for the same fixture)

New tests construct the same ChargedUsageTotals fixture (input $0.10, output $0.20, cache-read $0.015, cache-write $0.025) through both paths and assert they match:

  • ai::conversation_details_panel::tests::test_from_conversation_reflects_charged_usage_from_historical_server_metadata — historical/restored AIConversation path
  • workspaces::gql_convert::tests::conversation_usage_conversion::populates_charged_usage_from_aggregate_fields — Settings usage-history path
  • ai::conversation_details_panel::tests::test_set_server_metadata_does_not_regress_charged_usage — proves the never-regress guard
  • api::ai::tests::conversion_populates_charged_usage_aggregate_from_graphql_fields / conversion_leaves_charged_usage_aggregate_none_when_fields_absent — proves the flag-off/null-field path yields None, not a fabricated zero breakdown

Co-Authored-By: Warp agent@warp.dev

Xavientois and others added 5 commits August 14, 2026 09:45
…362)

Bumps warp_multi_agent_api from b0886a9 to b40bca6, which is a
descendant including #355 (per-category ChargedUsage/RequestCharges
schema) and #362 (wraps charged usage map in RequestCharges message).

Fixes compile breaks from additive proto fields on plain struct
literals (StreamFinished.request_charges,
ConversationUsageMetadata.total_charges,
CustomModel.reasoning_effort). No behavioral change; the new fields
are all set to None/empty defaults at existing construction sites.

Co-Authored-By: Warp <agent@warp.dev>
…ld warnings

- OrchestrationCreditRollup gains total_cost_in_cents, summed across the
  orchestrator and its locally-loaded descendants via usage_totals(). None
  when any contributor lacks a known baseline.
- Wires the rollup's dollar figure into the footer pill headline
  (render_usage_button) via format_credits_with_cost().
- Adds format_credits_with_cost() to view_util.rs (did not actually exist
  on master despite the task description claiming it landed in M1's U10).
- Silences deprecated-field warnings-as-errors surfaced by the proto bump
  (platform_credits_spent, request_cost) with #[allow(deprecated)] at each
  call site, matching the existing pattern already used nearby.

Co-Authored-By: Warp <agent@warp.dev>
…down

Adds a persistence::model::ChargedUsageTotals type that sums the
category/model-keyed StreamFinished.request_charges /
ConversationUsageMetadata.total_charges proto maps (from Foundation
task 1.3) into a flat input/output/cache-read/cache-write cost +
token breakdown, mirroring the Go SumChargedUsage helper.

Threads it through AIConversation::update_cost_and_usage_for_request
(new charged_usage_for_last_block + total_charged_usage fields on
ConversationUsageMetadata, alongside the existing
credits_spent_for_last_block pattern) and exposes it via
ConversationUsageTotals::charged_usage and the new
AIConversation::charged_usage_for_last_block() accessor, both
re-exported through tui_export for the TUI.

This is shared foundational plumbing needed by both the GUI (task 2.1)
and TUI (task 2.2) live-detail-breakdown verticals; landing it here so
2.1 can build on it directly.

Co-Authored-By: Warp <agent@warp.dev>
Consumes the cherry-picked ChargedUsageTotals plumbing (from task 2.2,
commit 47bda5e) to render the real input/output/cache-read/cache-write
dollar breakdown, gated by FeatureFlag::PricingTransparency:

- conversation_usage_view.rs (footer 'View details' panel + settings
  usage-history page): adds a 'Tokens used' row and a 'PRICING BREAKDOWN'
  section sourced from ConversationUsageInfo.charged_usage (cumulative,
  from AIConversation::usage_totals()). The settings usage-history source
  (gql_convert.rs) documents its gap as None pending the GraphQL vertical.
- conversation_details_panel.rs: same breakdown for local conversations
  (from_conversation); cloud-task/REST-backed sources document their gap
  as None pending the REST vertical.
- view_util_tests.rs (new): unit tests for format_credits_with_cost's
  flag gating.

Co-Authored-By: Warp <agent@warp.dev>
…etails surfaces

Consumes task 3.1's GraphQL aggregate fields (ConversationUsageMetadata.totalTokenCost/totalPlatformCostInCents) to close the two documented gaps from task 2.1 (warp PR #15148):
- Settings usage-history detail (usage_history_entry.rs -> ConversationUsageView) now sources charged_usage from the conversationUsage query instead of None.
- The conversation-details panel's non-streaming/restored path (list_ai_conversations / set_server_metadata) now populates total_charged_usage, so from_conversation() picks it up the same way it already does for live conversations.

Both paths reuse the existing FeatureFlag::PricingTransparency-gated pricing-breakdown rendering from task 2.1 -- no new UI code. set_server_metadata mirrors the existing never-regress guard used for total_provider_cost_in_cents so a stale async snapshot cannot clobber a fresher live total.

Also adds the local GraphQL schema.graphql entries for the new server fields/types (TokenCostBreakdown, ChargedUsageEntry, etc. from tasks 3.1/3.2) since they hadn't merged to develop yet.

Co-Authored-By: Warp <agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Aug 14, 2026
@Xavientois

Copy link
Copy Markdown
Contributor Author

Full local-stack validation (computer use)

Ran a genuinely local stack end-to-end: real warp-server (m3t2 branch, PostgreSQL/Redis/Temporal, pricing_transparency: true confirmed) + real warp client build (m3t3 branch, WITH_LOCAL_SERVER=1), no staging.

Validated:

  1. A real agent conversation ("What is 2+2?") completed via the server's /debug/llm-mock endpoint (real provider secrets aren't available in this sandbox), which still exercises the real token-usage/billing/GraphQL pipeline end-to-end.
  2. Both the inline per-message usage panel and the conversation's "task details" side panel show the PRICING BREAKDOWN section (Input/Cache read/Cache write/Output — all $0.00 here since the mocked response was trivial, 24 tokens).
  3. Quit and relaunched the app entirely, reopened the same conversation — PRICING BREAKDOWN rendered byte-for-byte identical values after restart. This is exactly the conversation-details-panel reload-after-restart gap this PR closes, and it worked.
  4. Settings → Billing & usage → Usage History stayed empty before and after restart, so that specific surface (the other gap this PR fixes, in gql_convert.rs) could not be independently confirmed in this pass. Root cause: userResolver.ConversationUsage explicitly returns [] for non-"user" principals, and this session was authenticated via a service-account-scoped local API key (no real Firebase user login available in this sandbox) — an environment/auth-type limitation, not a defect in this PR's code.

Media:

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant