package ai import ( "context" "fmt" "gitea.oblak.solutions/dimitar/gitFlow/internal/config" "gitea.oblak.solutions/dimitar/gitFlow/internal/httpclient" "gitea.oblak.solutions/dimitar/gitFlow/pkg/status" ) const ( defaultAnthropicBaseURL = "https://api.anthropic.com/v1" anthropicVersion = "2023-06-01" ) // AnthropicProvider talks to the Anthropic Messages API. type AnthropicProvider struct { model string baseURL string apiKey string http *httpclient.Client } // NewAnthropic builds an Anthropic provider. base_url and model fall back // to sensible defaults when unset in the configuration. func NewAnthropic(cfg config.AIConfig, apiKey string) *AnthropicProvider { baseURL := cfg.BaseURL if baseURL == "" { baseURL = defaultAnthropicBaseURL } model := cfg.Model if model == "" { model = "claude-3-5-haiku-latest" } return &AnthropicProvider{model: model, baseURL: baseURL, apiKey: apiKey, http: httpclient.New()} } // Name implements Provider. func (p *AnthropicProvider) Name() string { return "anthropic" } type anthropicRequest struct { Model string `json:"model"` MaxTokens int `json:"max_tokens"` System string `json:"system"` Messages []chatMessage `json:"messages"` } type anthropicResponse struct { Content []struct { Type string `json:"type"` Text string `json:"text"` } `json:"content"` Error *struct { Message string `json:"message"` } `json:"error"` } // Suggest implements Provider. func (p *AnthropicProvider) Suggest(ctx context.Context, result status.ScanResult) ([]Suggestion, error) { req := anthropicRequest{ Model: p.model, MaxTokens: 1024, System: "You are gitflow, a concise Git repository health assistant. You reply with JSON only.", Messages: []chatMessage{{Role: "user", Content: BuildPrompt(result)}}, } var resp anthropicResponse err := p.http.PostJSON(ctx, p.baseURL+"/messages", map[string]string{"x-api-key": p.apiKey, "anthropic-version": anthropicVersion}, req, &resp) if err != nil { return nil, err } if resp.Error != nil { return nil, fmt.Errorf("ai: anthropic: %s", resp.Error.Message) } for _, c := range resp.Content { if c.Type == "text" && c.Text != "" { return parseSuggestions(c.Text) } } return nil, fmt.Errorf("ai: anthropic: empty response") }