mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add AI budget policy and period deployment config (#25122)
Closes https://linear.app/codercom/issue/AIGOV-283/add-deployment-config-for-ai-budget-policy-and-period Adds `CODER_AI_BUDGET_POLICY` and `CODER_AI_BUDGET_PERIOD` deployment options for AI Governance cost controls.
This commit is contained in:
committed by
GitHub
parent
fabf7d31fc
commit
b5e1ea33d8
+9
@@ -173,6 +173,15 @@ AI BRIDGE OPTIONS:
|
||||
Emit structured logs for AI Bridge interception records. Use this for
|
||||
exporting these records to external SIEM or observability systems.
|
||||
|
||||
--ai-budget-period month, $CODER_AI_BUDGET_PERIOD (default: month)
|
||||
Determines when accumulated AI spend resets to zero, aligned to UTC
|
||||
calendar boundaries. Only "month" is currently supported.
|
||||
|
||||
--ai-budget-policy highest, $CODER_AI_BUDGET_POLICY (default: highest)
|
||||
Determines the effective group when a user belongs to multiple groups
|
||||
with AI budgets. "highest" selects the group with the largest spend
|
||||
limit, and is currently the only supported value.
|
||||
|
||||
AI BRIDGE PROXY OPTIONS:
|
||||
--aibridge-proxy-dump-dir string, $CODER_AIBRIDGE_PROXY_DUMP_DIR
|
||||
Directory for dumping MITM request/response pairs to disk for
|
||||
|
||||
+9
@@ -837,6 +837,15 @@ aibridge:
|
||||
# or re-open the circuit.
|
||||
# (default: 3, type: int)
|
||||
circuit_breaker_max_requests: 3
|
||||
# Determines the effective group when a user belongs to multiple groups with AI
|
||||
# budgets. "highest" selects the group with the largest spend limit, and is
|
||||
# currently the only supported value.
|
||||
# (default: highest, type: enum[highest])
|
||||
budget_policy: highest
|
||||
# Determines when accumulated AI spend resets to zero, aligned to UTC calendar
|
||||
# boundaries. Only "month" is currently supported.
|
||||
# (default: month, type: enum[month])
|
||||
budget_period: month
|
||||
aibridgeproxy:
|
||||
# Enable the AI Bridge MITM Proxy for intercepting and decrypting AI provider
|
||||
# requests.
|
||||
|
||||
Generated
+7
@@ -13802,6 +13802,13 @@ const docTemplate = `{
|
||||
}
|
||||
]
|
||||
},
|
||||
"budget_period": {
|
||||
"type": "string"
|
||||
},
|
||||
"budget_policy": {
|
||||
"description": "Budget settings for AI Governance cost controls.",
|
||||
"type": "string"
|
||||
},
|
||||
"circuit_breaker_enabled": {
|
||||
"description": "Circuit breaker protects against cascading failures from upstream AI\nprovider overload (503, 529).",
|
||||
"type": "boolean"
|
||||
|
||||
Generated
+7
@@ -12278,6 +12278,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"budget_period": {
|
||||
"type": "string"
|
||||
},
|
||||
"budget_policy": {
|
||||
"description": "Budget settings for AI Governance cost controls.",
|
||||
"type": "string"
|
||||
},
|
||||
"circuit_breaker_enabled": {
|
||||
"description": "Circuit breaker protects against cascading failures from upstream AI\nprovider overload (503, 529).",
|
||||
"type": "boolean"
|
||||
|
||||
@@ -574,6 +574,34 @@ var PostgresAuthDrivers = []string{
|
||||
// based on max open connections.
|
||||
const PostgresConnMaxIdleAuto = "auto"
|
||||
|
||||
// AIBudgetPolicy determines how the effective group is selected when a user
|
||||
// belongs to multiple groups with AI budgets configured.
|
||||
type AIBudgetPolicy string
|
||||
|
||||
const (
|
||||
// AIBudgetPolicyHighest selects the group with the highest spend limit.
|
||||
AIBudgetPolicyHighest AIBudgetPolicy = "highest"
|
||||
)
|
||||
|
||||
// AIBudgetPolicies lists the supported AIBudgetPolicy values.
|
||||
var AIBudgetPolicies = []string{
|
||||
string(AIBudgetPolicyHighest),
|
||||
}
|
||||
|
||||
// AIBudgetPeriod determines when accumulated AI spend resets to zero,
|
||||
// aligned to UTC calendar boundaries.
|
||||
type AIBudgetPeriod string
|
||||
|
||||
const (
|
||||
// AIBudgetPeriodMonth resets spend at the start of each UTC calendar month.
|
||||
AIBudgetPeriodMonth AIBudgetPeriod = "month"
|
||||
)
|
||||
|
||||
// AIBudgetPeriods lists the supported AIBudgetPeriod values.
|
||||
var AIBudgetPeriods = []string{
|
||||
string(AIBudgetPeriodMonth),
|
||||
}
|
||||
|
||||
// DeploymentValues is the central configuration values the coder server.
|
||||
type DeploymentValues struct {
|
||||
Verbose serpent.Bool `json:"verbose,omitempty"`
|
||||
@@ -3893,6 +3921,27 @@ Write out the current server config as YAML to stdout.`,
|
||||
YAML: "circuit_breaker_max_requests",
|
||||
},
|
||||
|
||||
{
|
||||
Name: "AI Budget Policy",
|
||||
Description: "Determines the effective group when a user belongs to multiple groups with AI budgets. \"highest\" selects the group with the largest spend limit, and is currently the only supported value.",
|
||||
Flag: "ai-budget-policy",
|
||||
Env: "CODER_AI_BUDGET_POLICY",
|
||||
Value: serpent.EnumOf(&c.AI.BridgeConfig.BudgetPolicy, AIBudgetPolicies...),
|
||||
Default: string(AIBudgetPolicyHighest),
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "budget_policy",
|
||||
},
|
||||
{
|
||||
Name: "AI Budget Period",
|
||||
Description: "Determines when accumulated AI spend resets to zero, aligned to UTC calendar boundaries. Only \"month\" is currently supported.",
|
||||
Flag: "ai-budget-period",
|
||||
Env: "CODER_AI_BUDGET_PERIOD",
|
||||
Value: serpent.EnumOf(&c.AI.BridgeConfig.BudgetPeriod, AIBudgetPeriods...),
|
||||
Default: string(AIBudgetPeriodMonth),
|
||||
Group: &deploymentGroupAIBridge,
|
||||
YAML: "budget_period",
|
||||
},
|
||||
|
||||
// AI Bridge Proxy Options
|
||||
{
|
||||
Name: "AI Bridge Proxy Enabled",
|
||||
@@ -4107,6 +4156,9 @@ type AIBridgeConfig struct {
|
||||
StructuredLogging serpent.Bool `json:"structured_logging" typescript:",notnull"`
|
||||
SendActorHeaders serpent.Bool `json:"send_actor_headers" typescript:",notnull"`
|
||||
AllowBYOK serpent.Bool `json:"allow_byok" typescript:",notnull"`
|
||||
// Budget settings for AI Governance cost controls.
|
||||
BudgetPolicy string `json:"budget_policy,omitempty" typescript:",notnull"`
|
||||
BudgetPeriod string `json:"budget_period,omitempty" typescript:",notnull"`
|
||||
// Circuit breaker protects against cascading failures from upstream AI
|
||||
// provider overload (503, 529).
|
||||
CircuitBreakerEnabled serpent.Bool `json:"circuit_breaker_enabled" typescript:",notnull"`
|
||||
|
||||
@@ -768,6 +768,66 @@ func TestRetentionConfigParsing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIBudgetConfigParsing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Defaults", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := codersdk.DeploymentValues{}
|
||||
opts := dv.Options()
|
||||
|
||||
require.NoError(t, opts.SetDefaults())
|
||||
|
||||
assert.Equal(t, string(codersdk.AIBudgetPolicyHighest), dv.AI.BridgeConfig.BudgetPolicy)
|
||||
assert.Equal(t, string(codersdk.AIBudgetPeriodMonth), dv.AI.BridgeConfig.BudgetPeriod)
|
||||
})
|
||||
|
||||
t.Run("AcceptsSupportedValues", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := codersdk.DeploymentValues{}
|
||||
opts := dv.Options()
|
||||
|
||||
require.NoError(t, opts.SetDefaults())
|
||||
require.NoError(t, opts.ParseEnv([]serpent.EnvVar{
|
||||
{Name: "CODER_AI_BUDGET_POLICY", Value: string(codersdk.AIBudgetPolicyHighest)},
|
||||
{Name: "CODER_AI_BUDGET_PERIOD", Value: string(codersdk.AIBudgetPeriodMonth)},
|
||||
}))
|
||||
|
||||
assert.Equal(t, string(codersdk.AIBudgetPolicyHighest), dv.AI.BridgeConfig.BudgetPolicy)
|
||||
assert.Equal(t, string(codersdk.AIBudgetPeriodMonth), dv.AI.BridgeConfig.BudgetPeriod)
|
||||
})
|
||||
|
||||
t.Run("RejectsUnsupportedPolicy", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := codersdk.DeploymentValues{}
|
||||
opts := dv.Options()
|
||||
|
||||
require.NoError(t, opts.SetDefaults())
|
||||
err := opts.ParseEnv([]serpent.EnvVar{
|
||||
{Name: "CODER_AI_BUDGET_POLICY", Value: "invalid"},
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid choice")
|
||||
})
|
||||
|
||||
t.Run("RejectsUnsupportedPeriod", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := codersdk.DeploymentValues{}
|
||||
opts := dv.Options()
|
||||
|
||||
require.NoError(t, opts.SetDefaults())
|
||||
err := opts.ParseEnv([]serpent.EnvVar{
|
||||
{Name: "CODER_AI_BUDGET_PERIOD", Value: "invalid"},
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid choice")
|
||||
})
|
||||
}
|
||||
|
||||
func TestComputeMaxIdleConns(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Generated
+2
@@ -193,6 +193,8 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
|
||||
"region": "string",
|
||||
"small_fast_model": "string"
|
||||
},
|
||||
"budget_period": "string",
|
||||
"budget_policy": "string",
|
||||
"circuit_breaker_enabled": true,
|
||||
"circuit_breaker_failure_threshold": 0,
|
||||
"circuit_breaker_interval": 0,
|
||||
|
||||
Generated
+10
@@ -450,6 +450,8 @@
|
||||
"region": "string",
|
||||
"small_fast_model": "string"
|
||||
},
|
||||
"budget_period": "string",
|
||||
"budget_policy": "string",
|
||||
"circuit_breaker_enabled": true,
|
||||
"circuit_breaker_failure_threshold": 0,
|
||||
"circuit_breaker_interval": 0,
|
||||
@@ -487,6 +489,8 @@
|
||||
| `allow_byok` | boolean | false | | |
|
||||
| `anthropic` | [codersdk.AIBridgeAnthropicConfig](#codersdkaibridgeanthropicconfig) | false | | Deprecated: Use Providers with indexed CODER_AIBRIDGE_PROVIDER_<N>_* env vars instead. |
|
||||
| `bedrock` | [codersdk.AIBridgeBedrockConfig](#codersdkaibridgebedrockconfig) | false | | Deprecated: Use Providers with indexed CODER_AIBRIDGE_PROVIDER_<N>_* env vars instead. |
|
||||
| `budget_period` | string | false | | |
|
||||
| `budget_policy` | string | false | | Budget settings for AI Governance cost controls. |
|
||||
| `circuit_breaker_enabled` | boolean | false | | Circuit breaker protects against cascading failures from upstream AI provider overload (503, 529). |
|
||||
| `circuit_breaker_failure_threshold` | integer | false | | |
|
||||
| `circuit_breaker_interval` | integer | false | | |
|
||||
@@ -1275,6 +1279,8 @@
|
||||
"region": "string",
|
||||
"small_fast_model": "string"
|
||||
},
|
||||
"budget_period": "string",
|
||||
"budget_policy": "string",
|
||||
"circuit_breaker_enabled": true,
|
||||
"circuit_breaker_failure_threshold": 0,
|
||||
"circuit_breaker_interval": 0,
|
||||
@@ -5288,6 +5294,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
"region": "string",
|
||||
"small_fast_model": "string"
|
||||
},
|
||||
"budget_period": "string",
|
||||
"budget_policy": "string",
|
||||
"circuit_breaker_enabled": true,
|
||||
"circuit_breaker_failure_threshold": 0,
|
||||
"circuit_breaker_interval": 0,
|
||||
@@ -5884,6 +5892,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
"region": "string",
|
||||
"small_fast_model": "string"
|
||||
},
|
||||
"budget_period": "string",
|
||||
"budget_policy": "string",
|
||||
"circuit_breaker_enabled": true,
|
||||
"circuit_breaker_failure_threshold": 0,
|
||||
"circuit_breaker_interval": 0,
|
||||
|
||||
Generated
+22
@@ -1901,6 +1901,28 @@ Allow users to provide their own LLM API keys or subscriptions. When disabled, o
|
||||
|
||||
Enable the circuit breaker to protect against cascading failures from upstream AI provider overload (503, 529).
|
||||
|
||||
### --ai-budget-policy
|
||||
|
||||
| | |
|
||||
|-------------|--------------------------------------|
|
||||
| Type | <code>highest</code> |
|
||||
| Environment | <code>$CODER_AI_BUDGET_POLICY</code> |
|
||||
| YAML | <code>aibridge.budget_policy</code> |
|
||||
| Default | <code>highest</code> |
|
||||
|
||||
Determines the effective group when a user belongs to multiple groups with AI budgets. "highest" selects the group with the largest spend limit, and is currently the only supported value.
|
||||
|
||||
### --ai-budget-period
|
||||
|
||||
| | |
|
||||
|-------------|--------------------------------------|
|
||||
| Type | <code>month</code> |
|
||||
| Environment | <code>$CODER_AI_BUDGET_PERIOD</code> |
|
||||
| YAML | <code>aibridge.budget_period</code> |
|
||||
| Default | <code>month</code> |
|
||||
|
||||
Determines when accumulated AI spend resets to zero, aligned to UTC calendar boundaries. Only "month" is currently supported.
|
||||
|
||||
### --aibridge-proxy-enabled
|
||||
|
||||
| | |
|
||||
|
||||
@@ -174,6 +174,15 @@ AI BRIDGE OPTIONS:
|
||||
Emit structured logs for AI Bridge interception records. Use this for
|
||||
exporting these records to external SIEM or observability systems.
|
||||
|
||||
--ai-budget-period month, $CODER_AI_BUDGET_PERIOD (default: month)
|
||||
Determines when accumulated AI spend resets to zero, aligned to UTC
|
||||
calendar boundaries. Only "month" is currently supported.
|
||||
|
||||
--ai-budget-policy highest, $CODER_AI_BUDGET_POLICY (default: highest)
|
||||
Determines the effective group when a user belongs to multiple groups
|
||||
with AI budgets. "highest" selects the group with the largest spend
|
||||
limit, and is currently the only supported value.
|
||||
|
||||
AI BRIDGE PROXY OPTIONS:
|
||||
--aibridge-proxy-dump-dir string, $CODER_AIBRIDGE_PROXY_DUMP_DIR
|
||||
Directory for dumping MITM request/response pairs to disk for
|
||||
|
||||
Generated
+15
@@ -68,6 +68,11 @@ export interface AIBridgeConfig {
|
||||
readonly structured_logging: boolean;
|
||||
readonly send_actor_headers: boolean;
|
||||
readonly allow_byok: boolean;
|
||||
/**
|
||||
* Budget settings for AI Governance cost controls.
|
||||
*/
|
||||
readonly budget_policy?: string;
|
||||
readonly budget_period?: string;
|
||||
/**
|
||||
* Circuit breaker protects against cascading failures from upstream AI
|
||||
* provider overload (503, 529).
|
||||
@@ -305,6 +310,16 @@ export interface AIBridgeUserPrompt {
|
||||
readonly created_at: string;
|
||||
}
|
||||
|
||||
// From codersdk/deployment.go
|
||||
export type AIBudgetPeriod = "month";
|
||||
|
||||
export const AIBudgetPeriods: AIBudgetPeriod[] = ["month"];
|
||||
|
||||
export const AIBudgetPolicies: AIBudgetPolicy[] = ["highest"];
|
||||
|
||||
// From codersdk/deployment.go
|
||||
export type AIBudgetPolicy = "highest";
|
||||
|
||||
// From codersdk/deployment.go
|
||||
export interface AIConfig {
|
||||
readonly bridge?: AIBridgeConfig;
|
||||
|
||||
Reference in New Issue
Block a user