diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index 2d28ada458..40666c10e8 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -713,3 +713,20 @@ workspace_prebuilds: # limit; disabled when set to zero. # (default: 3, type: int) failure_hard_limit: 3 +aibridge: + # Whether to start an in-memory aibridged instance ("aibridge" experiment must be + # enabled, too). + # (default: false, type: bool) + enabled: false + # The base URL of the OpenAI API. + # (default: https://api.openai.com/v1/, type: string) + openai_base_url: https://api.openai.com/v1/ + # The key to authenticate against the OpenAI API. + # (default: , type: string) + openai_key: "" + # The base URL of the Anthropic API. + # (default: https://api.anthropic.com/, type: string) + base_url: https://api.anthropic.com/ + # The key to authenticate against the Anthropic API. + # (default: , type: string) + key: "" diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 0182c92c2f..2f45203b6b 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -11175,6 +11175,50 @@ const docTemplate = `{ } } }, + "codersdk.AIBridgeAnthropicConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIBridgeConfig": { + "type": "object", + "properties": { + "anthropic": { + "$ref": "#/definitions/codersdk.AIBridgeAnthropicConfig" + }, + "enabled": { + "type": "boolean" + }, + "openai": { + "$ref": "#/definitions/codersdk.AIBridgeOpenAIConfig" + } + } + }, + "codersdk.AIBridgeOpenAIConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIConfig": { + "type": "object", + "properties": { + "bridge": { + "$ref": "#/definitions/codersdk.AIBridgeConfig" + } + } + }, "codersdk.APIKey": { "type": "object", "required": [ @@ -12811,6 +12855,9 @@ const docTemplate = `{ "agent_stat_refresh_interval": { "type": "integer" }, + "ai": { + "$ref": "#/definitions/codersdk.AIConfig" + }, "allow_workspace_renames": { "type": "boolean" }, @@ -13138,9 +13185,11 @@ const docTemplate = `{ "web-push", "oauth2", "mcp-server-http", - "workspace-sharing" + "workspace-sharing", + "aibridge" ], "x-enum-comments": { + "ExperimentAIBridge": "Enables AI Bridge 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.", @@ -13158,7 +13207,8 @@ const docTemplate = `{ "ExperimentWebPush", "ExperimentOAuth2", "ExperimentMCPServerHTTP", - "ExperimentWorkspaceSharing" + "ExperimentWorkspaceSharing", + "ExperimentAIBridge" ] }, "codersdk.ExternalAgentCredentials": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index af2e2d43e2..b94b2307d3 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9909,6 +9909,50 @@ } } }, + "codersdk.AIBridgeAnthropicConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIBridgeConfig": { + "type": "object", + "properties": { + "anthropic": { + "$ref": "#/definitions/codersdk.AIBridgeAnthropicConfig" + }, + "enabled": { + "type": "boolean" + }, + "openai": { + "$ref": "#/definitions/codersdk.AIBridgeOpenAIConfig" + } + } + }, + "codersdk.AIBridgeOpenAIConfig": { + "type": "object", + "properties": { + "base_url": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "codersdk.AIConfig": { + "type": "object", + "properties": { + "bridge": { + "$ref": "#/definitions/codersdk.AIBridgeConfig" + } + } + }, "codersdk.APIKey": { "type": "object", "required": [ @@ -11454,6 +11498,9 @@ "agent_stat_refresh_interval": { "type": "integer" }, + "ai": { + "$ref": "#/definitions/codersdk.AIConfig" + }, "allow_workspace_renames": { "type": "boolean" }, @@ -11774,9 +11821,11 @@ "web-push", "oauth2", "mcp-server-http", - "workspace-sharing" + "workspace-sharing", + "aibridge" ], "x-enum-comments": { + "ExperimentAIBridge": "Enables AI Bridge 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.", @@ -11794,7 +11843,8 @@ "ExperimentWebPush", "ExperimentOAuth2", "ExperimentMCPServerHTTP", - "ExperimentWorkspaceSharing" + "ExperimentWorkspaceSharing", + "ExperimentAIBridge" ] }, "codersdk.ExternalAgentCredentials": { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 2284e0266e..c86297307f 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -500,6 +500,7 @@ type DeploymentValues struct { WorkspaceHostnameSuffix serpent.String `json:"workspace_hostname_suffix,omitempty" typescript:",notnull"` Prebuilds PrebuildsConfig `json:"workspace_prebuilds,omitempty" typescript:",notnull"` HideAITasks serpent.Bool `json:"hide_ai_tasks,omitempty" typescript:",notnull"` + AI AIConfig `json:"ai,omitempty"` Config serpent.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"` WriteConfig serpent.Bool `json:"write_config,omitempty" typescript:",notnull"` @@ -1163,6 +1164,10 @@ func (c *DeploymentValues) Options() serpent.OptionSet { Parent: &deploymentGroupNotifications, YAML: "inbox", } + deploymentGroupAIBridge = serpent.Group{ + Name: "AIBridge", + YAML: "aibridge", + } ) httpAddress := serpent.Option{ @@ -3223,11 +3228,88 @@ Write out the current server config as YAML to stdout.`, Group: &deploymentGroupClient, YAML: "hideAITasks", }, + + // AIBridge Options + { + Name: "AIBridge Enabled", + Description: fmt.Sprintf("Whether to start an in-memory aibridged instance (%q experiment must be enabled, too).", ExperimentAIBridge), + Flag: "aibridge-enabled", + Env: "CODER_AIBRIDGE_ENABLED", + Value: &c.AI.BridgeConfig.Enabled, + Default: "false", + Group: &deploymentGroupAIBridge, + YAML: "enabled", + Hidden: true, + }, + { + Name: "AIBridge OpenAI Base URL", + Description: "The base URL of the OpenAI API.", + Flag: "aibridge-openai-base-url", + Env: "CODER_AIBRIDGE_OPENAI_BASE_URL", + Value: &c.AI.BridgeConfig.OpenAI.BaseURL, + Default: "https://api.openai.com/v1/", + Group: &deploymentGroupAIBridge, + YAML: "openai_base_url", + Hidden: true, + }, + { + Name: "AIBridge OpenAI Key", + Description: "The key to authenticate against the OpenAI API.", + Flag: "aibridge-openai-key", + Env: "CODER_AIBRIDGE_OPENAI_KEY", + Value: &c.AI.BridgeConfig.OpenAI.Key, + Default: "", + Group: &deploymentGroupAIBridge, + YAML: "openai_key", + Hidden: true, + }, + { + Name: "AIBridge Anthropic Base URL", + Description: "The base URL of the Anthropic API.", + Flag: "aibridge-anthropic-base-url", + Env: "CODER_AIBRIDGE_ANTHROPIC_BASE_URL", + Value: &c.AI.BridgeConfig.Anthropic.BaseURL, + Default: "https://api.anthropic.com/", + Group: &deploymentGroupAIBridge, + YAML: "base_url", + Hidden: true, + }, + { + Name: "AIBridge Anthropic KEY", + Description: "The key to authenticate against the Anthropic API.", + Flag: "aibridge-anthropic-key", + Env: "CODER_AIBRIDGE_ANTHROPIC_KEY", + Value: &c.AI.BridgeConfig.Anthropic.Key, + Default: "", + Group: &deploymentGroupAIBridge, + YAML: "key", + Hidden: true, + }, } return opts } +type AIBridgeConfig struct { + Enabled serpent.Bool `json:"enabled" typescript:",notnull"` + OpenAI AIBridgeOpenAIConfig `json:"openai" typescript:",notnull"` + Anthropic AIBridgeAnthropicConfig `json:"anthropic" typescript:",notnull"` +} + +type AIBridgeOpenAIConfig struct { + BaseURL serpent.String `json:"base_url" typescript:",notnull"` + Key serpent.String `json:"key" typescript:",notnull"` +} + +type AIBridgeAnthropicConfig struct { + BaseURL serpent.String `json:"base_url" typescript:",notnull"` + Key serpent.String `json:"key" typescript:",notnull"` +} + +type AIConfig struct { + BridgeConfig AIBridgeConfig `json:"bridge,omitempty"` +} + type SupportConfig struct { Links serpent.Struct[[]LinkConfig] `json:"links" typescript:",notnull"` } @@ -3475,6 +3557,7 @@ const ( ExperimentOAuth2 Experiment = "oauth2" // Enables OAuth2 provider functionality. ExperimentMCPServerHTTP Experiment = "mcp-server-http" // Enables the MCP HTTP server functionality. ExperimentWorkspaceSharing Experiment = "workspace-sharing" // Enables updating workspace ACLs for sharing with users and groups. + ExperimentAIBridge Experiment = "aibridge" // Enables AI Bridge functionality. ) func (e Experiment) DisplayName() string { @@ -3495,6 +3578,8 @@ func (e Experiment) DisplayName() string { return "MCP HTTP Server Functionality" case ExperimentWorkspaceSharing: return "Workspace Sharing" + case ExperimentAIBridge: + return "AI Bridge" default: // Split on hyphen and convert to title case // e.g. "web-push" -> "Web Push", "mcp-server-http" -> "Mcp Server Http" @@ -3513,6 +3598,7 @@ var ExperimentsKnown = Experiments{ ExperimentOAuth2, ExperimentMCPServerHTTP, ExperimentWorkspaceSharing, + ExperimentAIBridge, } // ExperimentsSafe should include all experiments that are safe for diff --git a/docs/reference/api/general.md b/docs/reference/api/general.md index 569ef067d8..925e74a022 100644 --- a/docs/reference/api/general.md +++ b/docs/reference/api/general.md @@ -161,6 +161,19 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "user": {} }, "agent_stat_refresh_interval": 0, + "ai": { + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } + }, "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index df8aac2115..f8e0ea267f 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -335,6 +335,86 @@ | `groups` | array of [codersdk.Group](#codersdkgroup) | false | | | | `users` | array of [codersdk.ReducedUser](#codersdkreduceduser) | false | | | +## codersdk.AIBridgeAnthropicConfig + +```json +{ + "base_url": "string", + "key": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------|--------|----------|--------------|-------------| +| `base_url` | string | false | | | +| `key` | string | false | | | + +## codersdk.AIBridgeConfig + +```json +{ + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------|----------------------------------------------------------------------|----------|--------------|-------------| +| `anthropic` | [codersdk.AIBridgeAnthropicConfig](#codersdkaibridgeanthropicconfig) | false | | | +| `enabled` | boolean | false | | | +| `openai` | [codersdk.AIBridgeOpenAIConfig](#codersdkaibridgeopenaiconfig) | false | | | + +## codersdk.AIBridgeOpenAIConfig + +```json +{ + "base_url": "string", + "key": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|------------|--------|----------|--------------|-------------| +| `base_url` | string | false | | | +| `key` | string | false | | | + +## codersdk.AIConfig + +```json +{ + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|----------|----------------------------------------------------|----------|--------------|-------------| +| `bridge` | [codersdk.AIBridgeConfig](#codersdkaibridgeconfig) | false | | | + ## codersdk.APIKey ```json @@ -2185,6 +2265,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "user": {} }, "agent_stat_refresh_interval": 0, + "ai": { + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } + }, "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, @@ -2673,6 +2766,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "user": {} }, "agent_stat_refresh_interval": 0, + "ai": { + "bridge": { + "anthropic": { + "base_url": "string", + "key": "string" + }, + "enabled": true, + "openai": { + "base_url": "string", + "key": "string" + } + } + }, "allow_workspace_renames": true, "autobuild_poll_interval": 0, "browser_only": true, @@ -3052,6 +3158,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `address` | [serpent.HostPort](#serpenthostport) | false | | Deprecated: Use HTTPAddress or TLS.Address instead. | | `agent_fallback_troubleshooting_url` | [serpent.URL](#serpenturl) | false | | | | `agent_stat_refresh_interval` | integer | false | | | +| `ai` | [codersdk.AIConfig](#codersdkaiconfig) | false | | | | `allow_workspace_renames` | boolean | false | | | | `autobuild_poll_interval` | integer | false | | | | `browser_only` | boolean | false | | | @@ -3356,6 +3463,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `oauth2` | | `mcp-server-http` | | `workspace-sharing` | +| `aibridge` | ## codersdk.ExternalAgentCredentials diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 8943e69289..9f10630ab9 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -6,6 +6,30 @@ export interface ACLAvailable { readonly groups: readonly Group[]; } +// From codersdk/deployment.go +export interface AIBridgeAnthropicConfig { + readonly base_url: string; + readonly key: string; +} + +// From codersdk/deployment.go +export interface AIBridgeConfig { + readonly enabled: boolean; + readonly openai: AIBridgeOpenAIConfig; + readonly anthropic: AIBridgeAnthropicConfig; +} + +// From codersdk/deployment.go +export interface AIBridgeOpenAIConfig { + readonly base_url: string; + readonly key: string; +} + +// From codersdk/deployment.go +export interface AIConfig { + readonly bridge?: AIBridgeConfig; +} + // From codersdk/aitasks.go export const AITaskPromptParameterName = "AI Prompt"; @@ -839,6 +863,7 @@ export interface DeploymentValues { readonly workspace_hostname_suffix?: string; readonly workspace_prebuilds?: PrebuildsConfig; readonly hide_ai_tasks?: boolean; + readonly ai?: AIConfig; readonly config?: string; readonly write_config?: boolean; readonly address?: string; @@ -930,6 +955,7 @@ export const EntitlementsWarningHeader = "X-Coder-Entitlements-Warning"; // From codersdk/deployment.go export type Experiment = + | "aibridge" | "auto-fill-parameters" | "example" | "mcp-server-http" @@ -940,6 +966,7 @@ export type Experiment = | "workspace-usage"; export const Experiments: Experiment[] = [ + "aibridge", "auto-fill-parameters", "example", "mcp-server-http",