Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: add user AI spend endpoint - #26978

Merged
ssncferreira merged 4 commits into
mainfrom
ssncf/user-ai-spend-endpoint
Jul 8, 2026
Merged

feat: add user AI spend endpoint#26978
ssncferreira merged 4 commits into
mainfrom
ssncf/user-ai-spend-endpoint

Conversation

@ssncferreira

@ssncferreira ssncferreira commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Description

Adds the GET /api/v2/users/{user}/ai/spend endpoint returning the user's current AI spend, effective budget, and period bounds.

Changes

  • Add userAISpendStatus handler under the same feature/experiment gate as /api/v2/users/{user}/ai/budget.
  • Add codersdk.UserAIBudgetSummary (embedded into UserAISpendStatus) and a UserAISpendStatus client method.
  • Move LimitSource from coderd/aibridge/budget to codersdk so the type is shared across endpoints.

Closes https://linear.app/codercom/issue/AIGOV-472

Note

Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Docs preview

📖 View docs preview for docs/reference/api/enterprise.md

ssncferreira commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Base automatically changed from ssncf/ai-budget-period-helper to main July 6, 2026 09:37
@ssncferreira
ssncferreira force-pushed the ssncf/user-ai-spend-endpoint branch from 78d6c24 to 9c05813 Compare July 6, 2026 12:26
@linear-code

linear-code Bot commented Jul 6, 2026

Copy link
Copy Markdown

AIGOV-472

@ssncferreira

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-07-06 12:43 UTC by @ssncferreira
Spend: $28.98 / $100.00

Review history
  • R1 (2026-07-06): 15 reviewers, 2 Nit, 2 Note, 1 P2, 2 P3, 1 P4, COMMENT. Review

deep-review v0.9.0 | Round 1 | 7c78698..76c0253

Last posted: Round 1, 8 findings (1 P2, 2 P3, 1 P4, 2 Nit, 2 Note), COMMENT. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P2 Open codersdk/aibridge.go:51 CurrentSpendMicros non-nullable; "not tracked" and "zero spend" indistinguishable in public API R1 Hisoka P2, Leorio P3 Yes
CRF-2 P3 Open codersdk/aibridge.go:15 AIBudgetLimitSource doc says "effective group" instead of "effective budget limit" R1 Gon P3, Leorio P3, Mafuuu Nit, Knov Nit Yes
CRF-3 P3 Open enterprise/coderd/aibridge.go:882 Period hardcoded to AIBudgetPeriodMonth, ignoring deployment config BudgetPeriod field R1 Hisoka P3, Mafuuu P3, Ryosuke P3, Knov Note, Kite Note Yes
CRF-4 P4 Open enterprise/coderd/aibridge_test.go:2957 No RBAC test for non-admin callers of spend endpoint R1 Bisky P4, Hisoka Note Yes
CRF-5 Nit Open enterprise/coderd/coderd.go:657 Middleware stack duplicated verbatim from budget route R1 Robin P3, Ryosuke Nit Yes
CRF-6 Nit Open codersdk/aibridge.go:28 UserAIBudgetSummary doc comment restates field names as prose R1 Gon Yes
CRF-7 Note Open enterprise/coderd/aibridge_test.go:3151 Test setup helper duplicates setupUserAIBudgetOverrideTest with minor additions R1 Robin P3 Yes
CRF-8 Note Open codersdk/aibridge.go:22 Frontend manual type uses "override" while backend uses "user_override" R1 Pariston Yes
CRF-9 Nit Dropped by orchestrator (squash merge includes PR description as commit body) commit Commit message has no body R1 Leorio No
CRF-10 Nit Dropped by orchestrator (minor verbosity in test helper doc; extra context useful for test authors) enterprise/coderd/aibridge_test.go:3145 setupUserAISpendStatusTest doc verbose R1 Gon No

Round log

Round 1

Panel. Netero clean. 1 P2, 2 P3, 1 P4, 2 Nit, 2 Note new. 2 dropped. Reviewed against 7c78698..76c0253.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@ssncferreira
ssncferreira force-pushed the ssncf/user-ai-spend-endpoint branch from 9c05813 to 76c0253 Compare July 6, 2026 12:44

@coder-agents-review coder-agents-review 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.

Clean mechanical floor (Netero found no convention or structural issues). The endpoint is well-structured: correct middleware gating, proper error handling with structured logging, deterministic test suite with a mock clock, and good branch coverage across no-budget, group-budget, and user-override scenarios.

Severity count: 1 P2, 2 P3, 1 P4, 2 Nit, 2 Note.

The P2 is an API contract issue: CurrentSpendMicros uses a bare int64 while the sibling fields use pointers to distinguish "no budget" from zero. Once this ships, changing the field type is a breaking change.

"A caller seeing current_spend_micros: 0 cannot tell whether the user spent nothing or whether spend is not tracked. The nullable fields already established the convention; the spend field breaks it." (Hisoka)

Five reviewers independently flagged the hardcoded AIBudgetPeriodMonth (CRF-3). The config field BudgetPeriod exists at codersdk/deployment.go:4611 and is unused. The sibling caller at coderd/aibridged/http.go:152 has the same issue.

🤖 This review was automatically generated with Coder Agents.

Comment thread codersdk/aibridge.go
Comment thread codersdk/aibridge.go Outdated
user := httpmw.UserParam(r)
logger := api.Logger.With(slog.F("user_id", user.ID))

periodWindow, err := budget.CurrentPeriod(api.Clock.Now(), codersdk.AIBudgetPeriodMonth)

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.

P3 [CRF-3] codersdk.AIBudgetPeriodMonth is hardcoded instead of reading from api.DeploymentValues.AI.BridgeConfig.BudgetPeriod.

The handler reads BudgetPolicy from config (line 889) but hardcodes the period. The config field BudgetPeriod is declared with serpent.EnumOf at codersdk/deployment.go:4611 and defaults to "month". Today only "month" exists, so behavior is correct. But this is the second caller to hardcode it (coderd/aibridged/http.go:152 does the same). When a second period type is added, both callers will silently use the wrong window.

Fix: codersdk.AIBudgetPeriod(api.DeploymentValues.AI.BridgeConfig.BudgetPeriod). The sibling in aibridged/http.go should get the same treatment. (Hisoka, Mafuuu, Ryosuke P3, Knov, Kite)

🤖

@ssncferreira ssncferreira Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferring to a follow-up PR because the fix should land in the endpoint, the daemon enforcement path, and the RPC test simultaneously.

Comment thread enterprise/coderd/aibridge_test.go
Comment thread enterprise/coderd/coderd.go Outdated
Comment thread codersdk/aibridge.go Outdated
Comment thread enterprise/coderd/aibridge_test.go Outdated
Comment thread codersdk/aibridge.go
const (
// AIBudgetLimitSourceUserOverride indicates the limit came from a
// per-user override.
AIBudgetLimitSourceUserOverride AIBudgetLimitSource = "user_override"

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.

Note [CRF-8] The pre-existing manual frontend type UserAISpend in site/src/api/api.ts:231 uses "override" for the user-override source, while this constant is "user_override". Currently harmless (the frontend never inspects limit_source), but the migration from the manual type to the generated UserAISpendStatus will change the wire value. The AIGOV-473 ticket should capture this discrepancy. (Pariston)

🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EhabY FYI

@ssncferreira
ssncferreira force-pushed the ssncf/user-ai-spend-endpoint branch from 76c0253 to b91c682 Compare July 6, 2026 15:50
@ssncferreira
ssncferreira marked this pull request as ready for review July 6, 2026 16:18
user := httpmw.UserParam(r)
logger := api.Logger.With(slog.F("user_id", user.ID))

periodWindow, err := budget.CurrentPeriod(api.Clock.Now(), codersdk.AIBudgetPeriodMonth)

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.

Since we'll support additional periods later, I wonder if we should move this behind a func (which always returns a static value for now).

Once the period is configurable, we'll just have one place to update it and everywhere will get the new value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to #26978 (comment) and will be addressed in a follow-up PR, since the period source should be consistent across the endpoint and the aibridge daemon.

Comment thread enterprise/coderd/aibridge.go Outdated
@ssncferreira
ssncferreira force-pushed the ssncf/user-ai-spend-endpoint branch from 2323d91 to 9f18e80 Compare July 8, 2026 10:26
@ssncferreira
ssncferreira merged commit 48f07e6 into main Jul 8, 2026
53 of 56 checks passed
@ssncferreira
ssncferreira deleted the ssncf/user-ai-spend-endpoint branch July 8, 2026 11:46
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants