mirror of
https://github.com/coder/coder.git
synced 2026-10-28 02:42:54 +08:00
refactor(aibridge): clean up keypool and provider error handling (#25609)
## Description Cleans up how key pool errors are represented and how they get turned into HTTP responses. Consolidates two error types into a single type with a kind tag, and gives the response helpers in both providers consistent names. ## Changes - Replaced the keypool sentinel and transient error struct with one error type that carries a kind and a retry-after duration. - Updated `KeyFailoverConfig.BuildKeyPoolResponse` to take the typed key pool error, so each provider can shape the exhaustion response in its own format. - Removed the per-provider `MarkKey` callback from `KeyFailoverConfig` since providers can rely on the shared `MarkKeyOnStatus` helper. - Renamed the response-error helpers so OpenAI and Anthropic use the same naming. Related to: https://linear.app/codercom/issue/AIGOV-334/aibridge-follow-ups-from-key-failover-prs > [!NOTE] > Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -173,8 +172,8 @@ func (p *Anthropic) CreateInterceptor(_ http.ResponseWriter, r *http.Request, tr
|
||||
// Centralized: use the first key as a placeholder hint.
|
||||
// TODO(ssncferreira): record the actually-used key in
|
||||
// the interception record to reflect failover.
|
||||
if k, err := cfg.KeyPool.Walker().Next(); err == nil {
|
||||
credSecret = k.Value()
|
||||
if key, keyPoolErr := cfg.KeyPool.Walker().Next(); keyPoolErr == nil {
|
||||
credSecret = key.Value()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,20 +221,18 @@ func (p *Anthropic) InjectAuthHeader(headers *http.Header) {
|
||||
}
|
||||
|
||||
func (p *Anthropic) KeyFailoverConfig(logger slog.Logger) keypool.KeyFailoverConfig {
|
||||
name := p.Name()
|
||||
return keypool.KeyFailoverConfig{
|
||||
Pool: p.cfg.KeyPool,
|
||||
Pool: p.cfg.KeyPool,
|
||||
ProviderName: p.Name(),
|
||||
Logger: logger,
|
||||
IsBYOK: func(r *http.Request) bool {
|
||||
return r.Header.Get("X-Api-Key") != "" || r.Header.Get("Authorization") != ""
|
||||
},
|
||||
InjectAuthKey: func(h *http.Header, key string) {
|
||||
h.Set("X-Api-Key", key)
|
||||
},
|
||||
MarkKey: func(ctx context.Context, key *keypool.Key, resp *http.Response) bool {
|
||||
return keypool.MarkKeyOnStatus(ctx, key, resp, logger, name)
|
||||
},
|
||||
BuildExhaustedResponse: func(err error) *http.Response {
|
||||
return messages.ProcessKeyPoolError(err).ToResponse()
|
||||
BuildKeyPoolResponse: func(keyPoolErr *keypool.Error) *http.Response {
|
||||
return messages.ResponseErrorFromKeyPool(keyPoolErr).ToResponse()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -146,8 +145,8 @@ func (p *OpenAI) CreateInterceptor(_ http.ResponseWriter, r *http.Request, trace
|
||||
// Centralized: use the first key as a placeholder hint.
|
||||
// TODO(ssncferreira): record the actually-used key in
|
||||
// the interception record to reflect failover.
|
||||
if k, err := cfg.KeyPool.Walker().Next(); err == nil {
|
||||
credSecret = k.Value()
|
||||
if key, keyPoolErr := cfg.KeyPool.Walker().Next(); keyPoolErr == nil {
|
||||
credSecret = key.Value()
|
||||
}
|
||||
}
|
||||
cred := intercept.NewCredentialInfo(credKind, credSecret)
|
||||
@@ -221,20 +220,18 @@ func (p *OpenAI) InjectAuthHeader(headers *http.Header) {
|
||||
}
|
||||
|
||||
func (p *OpenAI) KeyFailoverConfig(logger slog.Logger) keypool.KeyFailoverConfig {
|
||||
name := p.Name()
|
||||
return keypool.KeyFailoverConfig{
|
||||
Pool: p.cfg.KeyPool,
|
||||
Pool: p.cfg.KeyPool,
|
||||
ProviderName: p.Name(),
|
||||
Logger: logger,
|
||||
IsBYOK: func(r *http.Request) bool {
|
||||
return r.Header.Get("Authorization") != ""
|
||||
},
|
||||
InjectAuthKey: func(h *http.Header, key string) {
|
||||
h.Set("Authorization", "Bearer "+key)
|
||||
},
|
||||
MarkKey: func(ctx context.Context, key *keypool.Key, resp *http.Response) bool {
|
||||
return keypool.MarkKeyOnStatus(ctx, key, resp, logger, name)
|
||||
},
|
||||
BuildExhaustedResponse: func(err error) *http.Response {
|
||||
return chatcompletions.ProcessKeyPoolError(err).ToResponse()
|
||||
BuildKeyPoolResponse: func(keyPoolErr *keypool.Error) *http.Response {
|
||||
return intercept.ResponseErrorFromKeyPool(keyPoolErr).ToResponse()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user