feat(web): per-user Bedrock spend attribution table on the Usage page#15
Merged
Conversation
GET /api/usage/by-user (requireAdmin) groups all runs by user and splits each user's spend by backend (bedrock / anthropic / unknown), ranked by Bedrock spend. Runs with no resolved userId collapse into a single "Unattributed" row that always sorts last — never folded into a real user. Returns the same parity-estimate flag as /api/usage/local so the UI can caveat Bedrock dollars. Admin-only by design: members are always scoped to themselves (resolveViewScope), so a team-wide breakdown is an admin artifact, mirroring /api/budgets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a "Spend by user" table — the "who is burning the Bedrock spend" view AWS Cost Explorer can't give you (it attributes by IAM principal, with no user/agent concept). The useBedrockByUser hook returns null on the route's 403, so non-admins never render the section. The Bedrock column carries the "Bedrock est." badge; Unattributed runs render as a muted row, never hidden. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6dee19a to
8d4e528
Compare
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
The "who is burning the Bedrock spend?" view — the per-principal attribution AWS Cost Explorer structurally can't give you (it bills by IAM principal, with no user / agent / session concept). Builds directly on the backend segmentation from #14.
Changes
GET /api/usage/by-user(requireAdmin,b33122b) — groups all runs by user, splits each user's spend by backend (bedrock/anthropic/unknown), ranked by Bedrock spend desc. Runs with no resolveduserIdcollapse into a single "Unattributed" row that always sorts last — never folded into a real user. Returns the parity-estimate flag from feat: segment agent spend by backend (Bedrock vs direct) on the Usage page #14. Admin-only by design: members are always self-scoped (resolveViewScope), so a team-wide breakdown is an admin artifact — mirrors/api/budgets.6dee19a) —useBedrockByUserreturns null on the route's 403, so non-admins never render the section (no client role check needed). Bedrock column carries the "Bedrock est." badge; Unattributed renders as a muted row.Attribution gate — verified
A per-user table is only worth building if
user_idactually populates. Confirmed against currentmain: SDK mode always stampsuserId(bearer token); local mode resolves it viaresolveLocalUserId(creds-email → sole-user fallback), NULL only in multi-user-without-creds setups. Those NULL runs are shown honestly as "Unattributed" rather than hidden.Verification
tscbuild green, web lint clean.auth-gaps.test.ts): member → 403; admin sees per-user backend buckets, ranked by Bedrock, with the Unattributed row last + estimate flag.better-sqlite3@11.10.0has no compatible binary. The tests will first run on CI (Node 20). Flagging explicitly.Deferred (out of scope)
Budget-vs-actual stays on
/admin/budgets(avoids conflating a total budget with Bedrock spend); period-scoping (all-time matches the page today); real per-region Bedrock rates;run_metrics.backendcolumn.🤖 Generated with Claude Code