fix(provider): normalize anthropic model ids for API key path

This commit is contained in:
李龙 0668001470 2026-03-16 11:47:43 +08:00
parent 4a8a2e9c23
commit 86eb07752d
2 changed files with 62 additions and 3 deletions

View file

@ -428,12 +428,20 @@ func serializeMessages(messages []Message) []any {
}
func normalizeModel(model, apiBase string) string {
before, after, ok := strings.Cut(model, "/")
if !ok {
lowerBase := strings.ToLower(apiBase)
if strings.Contains(lowerBase, "openrouter.ai") {
return model
}
if strings.Contains(strings.ToLower(apiBase), "openrouter.ai") {
if strings.Contains(lowerBase, "api.anthropic.com") {
if _, after, ok := strings.Cut(model, "/"); ok {
model = after
}
return strings.ReplaceAll(model, ".", "-")
}
before, after, ok := strings.Cut(model, "/")
if !ok {
return model
}

View file

@ -514,6 +514,12 @@ func TestNormalizeModel_UsesAPIBase(t *testing.T) {
if got := normalizeModel("deepseek/deepseek-chat", "https://api.deepseek.com/v1"); got != "deepseek-chat" {
t.Fatalf("normalizeModel(deepseek) = %q, want %q", got, "deepseek-chat")
}
if got := normalizeModel("claude-sonnet-4.6", "https://api.anthropic.com/v1"); got != "claude-sonnet-4-6" {
t.Fatalf("normalizeModel(anthropic plain) = %q, want %q", got, "claude-sonnet-4-6")
}
if got := normalizeModel("anthropic/claude-sonnet-4.6", "https://api.anthropic.com/v1"); got != "claude-sonnet-4-6" {
t.Fatalf("normalizeModel(anthropic prefixed) = %q, want %q", got, "claude-sonnet-4-6")
}
if got := normalizeModel("openrouter/auto", "https://openrouter.ai/api/v1"); got != "openrouter/auto" {
t.Fatalf("normalizeModel(openrouter) = %q, want %q", got, "openrouter/auto")
}
@ -525,6 +531,51 @@ func TestNormalizeModel_UsesAPIBase(t *testing.T) {
}
}
func TestProviderChat_NormalizesAnthropicModelForAnthropicAPIBase(t *testing.T) {
var requestBody map[string]any
p := NewProvider("key", "https://api.anthropic.com/v1", "")
p.httpClient.Transport = roundTripperFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.String() != "https://api.anthropic.com/v1/chat/completions" {
t.Fatalf("request URL = %q, want %q", r.URL.String(), "https://api.anthropic.com/v1/chat/completions")
}
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
t.Fatalf("decode request body: %v", err)
}
resp := map[string]any{
"choices": []map[string]any{
{
"message": map[string]any{"content": "ok"},
"finish_reason": "stop",
},
},
}
data, err := json.Marshal(resp)
if err != nil {
t.Fatalf("marshal response: %v", err)
}
return &http.Response{
StatusCode: http.StatusOK,
Header: http.Header{"Content-Type": []string{"application/json"}},
Body: io.NopCloser(bytes.NewReader(data)),
}, nil
})
_, err := p.Chat(
t.Context(),
[]Message{{Role: "user", Content: "hi"}},
nil,
"claude-sonnet-4.6",
nil,
)
if err != nil {
t.Fatalf("Chat() error = %v", err)
}
if got := requestBody["model"]; got != "claude-sonnet-4-6" {
t.Fatalf("request model = %v, want %q", got, "claude-sonnet-4-6")
}
}
func TestProvider_RequestTimeoutDefault(t *testing.T) {
p := NewProviderWithMaxTokensFieldAndTimeout("key", "https://example.com/v1", "", "", 0)
if p.httpClient.Timeout != defaultRequestTimeout {