package summarize import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "os" "time" ) // Anthropic talks to the Claude Messages API directly via net/http to avoid an // SDK dependency. Requires ANTHROPIC_API_KEY (or APIKey set explicitly). type Anthropic struct { APIKey string Model string MaxTokens int BaseURL string // optional override; defaults to https://api.anthropic.com Client *http.Client } func (a *Anthropic) Name() string { return "anthropic-api" } type anthroMessage struct { Role string `json:"role"` Content string `json:"content"` } type anthroRequest struct { Model string `json:"model"` MaxTokens int `json:"max_tokens"` System string `json:"system,omitempty"` Messages []anthroMessage `json:"messages"` } type anthroContentBlock struct { Type string `json:"type"` Text string `json:"text"` } type anthroResponse struct { Content []anthroContentBlock `json:"content"` Error *struct { Type string `json:"type"` Message string `json:"message"` } `json:"error,omitempty"` } func (a *Anthropic) Summarize(ctx context.Context, systemPrompt, userContent string) (string, error) { key := a.APIKey if key == "" { key = os.Getenv("ANTHROPIC_API_KEY") } if key == "" { return "", fmt.Errorf("ANTHROPIC_API_KEY is not set") } model := a.Model if model == "" { model = "claude-sonnet-4-6" } maxTokens := a.MaxTokens if maxTokens == 0 { maxTokens = 4096 } baseURL := a.BaseURL if baseURL == "" { baseURL = "https://api.anthropic.com" } client := a.Client if client == nil { client = &http.Client{Timeout: 5 * time.Minute} } body := anthroRequest{ Model: model, MaxTokens: maxTokens, System: systemPrompt, Messages: []anthroMessage{ {Role: "user", Content: userContent}, }, } buf, err := json.Marshal(body) if err != nil { return "", err } req, err := http.NewRequestWithContext(ctx, "POST", baseURL+"/v1/messages", bytes.NewReader(buf)) if err != nil { return "", err } req.Header.Set("Content-Type", "application/json") req.Header.Set("x-api-key", key) req.Header.Set("anthropic-version", "2023-06-01") resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) if resp.StatusCode/100 != 2 { return "", fmt.Errorf("anthropic API %d: %s", resp.StatusCode, string(respBody)) } var out anthroResponse if err := json.Unmarshal(respBody, &out); err != nil { return "", fmt.Errorf("decoding anthropic response: %w", err) } if out.Error != nil { return "", fmt.Errorf("anthropic error: %s: %s", out.Error.Type, out.Error.Message) } var text bytes.Buffer for _, c := range out.Content { if c.Type == "text" { text.WriteString(c.Text) } } return text.String(), nil }