fix: ai cost control cap configurable AI spend limit (#27640)

## Problem

A configured AI spend limit was only validated as `gte=0`, with no upper
bound. The group spend query multiplies the per-member limit by the
number of attributed members, so a large enough limit overflows `bigint`
and fails the whole query, returning an error for every group in the
request rather than just the misconfigured one.

## Changes

- Add `MaxAISpendLimitMicros`, $1,000,000 per member per budget period.
- Reject group budgets and per-user overrides above the maximum with a
400 naming the limit.
- Bound both budget forms in the UI so they show the valid range before
submitting.

Follow-up
https://github.com/coder/coder/pull/27589#discussion_r3668956350
Depends on https://github.com/coder/coder/pull/27589

> [!NOTE]
> Initially generated by Claude Opus 5, modified and reviewed by
@ssncferreira
This commit is contained in:
Susana Ferreira
2026-07-29 14:00:13 +01:00
committed by GitHub
parent 4987afada7
commit e71249a821
12 changed files with 218 additions and 30 deletions
+8 -2
View File
@@ -15,6 +15,10 @@ import (
"github.com/coder/coder/v2/coderd/util/slice"
)
// MaxAISpendLimitMicros is the highest AI spend limit that can be configured,
// $1,000,000 per member per budget period.
const MaxAISpendLimitMicros int64 = 1_000_000_000_000
// AIBudgetLimitSource identifies which tier produced the user's
// effective budget limit.
type AIBudgetLimitSource string
@@ -418,6 +422,7 @@ type GroupAIBudget struct {
}
type UpsertGroupAIBudgetRequest struct {
// SpendLimitMicros must not exceed MaxAISpendLimitMicros.
SpendLimitMicros int64 `json:"spend_limit_micros" validate:"gte=0"`
}
@@ -485,8 +490,9 @@ type UserAIBudgetOverride struct {
type UpsertUserAIBudgetOverrideRequest struct {
// GroupID is the group the user's spend is attributed to. The user must
// be a member of this group.
GroupID uuid.UUID `json:"group_id" format:"uuid" validate:"required"`
SpendLimitMicros int64 `json:"spend_limit_micros" validate:"gte=0"`
GroupID uuid.UUID `json:"group_id" format:"uuid" validate:"required"`
// SpendLimitMicros must not exceed MaxAISpendLimitMicros.
SpendLimitMicros int64 `json:"spend_limit_micros" validate:"gte=0"`
}
// UserAIBudgetOverride returns the AI spend budget override configured for the given user.