feat: route chatd provider traffic through aibridge (#25629)

## Summary

Routes chatd model calls backed by concrete AI Provider rows through the
in-process aibridge transport by default, with deployment options to use
direct provider routing when AI Gateway is disabled or chat AI Gateway
routing is disabled.

- Splits model routing into common, direct provider, and AI Gateway
paths behind a single deployment-mode entry point.
- Builds chatd models through explicit request, route, and options data.
Active API key attribution is passed explicitly instead of being hidden
inside generic model construction.
- For AI Gateway BYOK routes, resolves the user's provider key in chatd,
forwards it through provider-specific auth headers, and sets
`X-Coder-AI-Governance-Token` to the `delegated` marker so aibridge
preserves those headers while still stripping Coder-specific metadata.
- Keeps central provider credentials and deployment fallback credentials
out of forwarded provider auth headers, so AI Gateway central policy
remains authoritative.
- Redacts delegated provider auth from default string formatting to
avoid accidental plaintext logging of user BYOK credentials.
- Covers selected chat models, advisor overrides, title and quickgen
paths, subagent overrides, computer use model selection, and an
integration-style chat turn through the aibridge transport path.
- Persists initiating API key IDs on chat and queued user messages,
including subagent child messages, and fails closed for AI
Gateway-routed model builds without an active key.
- Removes unused `api_key_id` indexes while keeping the persistence
columns and foreign keys.
- Keeps the deployment option available through config and env parsing,
but hides it from CLI help and generated docs.
- Stabilizes the subagent poll fallback test so background CreateChat
processing cannot win the state transition under slower CI environments.

## Tests

- `go test ./coderd/x/chatd -run
'TestAIGatewayProviderAuthForUser|TestAIGatewayProviderAuthRedactsFormatting|TestResolveModelRouteForConfigAIGatewayProviderAuth|TestAIGatewayModelForwardsProviderAuth|TestProcessChat_AIGatewayRoutingUsesDelegatedAPIKey|TestAwaitSubagentCompletion'
-count=1`
- `go test ./coderd/aibridged -run
'TestServeHTTP_DelegatedAPIKey|TestServeHTTP_StripCoderToken' -count=1`
- `git diff --check HEAD~1..HEAD`
- `make lint`

> Mux working on behalf of Mike.
This commit is contained in:
Michael Suchacz
2026-05-26 19:31:52 +00:00
committed by GitHub
parent a56c88a0cc
commit 8b1705eb65
31 changed files with 2463 additions and 377 deletions
+25
View File
@@ -77,6 +77,9 @@ func TestChatStreamRelay(t *testing.T) {
Options: &coderdtest.Options{
Database: db,
Pubsub: pubsub,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
@@ -89,6 +92,9 @@ func TestChatStreamRelay(t *testing.T) {
Options: &coderdtest.Options{
Database: db,
Pubsub: pubsub,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
DontAddLicense: true,
DontAddFirstUser: true,
@@ -219,6 +225,9 @@ func TestChatStreamRelay(t *testing.T) {
Database: db,
Pubsub: pubsub,
TLSCertificates: certificates,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
@@ -232,6 +241,9 @@ func TestChatStreamRelay(t *testing.T) {
Database: db,
Pubsub: pubsub,
TLSCertificates: certificates,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
DontAddLicense: true,
DontAddFirstUser: true,
@@ -398,6 +410,9 @@ func TestChatStreamRelay(t *testing.T) {
Options: &coderdtest.Options{
Database: db,
Pubsub: pubsub,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
@@ -410,6 +425,9 @@ func TestChatStreamRelay(t *testing.T) {
Options: &coderdtest.Options{
Database: db,
Pubsub: pubsub,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
DontAddLicense: true,
DontAddFirstUser: true,
@@ -544,6 +562,7 @@ func TestChatStreamRelay(t *testing.T) {
db, pubsub := dbtestutil.NewDB(t)
hostPrefixValues := coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
dv.HTTPCookies.EnableHostPrefix = true
dv.HTTPCookies.Secure = true
})
@@ -696,6 +715,9 @@ func TestChatStreamRelay(t *testing.T) {
Options: &coderdtest.Options{
Database: db,
Pubsub: pubsub,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
@@ -708,6 +730,9 @@ func TestChatStreamRelay(t *testing.T) {
Options: &coderdtest.Options{
Database: db,
Pubsub: pubsub,
DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.Chat.AIGatewayRoutingEnabled.Set("false"))
}),
},
DontAddLicense: true,
DontAddFirstUser: true,