feat(agents): per-model rate table + per-delta cost accumulation #1003

Merged
reviewer merged 1 commit from code-lead/953 into main 2026-05-08 21:32:54 +00:00
Collaborator

Charge each usage_delta against a per-(provider, modelId) rate table and accumulate USD onto record.cost_usd so cursor runs stop reporting $0 and provider switches bill correctly.

  • Seed table covers Claude 4.x (Opus 4.7 / Sonnet 4.6 / Haiku 4.5), cursor Agent.listModels SKUs (composer-2, gpt-5.5), and Bedrock-routed Anthropic SKUs; unverified rows carry // TODO(#953) comments
  • Operator overrides via service_config.model_rates_json (migration 0012); cache invalidator hooked into the webhook-config reloader so an edit lands without a restart
  • Adapters tag usage_delta with the run model; event-log.ts looks up the rate at delta time (not terminal time) — runtime overrides do not retroactively re-price; SDK-reported total_cost_usd is preserved on the result row as sdk_cost_usd for diagnostics
  • Unknown (provider, model) → one cost_unknown_model system event per task + record.cost_unknown_model: true; SSE usage_delta envelope carries total_cost_usd + cost_unknown_model so the live chip ticks without a refetch
  • <TokenMeter> renders a 3-sig-figs cost chip in both compact and full variants; unknown-model runs render ? with a tooltip pointing at service_config.model_rates_json

Test plan

  • just qa clean — 3473 server tests pass (14 new in model-rates.test.ts, 4 new in event-log-delta.test.ts)
  • Hand-computed cost on a sonnet-rated stream (3 deltas) matches record.cost_usd to 9 decimals
  • Unknown-model fallback emits exactly one audit row regardless of how many deltas land
  • Mid-run override does not retroactively re-price earlier deltas

Closes #953

Charge each `usage_delta` against a per-`(provider, modelId)` rate table and accumulate USD onto `record.cost_usd` so cursor runs stop reporting `$0` and provider switches bill correctly. - Seed table covers Claude 4.x (Opus 4.7 / Sonnet 4.6 / Haiku 4.5), cursor `Agent.listModels` SKUs (composer-2, gpt-5.5), and Bedrock-routed Anthropic SKUs; unverified rows carry `// TODO(#953)` comments - Operator overrides via `service_config.model_rates_json` (migration `0012`); cache invalidator hooked into the webhook-config reloader so an edit lands without a restart - Adapters tag `usage_delta` with the run model; `event-log.ts` looks up the rate **at delta time** (not terminal time) — runtime overrides do not retroactively re-price; SDK-reported `total_cost_usd` is preserved on the result row as `sdk_cost_usd` for diagnostics - Unknown `(provider, model)` → one `cost_unknown_model` system event per task + `record.cost_unknown_model: true`; SSE `usage_delta` envelope carries `total_cost_usd` + `cost_unknown_model` so the live chip ticks without a refetch - `<TokenMeter>` renders a 3-sig-figs cost chip in both `compact` and `full` variants; unknown-model runs render `?` with a tooltip pointing at `service_config.model_rates_json` ## Test plan - [x] `just qa` clean — 3473 server tests pass (14 new in `model-rates.test.ts`, 4 new in `event-log-delta.test.ts`) - [x] Hand-computed cost on a sonnet-rated stream (3 deltas) matches `record.cost_usd` to 9 decimals - [x] Unknown-model fallback emits exactly one audit row regardless of how many deltas land - [x] Mid-run override does not retroactively re-price earlier deltas Closes #953
feat(agents): per-model rate table + per-delta cost accumulation (cursor parity)
All checks were successful
qa / i18n-string-check (pull_request) Successful in 21s
qa / dockerfile (pull_request) Successful in 26s
qa / db-schema (pull_request) Successful in 27s
qa / sql-layer-check (pull_request) Successful in 9s
qa / qa-1 (pull_request) Successful in 2m30s
qa / qa (pull_request) Successful in 0s
c7b0819ecf
Adds `infrastructure/agent/model-rates.ts` with seed rates for the Claude 4.x
fleet, cursor SKUs, and Bedrock-routed Anthropic SKUs. The runner charges
each `usage_delta` at the rate effective at delta time and accumulates onto
`record.cost_usd`. Operator overrides via `service_config.model_rates_json`
(0012 migration); `cost_unknown_model` system event + `?` chip flag runs
whose model the table doesn't know about.

Closes #953

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
reviewer approved these changes 2026-05-08 21:32:44 +00:00
reviewer left a comment

Rate table, delta accumulation, override layer, cache invalidation, SSE tick, and frontend chip all correct. Tests cover seed coverage, hand-computed cost, unknown-model dedup, and the non-retroactive mid-run override AC.

Nit: fmtCostUsd(0.043)"$0.0430" (trailing zero from toPrecision(3)); the PR description shows $0.043. Technically correct per 3-sig-figs spec, but cosmetically odd — worth stripping trailing zeros before shipping to users.

Rate table, delta accumulation, override layer, cache invalidation, SSE tick, and frontend chip all correct. Tests cover seed coverage, hand-computed cost, unknown-model dedup, and the non-retroactive mid-run override AC. Nit: `fmtCostUsd(0.043)` → `"$0.0430"` (trailing zero from `toPrecision(3)`); the PR description shows `$0.043`. Technically correct per 3-sig-figs spec, but cosmetically odd — worth stripping trailing zeros before shipping to users.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!1003
No description provided.