mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -17,6 +17,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"github.com/coder/coder/v2/coderd/aibridge"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
coderdpubsub "github.com/coder/coder/v2/coderd/pubsub"
|
||||
@@ -908,6 +909,14 @@ func (p *Server) resolveExploreToolSnapshot(
|
||||
return inheritedMCPServerIDs, nil
|
||||
}
|
||||
|
||||
func (p *Server) delegatedAPIKeyIDForSubagent(ctx context.Context) (string, error) {
|
||||
apiKeyID, ok := aibridge.DelegatedAPIKeyIDFromContext(ctx)
|
||||
if !ok && p.shouldUseAIGatewayRouting() {
|
||||
return "", xerrors.New("AI Gateway routing requires the active turn API key ID for subagent messages")
|
||||
}
|
||||
return apiKeyID, nil
|
||||
}
|
||||
|
||||
func (p *Server) createChildSubagentChat(
|
||||
ctx context.Context,
|
||||
parent database.Chat,
|
||||
@@ -950,6 +959,10 @@ func (p *Server) createChildSubagentChatWithOptions(
|
||||
if modelConfigID == uuid.Nil {
|
||||
return database.Chat{}, xerrors.New("model config is required")
|
||||
}
|
||||
childAPIKeyID, err := p.delegatedAPIKeyIDForSubagent(ctx)
|
||||
if err != nil {
|
||||
return database.Chat{}, err
|
||||
}
|
||||
|
||||
childPlanMode := parent.PlanMode
|
||||
if opts.planModeOverride != nil {
|
||||
@@ -1086,7 +1099,7 @@ func (p *Server) createChildSubagentChatWithOptions(
|
||||
database.ChatMessageVisibilityBoth,
|
||||
modelConfigID,
|
||||
chatprompt.CurrentContentVersion,
|
||||
).withCreatedBy(parent.OwnerID))
|
||||
).withCreatedBy(parent.OwnerID).withAPIKeyID(childAPIKeyID))
|
||||
if _, err := tx.InsertChatMessages(ctx, userParams); err != nil {
|
||||
return xerrors.Errorf("insert initial child user message: %w", err)
|
||||
}
|
||||
@@ -1236,10 +1249,16 @@ func (p *Server) sendSubagentMessage(
|
||||
return database.Chat{}, xerrors.Errorf("get target chat: %w", err)
|
||||
}
|
||||
|
||||
apiKeyID, err := p.delegatedAPIKeyIDForSubagent(ctx)
|
||||
if err != nil {
|
||||
return database.Chat{}, err
|
||||
}
|
||||
|
||||
sendResult, err := p.SendMessage(ctx, SendMessageOptions{
|
||||
ChatID: targetChatID,
|
||||
CreatedBy: targetChat.OwnerID,
|
||||
Content: []codersdk.ChatMessagePart{codersdk.ChatMessageText(message)},
|
||||
APIKeyID: apiKeyID,
|
||||
BusyBehavior: busyBehavior,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user