feat: segment agent spend by backend (Bedrock vs direct) on the Usage page#14
Merged
Conversation
The hook already detects the backend (CLAUDE_CODE_USE_BEDROCK) and computes per-model cost from the transcript, then discarded both before persisting metrics — so the dashboard could not tell a Bedrock run from a direct one. Add optional `backend` and `byModel` to the Metrics type and carry the already-in-scope values into the session-end metrics literal. Local mode persists them for free via finalizeRun -> updateRun into the runs.metrics JSON column (no migration, no new column). Also export BEDROCK_PRICING_IS_PARITY_ESTIMATE + a verified date so the UI can flag Bedrock dollars as estimated — BEDROCK_PRICING is still a parity copy of the Anthropic-direct rates, so the $-magnitude is an estimate while the backend attribution is exact. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… badge /api/usage/local now buckets captured spend into bedrock / anthropic / unknown (the buckets reconcile to totalCost) and surfaces the parity-estimate flag + verified date. The Usage page renders a "By backend" breakdown under the local cost card. Untagged/legacy runs show as "Not yet classified" and are never folded into Direct (which would manufacture a confident "you have no Bedrock spend"). Bedrock dollars carry a "Bedrock est." badge + footnote because the rates are an Anthropic-parity estimate, not AWS billing. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
In SDK mode the metrics POST body dropped backend/byModel, and the route
rebuilt the metrics object field-by-field, so SDK-mode runs always read as
unclassified — a confusing half-feature next to local mode. Forward both
fields from doReportMetrics and validate + persist them in the route
(backend in {anthropic, bedrock}; byModel an object mapping model ids to
finite, non-negative costs). They live only in the runs.metrics JSON blob —
no run_metrics column, since nothing queries run_metrics for backend yet.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
AgentOps already computes backend-agnostic cost from Claude Code transcripts (the differentiator that governs Bedrock/Vertex traffic the Anthropic Admin API can't see) — but the hook detected the backend and computed per-model cost, then threw both away before persisting metrics. So the dashboard couldn't tell a Bedrock run from a direct one, and the Bedrock value was invisible.
This slice makes that value legible: it persists the per-run
backendandbyModel, then segments Usage-page spend by backend with an explicit honesty badge.Changes
b79b6a3) — optionalbackend+byModelon theMetricstype; the already-in-scope values are carried into the session-end metrics literal. Local mode persists for free viafinalizeRun → updateRuninto theruns.metricsJSON column (no migration, no new column). ExportsBEDROCK_PRICING_IS_PARITY_ESTIMATE+ verified date.9152a92) —/api/usage/localbuckets spend intobedrock/anthropic/unknown(buckets reconcile tototalCost) and surfaces the parity-estimate flag. The Usage page renders a "By backend" breakdown; untagged/legacy runs show as "Not yet classified" and are never folded into Direct; Bedrock dollars carry a "Bedrock est." badge + footnote.3833679) —doReportMetricsforwardsbackend/byModel; the metrics route validates (backend ∈ {anthropic,bedrock};byModela map of finite, non-negative costs) and persists them, so SDK-mode runs are tagged too.Honesty guardrail
BEDROCK_PRICINGis still a parity copy of the Anthropic-direct rates, so the $-magnitude is an estimate while the backend attribution is exact. The UI says exactly that: token volumes + "this ran on Bedrock" are exact; dollar amounts are estimated at Anthropic-direct US rates (verified 2026-05-13) pending AWS verification. When real Bedrock rates land, flip the flag and the caveat disappears with zero UI rework.Verification
Deferred to follow-ups (out of scope here)
run_metrics.backendcolumn (nothing queries it yet) · per-user Bedrock attribution table · stacked Cost-Per-Day chart · real per-region Bedrock rates · historical backfill from on-disk transcripts.🤖 Generated with Claude Code