feat: add ai-gateway-cost-control experiment flag (#26399)

Adds the `ai-gateway-cost-control` experiment flag to gate new cost
control endpoints and upcoming frontend UI behind an explicit opt-in.

Currently AI Gateway cost control supports the following endpoints:
- `GET/PUT/DELETE /api/v2/organizations/{org}/groups/{group}/ai/budget`
- `GET/PUT/DELETE /api/v2/users/{user}/ai/budget`

Note: the group-level endpoints were already released in v2.34.0 and
remain ungated. Only the user-level endpoints are gated behind this
experiment. Future cost control endpoints and UI should use this
experiment for gating until the feature is stable.

> Generated by Coder Agents on behalf of @ssncferreira
This commit is contained in:
Susana Ferreira
2026-06-16 10:33:34 +01:00
committed by GitHub
parent a1330e3a8c
commit 12d7ad6100
7 changed files with 29 additions and 9 deletions
+7 -3
View File
@@ -19279,9 +19279,11 @@ const docTemplate = `{
"mcp-server-http",
"workspace-build-updates",
"nats_pubsub",
"minimum-implicit-member"
"minimum-implicit-member",
"ai-gateway-cost-control"
],
"x-enum-comments": {
"ExperimentAIGatewayCostControl": "Enables AI Gateway cost control functionality.",
"ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.",
"ExperimentExample": "This isn't used for anything.",
"ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.",
@@ -19301,7 +19303,8 @@ const docTemplate = `{
"Enables the MCP HTTP server functionality.",
"Enables publishing workspace build updates to the all builds pubsub channel.",
"Enables embedded NATS pubsub.",
"Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts."
"Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts.",
"Enables AI Gateway cost control functionality."
],
"x-enum-varnames": [
"ExperimentExample",
@@ -19312,7 +19315,8 @@ const docTemplate = `{
"ExperimentMCPServerHTTP",
"ExperimentWorkspaceBuildUpdates",
"ExperimentNATSPubsub",
"ExperimentMinimumImplicitMember"
"ExperimentMinimumImplicitMember",
"ExperimentAIGatewayCostControl"
]
},
"codersdk.ExternalAPIKeyScopes": {
+7 -3
View File
@@ -17493,9 +17493,11 @@
"mcp-server-http",
"workspace-build-updates",
"nats_pubsub",
"minimum-implicit-member"
"minimum-implicit-member",
"ai-gateway-cost-control"
],
"x-enum-comments": {
"ExperimentAIGatewayCostControl": "Enables AI Gateway cost control functionality.",
"ExperimentAutoFillParameters": "This should not be taken out of experiments until we have redesigned the feature.",
"ExperimentExample": "This isn't used for anything.",
"ExperimentMCPServerHTTP": "Enables the MCP HTTP server functionality.",
@@ -17515,7 +17517,8 @@
"Enables the MCP HTTP server functionality.",
"Enables publishing workspace build updates to the all builds pubsub channel.",
"Enables embedded NATS pubsub.",
"Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts."
"Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts.",
"Enables AI Gateway cost control functionality."
],
"x-enum-varnames": [
"ExperimentExample",
@@ -17526,7 +17529,8 @@
"ExperimentMCPServerHTTP",
"ExperimentWorkspaceBuildUpdates",
"ExperimentNATSPubsub",
"ExperimentMinimumImplicitMember"
"ExperimentMinimumImplicitMember",
"ExperimentAIGatewayCostControl"
]
},
"codersdk.ExternalAPIKeyScopes": {
+4
View File
@@ -5108,6 +5108,7 @@ const (
ExperimentWorkspaceBuildUpdates Experiment = "workspace-build-updates" // Enables publishing workspace build updates to the all builds pubsub channel.
ExperimentNATSPubsub Experiment = "nats_pubsub" // Enables embedded NATS pubsub.
ExperimentMinimumImplicitMember Experiment = "minimum-implicit-member" // Allows organizations to deviate from the default organization-member roles, in support of Gateway Accounts.
ExperimentAIGatewayCostControl Experiment = "ai-gateway-cost-control" // Enables AI Gateway cost control functionality.
)
func (e Experiment) DisplayName() string {
@@ -5130,6 +5131,8 @@ func (e Experiment) DisplayName() string {
return "NATS Pubsub"
case ExperimentMinimumImplicitMember:
return "Gateway Accounts (minimum implicit member)"
case ExperimentAIGatewayCostControl:
return "AI Gateway Cost Control"
default:
// Split on hyphen and convert to title case
// e.g. "mcp-server-http" -> "Mcp Server Http"
@@ -5149,6 +5152,7 @@ var ExperimentsKnown = Experiments{
ExperimentNATSPubsub,
ExperimentWorkspaceBuildUpdates,
ExperimentMinimumImplicitMember,
ExperimentAIGatewayCostControl,
}
// ExperimentsSafe should include all experiments that are safe for
+3 -3
View File
@@ -6961,9 +6961,9 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
#### Enumerated Values
| Value(s) |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `auto-fill-parameters`, `example`, `mcp-server-http`, `minimum-implicit-member`, `nats_pubsub`, `notifications`, `oauth2`, `workspace-build-updates`, `workspace-usage` |
| Value(s) |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ai-gateway-cost-control`, `auto-fill-parameters`, `example`, `mcp-server-http`, `minimum-implicit-member`, `nats_pubsub`, `notifications`, `oauth2`, `workspace-build-updates`, `workspace-usage` |
## codersdk.ExternalAPIKeyScopes
+4
View File
@@ -2706,6 +2706,7 @@ func TestUserAIBudgetOverrideRoleAccess(t *testing.T) {
dv := coderdtest.DeploymentValues(t)
dv.AI.BridgeConfig.Enabled = serpent.Bool(true)
dv.Experiments = []string{string(codersdk.ExperimentAIGatewayCostControl)}
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{DeploymentValues: dv},
LicenseOptions: &coderdenttest.LicenseOptions{
@@ -2803,6 +2804,7 @@ func TestUserAIBudgetOverrideDeletedOnMembershipRemoval(t *testing.T) {
dv := coderdtest.DeploymentValues(t)
dv.AI.BridgeConfig.Enabled = serpent.Bool(true)
dv.Experiments = []string{string(codersdk.ExperimentAIGatewayCostControl)}
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{DeploymentValues: dv},
LicenseOptions: &coderdenttest.LicenseOptions{
@@ -2892,6 +2894,7 @@ func setupUserAIBudgetOverrideTest(t *testing.T) (adminClient *codersdk.Client,
dv := coderdtest.DeploymentValues(t)
dv.AI.BridgeConfig.Enabled = serpent.Bool(true)
dv.Experiments = []string{string(codersdk.ExperimentAIGatewayCostControl)}
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{DeploymentValues: dv},
LicenseOptions: &coderdenttest.LicenseOptions{
@@ -2930,6 +2933,7 @@ func setupUserAIBudgetOverrideAuditTest(t *testing.T) (database.Store, *codersdk
)
dv := coderdtest.DeploymentValues(t)
dv.AI.BridgeConfig.Enabled = serpent.Bool(true)
dv.Experiments = []string{string(codersdk.ExperimentAIGatewayCostControl)}
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
AuditLogging: true,
Options: &coderdtest.Options{
+2
View File
@@ -612,6 +612,8 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
r.Route("/users/{user}/ai/budget", func(r chi.Router) {
// AI cost controls are a paid feature (AI Governance add-on).
r.Use(
// TODO(AIGOV-443): remove once AI Gateway cost control functionality is stable.
httpmw.RequireExperiment(api.AGPL.Experiments, codersdk.ExperimentAIGatewayCostControl),
api.RequireFeatureMW(codersdk.FeatureAIBridge),
apiKeyMiddleware,
httpmw.ExtractUserParam(options.Database),
+2
View File
@@ -4347,6 +4347,7 @@ export const EntitlementsWarningHeader = "X-Coder-Entitlements-Warning";
// From codersdk/deployment.go
export type Experiment =
| "ai-gateway-cost-control"
| "auto-fill-parameters"
| "example"
| "mcp-server-http"
@@ -4358,6 +4359,7 @@ export type Experiment =
| "workspace-usage";
export const Experiments: Experiment[] = [
"ai-gateway-cost-control",
"auto-fill-parameters",
"example",
"mcp-server-http",