feat(llm): provider factory with per-provider default base URLs

This commit is contained in:
2026-06-07 13:08:27 +01:00
parent f6def2f71d
commit d008ba1424
3 changed files with 103 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
package llm
import (
"context"
"fmt"
"net/http"
)
type geminiClient struct {
http *http.Client
base string
key string
model string
audioModel string
maxTokens int
}
func (c *geminiClient) Vision(_ context.Context, _ string, _ []Image, _ Options) (string, error) {
return "", fmt.Errorf("gemini provider not yet implemented")
}
func (c *geminiClient) Transcribe(_ context.Context, _ []byte, _ string, _ Options) (string, error) {
return "", fmt.Errorf("gemini provider not yet implemented")
}
+40
View File
@@ -0,0 +1,40 @@
package llm
import (
"fmt"
"net/http"
"github.com/lukaszraczylo/captcha-solver-mcp/internal/config"
)
// New constructs the configured provider Client.
func New(cfg config.Config) (Client, error) {
hc := &http.Client{Timeout: cfg.Timeout}
switch cfg.Provider {
case "anthropic":
base := cfg.BaseURL
if base == "" {
base = "https://api.anthropic.com"
}
return &anthropicClient{http: hc, base: base, key: cfg.APIKey, model: cfg.Model, maxTokens: cfg.MaxTokens}, nil
case "openai":
base := cfg.BaseURL
if base == "" {
base = "https://api.openai.com"
}
return &openAIClient{http: hc, base: base, key: cfg.APIKey, model: cfg.Model, audioModel: cfg.AudioModel, maxTokens: cfg.MaxTokens}, nil
case "openai-compatible":
if cfg.BaseURL == "" {
return nil, fmt.Errorf("openai-compatible requires CAPTCHA_LLM_BASE_URL")
}
return &openAIClient{http: hc, base: cfg.BaseURL, key: cfg.APIKey, model: cfg.Model, audioModel: cfg.AudioModel, maxTokens: cfg.MaxTokens}, nil
case "gemini":
base := cfg.BaseURL
if base == "" {
base = "https://generativelanguage.googleapis.com"
}
return &geminiClient{http: hc, base: base, key: cfg.APIKey, model: cfg.Model, audioModel: cfg.AudioModel, maxTokens: cfg.MaxTokens}, nil
default:
return nil, fmt.Errorf("unknown provider %q", cfg.Provider)
}
}
+39
View File
@@ -0,0 +1,39 @@
package llm
import (
"testing"
"github.com/lukaszraczylo/captcha-solver-mcp/internal/config"
)
func TestNew(t *testing.T) {
tests := []struct {
name string
provider string
wantBase string
wantErr bool
}{
{"anthropic default base", "anthropic", "https://api.anthropic.com", false},
{"openai default base", "openai", "https://api.openai.com", false},
{"compatible needs base", "openai-compatible", "", true},
{"unknown", "zzz", "", true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
cfg := config.Config{Provider: tc.provider, APIKey: "k", Model: "m"}
c, err := New(cfg)
if tc.wantErr {
if err == nil {
t.Fatal("want error")
}
return
}
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if c == nil {
t.Fatal("nil client")
}
})
}
}