diff --git a/pkg/providers/anthropic_messages/provider.go b/pkg/providers/anthropic_messages/provider.go index 0f362dd39..6ef74a5b1 100644 --- a/pkg/providers/anthropic_messages/provider.go +++ b/pkg/providers/anthropic_messages/provider.go @@ -73,10 +73,6 @@ func (p *Provider) Chat( model string, options map[string]any, ) (*LLMResponse, error) { - if p.apiBase == "" { - return nil, fmt.Errorf("API base not configured") - } - if p.apiKey == "" { return nil, fmt.Errorf("API key not configured") } @@ -323,19 +319,30 @@ func parseResponseBody(body []byte) (*LLMResponse, error) { } // normalizeBaseURL ensures the base URL is properly formatted. +// It removes /v1 suffix if present (to avoid duplication) and always appends /v1. +// This handles edge cases like "https://api.example.com/v1/proxy" correctly. func normalizeBaseURL(apiBase string) string { base := strings.TrimSpace(apiBase) if base == "" { return defaultBaseURL } + // Remove trailing slashes base = strings.TrimRight(base, "/") - // Add /v1 if not present - if !strings.HasSuffix(base, "/v1") { - base = base + "/v1" + + // Remove /v1 suffix if present (will be re-added) + // This prevents duplication for URLs like "https://api.example.com/v1/proxy" + if before, ok := strings.CutSuffix(base, "/v1"); ok { + base = before } - return base + // Ensure we don't have an empty string after cutting + if base == "" { + return defaultBaseURL + } + + // Add /v1 suffix (required by Anthropic Messages API) + return base + "/v1" } // Helper functions for type conversion diff --git a/pkg/providers/anthropic_messages/provider_test.go b/pkg/providers/anthropic_messages/provider_test.go index 36a582412..3fa062360 100644 --- a/pkg/providers/anthropic_messages/provider_test.go +++ b/pkg/providers/anthropic_messages/provider_test.go @@ -413,26 +413,18 @@ func TestGetDefaultModel(t *testing.T) { } } -// Mock HTTP server test for integration testing +// TestProviderChatErrors tests error handling in Chat. +// Note: apiBase check removed as it's dead code - normalizeBaseURL() always provides a default. func TestProviderChatErrors(t *testing.T) { tests := []struct { name string apiKey string - apiBase string messages []Message wantErrMsg string }{ - { - name: "missing API base", - apiKey: "test-key", - apiBase: "", - messages: []Message{{Role: "user", Content: "Test"}}, - wantErrMsg: "API base not configured", - }, { name: "missing API key", apiKey: "", - apiBase: "https://api.example.com", messages: []Message{{Role: "user", Content: "Test"}}, wantErrMsg: "API key not configured", }, @@ -440,11 +432,8 @@ func TestProviderChatErrors(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create provider with empty apiBase to trigger error - provider := &Provider{ - apiKey: tt.apiKey, - apiBase: tt.apiBase, - } + // Create provider using constructor to ensure proper initialization + provider := NewProvider(tt.apiKey, "https://api.example.com") _, err := provider.Chat(context.Background(), tt.messages, nil, "test-model", nil) if err == nil {