From 0d9718e217cdbf55238c22dfc5c996bc272f34f1 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Fri, 22 May 2026 16:10:37 +0200 Subject: [PATCH] feat: add 'copilot' to ai_provider_type (#25616) --- coderd/ai_providers_migrate.go | 28 +++++++++++++++---- coderd/ai_providers_migrate_test.go | 9 ++++-- coderd/apidoc/docs.go | 6 ++-- coderd/apidoc/swagger.json | 6 ++-- coderd/database/dump.sql | 3 +- ...06_ai_provider_type_copilot_value.down.sql | 2 ++ ...0506_ai_provider_type_copilot_value.up.sql | 5 ++++ coderd/database/models.go | 5 +++- codersdk/aiproviders.go | 4 +++ docs/reference/api/aiproviders.md | 6 ++-- docs/reference/api/schemas.md | 6 ++-- site/src/api/typesGenerated.ts | 2 ++ 12 files changed, 61 insertions(+), 21 deletions(-) create mode 100644 coderd/database/migrations/000506_ai_provider_type_copilot_value.down.sql create mode 100644 coderd/database/migrations/000506_ai_provider_type_copilot_value.up.sql diff --git a/coderd/ai_providers_migrate.go b/coderd/ai_providers_migrate.go index aedd8855e7..c54cd12e28 100644 --- a/coderd/ai_providers_migrate.go +++ b/coderd/ai_providers_migrate.go @@ -327,9 +327,9 @@ func providersFromEnv(ctx context.Context, cfg codersdk.AIBridgeConfig, logger s dp.Type = database.AiProviderTypeOpenai case aibridge.ProviderAnthropic: dp.Type = database.AiProviderTypeAnthropic + case aibridge.ProviderCopilot: + dp.Type = database.AiProviderTypeCopilot default: - // Skip other types (e.g. copilot) until they are added - // to the database enum. logger.Warn(ctx, "skipping indexed AI provider with unsupported type", slog.F("name", name), slog.F("type", p.Type), @@ -367,11 +367,27 @@ func providersFromEnv(ctx context.Context, cfg codersdk.AIBridgeConfig, logger s dp.BaseURL = p.BedrockBaseURL } } - // Non-Bedrock providers carry their bearer keys in + // Non-Bedrock, non-Copilot providers carry their bearer keys in // ai_provider_keys. Bedrock providers authenticate via the - // settings blob and have no keys; cli/server.go rejects - // configs that set both before we get here. - if !isBedrock { + // settings blob; Copilot providers use request-time GitHub + // OAuth tokens. cli/server.go rejects configs that set Bedrock + // alongside bearer keys before we get here. + switch { + case isBedrock: + if len(p.Keys) > 0 { + logger.Warn(ctx, "ignoring bearer keys configured on Bedrock AI provider; Bedrock authenticates via access keys or credential chain", + slog.F("name", name), + slog.F("ignored_key_count", len(p.Keys)), + ) + } + case dp.Type == database.AiProviderTypeCopilot: + if len(p.Keys) > 0 { + logger.Warn(ctx, "ignoring bearer keys configured on Copilot AI provider; Copilot authenticates via request-time GitHub OAuth tokens", + slog.F("name", name), + slog.F("ignored_key_count", len(p.Keys)), + ) + } + default: dp.Keys = append(dp.Keys, p.Keys...) } diff --git a/coderd/ai_providers_migrate_test.go b/coderd/ai_providers_migrate_test.go index a989d126dc..87f5dd0764 100644 --- a/coderd/ai_providers_migrate_test.go +++ b/coderd/ai_providers_migrate_test.go @@ -371,12 +371,15 @@ func TestSeedAIProvidersFromEnv(t *testing.T) { db, _ := dbtestutil.NewDB(t) ctx := testutil.Context(t, testutil.WaitShort) + // vercel is a valid ai_provider_type DB value but the aibridge + // runtime has no constructor for it, so the seed switch falls + // into the default branch and skips the row. cfg := codersdk.AIBridgeConfig{ Providers: []codersdk.AIProviderConfig{ { - Type: "copilot", - Name: "gh-copilot", - BaseURL: "https://api.githubcopilot.com/", + Type: "vercel", + Name: "vercel-instance", + BaseURL: "https://example.com", }, { Type: "openai", diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index d7e4b6e2b8..7b85bd3323 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -15117,7 +15117,8 @@ const docTemplate = `{ "openai-compat", "openrouter", "vercel", - "bedrock" + "bedrock", + "copilot" ], "x-enum-varnames": [ "AIProviderTypeOpenAI", @@ -15127,7 +15128,8 @@ const docTemplate = `{ "AIProviderTypeOpenAICompat", "AIProviderTypeOpenrouter", "AIProviderTypeVercel", - "AIProviderTypeBedrock" + "AIProviderTypeBedrock", + "AIProviderTypeCopilot" ] }, "codersdk.APIAllowListTarget": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index d28f8bb476..0bff17f6dc 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -13521,7 +13521,8 @@ "openai-compat", "openrouter", "vercel", - "bedrock" + "bedrock", + "copilot" ], "x-enum-varnames": [ "AIProviderTypeOpenAI", @@ -13531,7 +13532,8 @@ "AIProviderTypeOpenAICompat", "AIProviderTypeOpenrouter", "AIProviderTypeVercel", - "AIProviderTypeBedrock" + "AIProviderTypeBedrock", + "AIProviderTypeCopilot" ] }, "codersdk.APIAllowListTarget": { diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 2c0292ab60..20a1b78a77 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -18,7 +18,8 @@ CREATE TYPE ai_provider_type AS ENUM ( 'google', 'openai-compat', 'openrouter', - 'vercel' + 'vercel', + 'copilot' ); CREATE TYPE ai_seat_usage_reason AS ENUM ( diff --git a/coderd/database/migrations/000506_ai_provider_type_copilot_value.down.sql b/coderd/database/migrations/000506_ai_provider_type_copilot_value.down.sql new file mode 100644 index 0000000000..100307bb3d --- /dev/null +++ b/coderd/database/migrations/000506_ai_provider_type_copilot_value.down.sql @@ -0,0 +1,2 @@ +-- No-op: Postgres does not allow removing enum values safely. +-- Matches the precedent in 000499_ai_provider_type_chatd_values.down.sql. diff --git a/coderd/database/migrations/000506_ai_provider_type_copilot_value.up.sql b/coderd/database/migrations/000506_ai_provider_type_copilot_value.up.sql new file mode 100644 index 0000000000..98de2ffe00 --- /dev/null +++ b/coderd/database/migrations/000506_ai_provider_type_copilot_value.up.sql @@ -0,0 +1,5 @@ +-- Add 'copilot' to ai_provider_type. The aibridge runtime already supports +-- Copilot via aibridge.NewCopilotProvider; the enum just needs the +-- discriminator so DB-driven providers can carry it. Mirrors the precedent +-- in 000499_ai_provider_type_chatd_values.up.sql. +ALTER TYPE ai_provider_type ADD VALUE IF NOT EXISTS 'copilot'; diff --git a/coderd/database/models.go b/coderd/database/models.go index 080e9ae027..3eacf45aa7 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -27,6 +27,7 @@ const ( AiProviderTypeOpenaiCompat AIProviderType = "openai-compat" AiProviderTypeOpenrouter AIProviderType = "openrouter" AiProviderTypeVercel AIProviderType = "vercel" + AiProviderTypeCopilot AIProviderType = "copilot" ) func (e *AIProviderType) Scan(src interface{}) error { @@ -73,7 +74,8 @@ func (e AIProviderType) Valid() bool { AiProviderTypeGoogle, AiProviderTypeOpenaiCompat, AiProviderTypeOpenrouter, - AiProviderTypeVercel: + AiProviderTypeVercel, + AiProviderTypeCopilot: return true } return false @@ -89,6 +91,7 @@ func AllAIProviderTypeValues() []AIProviderType { AiProviderTypeOpenaiCompat, AiProviderTypeOpenrouter, AiProviderTypeVercel, + AiProviderTypeCopilot, } } diff --git a/codersdk/aiproviders.go b/codersdk/aiproviders.go index 658163f213..3b47598118 100644 --- a/codersdk/aiproviders.go +++ b/codersdk/aiproviders.go @@ -41,6 +41,10 @@ const ( // using the Bedrock discriminator in Settings; native support is // future work. AIProviderTypeBedrock AIProviderType = "bedrock" + // AIProviderTypeCopilot routes through aibridge's Copilot client, + // which uses request-time GitHub OAuth tokens rather than pre-shared + // API keys. + AIProviderTypeCopilot AIProviderType = "copilot" ) // AIProviderSettings is the discriminated container for type-specific diff --git a/docs/reference/api/aiproviders.md b/docs/reference/api/aiproviders.md index 5adc51d4cf..51a18ddd44 100644 --- a/docs/reference/api/aiproviders.md +++ b/docs/reference/api/aiproviders.md @@ -69,9 +69,9 @@ Status Code **200** #### Enumerated Values -| Property | Value(s) | -|----------|----------------------------------------------------------------------------------------------| -| `type` | `anthropic`, `azure`, `bedrock`, `google`, `openai`, `openai-compat`, `openrouter`, `vercel` | +| Property | Value(s) | +|----------|---------------------------------------------------------------------------------------------------------| +| `type` | `anthropic`, `azure`, `bedrock`, `copilot`, `google`, `openai`, `openai-compat`, `openrouter`, `vercel` | To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 9744e9bf87..c2cffd13d4 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -1416,9 +1416,9 @@ None #### Enumerated Values -| Value(s) | -|----------------------------------------------------------------------------------------------| -| `anthropic`, `azure`, `bedrock`, `google`, `openai`, `openai-compat`, `openrouter`, `vercel` | +| Value(s) | +|---------------------------------------------------------------------------------------------------------| +| `anthropic`, `azure`, `bedrock`, `copilot`, `google`, `openai`, `openai-compat`, `openrouter`, `vercel` | ## codersdk.APIAllowListTarget diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 566a44f4af..af46758f9f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -463,6 +463,7 @@ export type AIProviderType = | "anthropic" | "azure" | "bedrock" + | "copilot" | "google" | "openai" | "openai-compat" @@ -473,6 +474,7 @@ export const AIProviderTypes: AIProviderType[] = [ "anthropic", "azure", "bedrock", + "copilot", "google", "openai", "openai-compat",