fix: anthropic provider falls through to HTTPProvider with wrong API format

When provider is configured as 'anthropic' with a custom api_base,
CreateProvider's anthropic branch assigned apiKey/apiBase to local
variables but never returned, causing execution to fall through to
NewHTTPProvider. This resulted in:

1. Requests using OpenAI format (/chat/completions) instead of
   Anthropic format (/v1/messages)
2. Auth header using 'Authorization: Bearer' instead of 'x-api-key'

Fix:
- NewClaudeProvider now accepts an apiBase parameter (defaults to
  https://api.anthropic.com when empty)
- NewClaudeProviderWithTokenSource passes apiBase through
- Both the explicit provider branch and model-name fallback branch
  now return NewClaudeProvider directly instead of falling through
This commit is contained in:
Together 2026-02-17 13:57:48 +08:00
parent 57dac394c5
commit 2b6b4fb35a
3 changed files with 12 additions and 18 deletions

View file

@ -15,16 +15,19 @@ type ClaudeProvider struct {
tokenSource func() (string, error)
}
func NewClaudeProvider(token string) *ClaudeProvider {
func NewClaudeProvider(token string, apiBase string) *ClaudeProvider {
if apiBase == "" {
apiBase = "https://api.anthropic.com"
}
client := anthropic.NewClient(
option.WithAuthToken(token),
option.WithBaseURL("https://api.anthropic.com"),
option.WithBaseURL(apiBase),
)
return &ClaudeProvider{client: &client}
}
func NewClaudeProviderWithTokenSource(token string, tokenSource func() (string, error)) *ClaudeProvider {
p := NewClaudeProvider(token)
func NewClaudeProviderWithTokenSource(token string, apiBase string, tokenSource func() (string, error)) *ClaudeProvider {
p := NewClaudeProvider(token, apiBase)
p.tokenSource = tokenSource
return p
}

View file

@ -175,7 +175,7 @@ func TestClaudeProvider_ChatRoundTrip(t *testing.T) {
}))
defer server.Close()
provider := NewClaudeProvider("test-token")
provider := NewClaudeProvider("test-token", "")
provider.client = createAnthropicTestClient(server.URL, "test-token")
messages := []Message{{Role: "user", Content: "Hello"}}
@ -195,7 +195,7 @@ func TestClaudeProvider_ChatRoundTrip(t *testing.T) {
}
func TestClaudeProvider_GetDefaultModel(t *testing.T) {
p := NewClaudeProvider("test-token")
p := NewClaudeProvider("test-token", "")
if got := p.GetDefaultModel(); got != "claude-sonnet-4-5-20250929" {
t.Errorf("GetDefaultModel() = %q, want %q", got, "claude-sonnet-4-5-20250929")
}

View file

@ -205,7 +205,7 @@ func createClaudeAuthProvider() (LLMProvider, error) {
if cred == nil {
return nil, fmt.Errorf("no credentials for anthropic. Run: picoclaw auth login --provider anthropic")
}
return NewClaudeProviderWithTokenSource(cred.AccessToken, createClaudeTokenSource()), nil
return NewClaudeProviderWithTokenSource(cred.AccessToken, "", createClaudeTokenSource()), nil
}
func createCodexAuthProvider() (LLMProvider, error) {
@ -257,11 +257,7 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) {
if cfg.Providers.Anthropic.AuthMethod == "oauth" || cfg.Providers.Anthropic.AuthMethod == "token" {
return createClaudeAuthProvider()
}
apiKey = cfg.Providers.Anthropic.APIKey
apiBase = cfg.Providers.Anthropic.APIBase
if apiBase == "" {
apiBase = "https://api.anthropic.com/v1"
}
return NewClaudeProvider(cfg.Providers.Anthropic.APIKey, cfg.Providers.Anthropic.APIBase), nil
}
case "openrouter":
if cfg.Providers.OpenRouter.APIKey != "" {
@ -360,12 +356,7 @@ func CreateProvider(cfg *config.Config) (LLMProvider, error) {
if cfg.Providers.Anthropic.AuthMethod == "oauth" || cfg.Providers.Anthropic.AuthMethod == "token" {
return createClaudeAuthProvider()
}
apiKey = cfg.Providers.Anthropic.APIKey
apiBase = cfg.Providers.Anthropic.APIBase
proxy = cfg.Providers.Anthropic.Proxy
if apiBase == "" {
apiBase = "https://api.anthropic.com/v1"
}
return NewClaudeProvider(cfg.Providers.Anthropic.APIKey, cfg.Providers.Anthropic.APIBase), nil
case (strings.Contains(lowerModel, "gpt") || strings.HasPrefix(model, "openai/")) && (cfg.Providers.OpenAI.APIKey != "" || cfg.Providers.OpenAI.AuthMethod != ""):
if cfg.Providers.OpenAI.AuthMethod == "oauth" || cfg.Providers.OpenAI.AuthMethod == "token" {