Skip to content

feat: add automatic key failover for AI Bridge passthrough#24920

Merged
ssncferreira merged 3 commits into
mainfrom
ssncf/aibridge-passthrough-key-failover
May 7, 2026
Merged

feat: add automatic key failover for AI Bridge passthrough#24920
ssncferreira merged 3 commits into
mainfrom
ssncf/aibridge-passthrough-key-failover

Conversation

@ssncferreira
Copy link
Copy Markdown
Contributor

@ssncferreira ssncferreira commented May 4, 2026

Description

Adds automatic key failover for passthrough routes for the Anthropic and OpenAI providers. A new keyFailoverTransport wraps the reverse-proxy transport: centralized requests walk the configured key pool and retry with the next key on key-specific failures (401/403/429), reusing the same key-marking semantics as the bridged routes.

BYOK passthrough requests run as a single attempt with no failover.

Changes

  • New keypool.KeyFailoverConfig carrying the Pool to walk and the provider-specific closures (IsBYOK, InjectAuthKey, MarkKey, BuildExhaustedResponse).
  • New keypool.NewKeyFailoverTransport: wraps an inner http.RoundTripper. Returns inner unchanged when Pool is nil, otherwise produces a transport that buffers the request body once, walks the pool per request, and replays each attempt with the next key.
  • New Provider.KeyFailoverConfig(logger) interface method. Anthropic injects X-Api-Key; OpenAI injects Authorization: Bearer ...; Copilot returns an empty config.
  • passthrough.go wires NewKeyFailoverTransport around the existing apidump middleware, so every retry attempt is recorded.

Related Issues

Related to: coder/internal#1446
Related to: https://linear.app/codercom/issue/AIGOV-197/aibridge-automatic-key-failover-for-bridged-and-passthrough-routes

Follow-up PRs

  • Remove dead Provider.InjectAuthHeader method now that all auth is applied per-attempt by KeyFailoverTransport.
  • Bedrock multi-key support.
  • Refactor provider vs interceptor config separation.
  • Record the actually-used key in the interception credential hint after failover.

Note

Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira

Copy link
Copy Markdown
Contributor Author

ssncferreira commented May 4, 2026

@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from b282407 to ae98c2d Compare May 4, 2026 08:55
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from 56a2653 to f0af485 Compare May 4, 2026 09:18
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from ae98c2d to 541a917 Compare May 4, 2026 09:18
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch from f0af485 to aec65b9 Compare May 4, 2026 09:36
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from 541a917 to 337ee29 Compare May 4, 2026 09:36
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch from aec65b9 to 3c4acae Compare May 4, 2026 09:59
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch 2 times, most recently from 2f7c02d to c38a2a8 Compare May 4, 2026 10:11
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from c5042db to 95dd77d Compare May 4, 2026 10:59
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from c38a2a8 to 8ac3606 Compare May 4, 2026 11:01
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from 7091a0f to 4ccc07d Compare May 4, 2026 11:14
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch 2 times, most recently from 38ed74c to 866efb9 Compare May 4, 2026 14:52
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch from 4ccc07d to 8fe651e Compare May 4, 2026 14:52
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from 866efb9 to 3ddd9a2 Compare May 4, 2026 15:35
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch from 8fe651e to 894d769 Compare May 4, 2026 15:35
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from 3ddd9a2 to 8590bdf Compare May 4, 2026 16:03
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from 3657928 to 05a86ef Compare May 4, 2026 16:08
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch 2 times, most recently from 6f3b4f4 to 02cc359 Compare May 4, 2026 17:37
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from 926ca53 to 2fe5298 Compare May 4, 2026 18:35
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from a432358 to d143231 Compare May 5, 2026 17:51
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from 3e888ea to 3b185bb Compare May 5, 2026 17:56
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from d143231 to 4c78db8 Compare May 5, 2026 17:56
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch from 3b185bb to dc0fd49 Compare May 5, 2026 18:13
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch 2 times, most recently from 64844ef to 99452e8 Compare May 5, 2026 18:41
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from 5442499 to 88e1d65 Compare May 5, 2026 18:52
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch 2 times, most recently from 2dc9986 to 554ca68 Compare May 5, 2026 19:06
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 2 times, most recently from a035e39 to 4d17599 Compare May 5, 2026 19:22
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch from 554ca68 to a6072ef Compare May 5, 2026 19:22
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 3 times, most recently from 64f24d6 to f621fe5 Compare May 7, 2026 07:51
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-openai-key-failover branch 2 times, most recently from db0c7d2 to 36154dd Compare May 7, 2026 10:37
@ssncferreira ssncferreira force-pushed the ssncf/aibridge-passthrough-key-failover branch 3 times, most recently from 52acd55 to 8b61f0c Compare May 7, 2026 11:17
Comment thread aibridge/intercept/responses/base.go
Comment thread aibridge/intercept/chatcompletions/base.go
Comment thread aibridge/internal/testutil/mockprovider.go

// KeyFailoverConfig is the per-provider configuration consumed by
// NewKeyFailoverTransport.
type KeyFailoverConfig struct {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is this Config? Looks maybe like KeyManager or something?

Comment thread aibridge/keypool/failover.go
Comment thread aibridge/keypool/failover.go

// Mock upstream: counts requests and returns
// scripted responses keyed by API key. An unmapped
// key falls through to 500 so misconfigured cases
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe directly fail test in that case?

Copy link
Copy Markdown
Contributor Author

ssncferreira commented May 7, 2026

Merge activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants