ScuttleBot

scuttlebot / internal / llm / anthropic.go
Blame History Raw 101 lines
1
package llm
2
3
import (
4
"bytes"
5
"context"
6
"encoding/json"
7
"fmt"
8
"io"
9
"net/http"
10
)
11
12
const anthropicAPIBase = "https://api.anthropic.com"
13
const anthropicVersion = "2023-06-01"
14
15
// anthropicModels is a curated static list — Anthropic has no public list API.
16
var anthropicModels = []ModelInfo{
17
{ID: "claude-opus-4-6", Name: "Claude Opus 4.6"},
18
{ID: "claude-sonnet-4-6", Name: "Claude Sonnet 4.6"},
19
{ID: "claude-haiku-4-5-20251001", Name: "Claude Haiku 4.5"},
20
{ID: "claude-3-5-sonnet-20241022", Name: "Claude 3.5 Sonnet"},
21
{ID: "claude-3-5-haiku-20241022", Name: "Claude 3.5 Haiku"},
22
{ID: "claude-3-opus-20240229", Name: "Claude 3 Opus"},
23
{ID: "claude-3-sonnet-20240229", Name: "Claude 3 Sonnet"},
24
{ID: "claude-3-haiku-20240307", Name: "Claude 3 Haiku"},
25
}
26
27
type anthropicProvider struct {
28
apiKey string
29
model string
30
baseURL string
31
http *http.Client
32
}
33
34
func newAnthropicProvider(cfg BackendConfig, hc *http.Client) *anthropicProvider {
35
model := cfg.Model
36
if model == "" {
37
model = "claude-3-5-sonnet-20241022"
38
}
39
baseURL := cfg.BaseURL
40
if baseURL == "" {
41
baseURL = anthropicAPIBase
42
}
43
return &anthropicProvider{
44
apiKey: cfg.APIKey,
45
model: model,
46
baseURL: baseURL,
47
http: hc,
48
}
49
}
50
51
func (p *anthropicProvider) Summarize(ctx context.Context, prompt string) (string, error) {
52
body, _ := json.Marshal(map[string]any{
53
"model": p.model,
54
"max_tokens": 512,
55
"messages": []map[string]string{
56
{"role": "user", "content": prompt},
57
},
58
})
59
req, err := http.NewRequestWithContext(ctx, "POST", p.baseURL+"/v1/messages", bytes.NewReader(body))
60
if err != nil {
61
return "", err
62
}
63
req.Header.Set("x-api-key", p.apiKey)
64
req.Header.Set("anthropic-version", anthropicVersion)
65
req.Header.Set("Content-Type", "application/json")
66
67
resp, err := p.http.Do(req)
68
if err != nil {
69
return "", fmt.Errorf("anthropic request: %w", err)
70
}
71
defer resp.Body.Close()
72
73
data, _ := io.ReadAll(resp.Body)
74
if resp.StatusCode != http.StatusOK {
75
return "", fmt.Errorf("anthropic error %d: %s", resp.StatusCode, string(data))
76
}
77
78
var result struct {
79
Content []struct {
80
Type string `json:"type"`
81
Text string `json:"text"`
82
} `json:"content"`
83
}
84
if err := json.Unmarshal(data, &result); err != nil {
85
return "", fmt.Errorf("anthropic parse: %w", err)
86
}
87
for _, c := range result.Content {
88
if c.Type == "text" {
89
return c.Text, nil
90
}
91
}
92
return "", fmt.Errorf("anthropic returned no text content")
93
}
94
95
// DiscoverModels returns a curated static list (Anthropic has no public list API).
96
func (p *anthropicProvider) DiscoverModels(_ context.Context) ([]ModelInfo, error) {
97
models := make([]ModelInfo, len(anthropicModels))
98
copy(models, anthropicModels)
99
return models, nil
100
}
101

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button