diff options
| author | Paul Buetow <paul@buetow.org> | 2026-01-29 20:23:41 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-01-29 20:23:41 +0200 |
| commit | c6bb463837ec8c41261604e416aeab023663ba09 (patch) | |
| tree | ea81ab84b698c3a98fda93a9051d21f2c79708a6 /internal/appconfig/config.go | |
| parent | d088267f55c45a7ffd90a056d56e02da61b525fc (diff) | |
feat: add native Anthropic API provider support
- Implement new anthropicClient with full Client interface
- Add Streamer interface for token-by-token streaming via SSE
- Add Anthropic Messages API v1 integration with proper headers
- Support claude-3-5-sonnet-20241022 as default model
- Add configuration via [anthropic] TOML section
- Add environment variable overrides (HEXAI_ANTHROPIC_*)
- Support both HEXAI_ANTHROPIC_API_KEY and ANTHROPIC_API_KEY fallback
- Integrate Anthropic key handling in LSP, CLI, and llmutils
- Update provider factory to support 'anthropic' provider name
- Add 11 comprehensive unit tests for Anthropic client
- Update config.toml.example with [anthropic] section
- Update NewFromConfig() signature to accept anthropicAPIKey parameter
- All 51 internal LLM tests pass (11 new Anthropic tests + 40 existing)
Anthropic models can be accessed via:
[anthropic]
model = "claude-3-5-sonnet-20241022"
base_url = "https://api.anthropic.com/v1"
temperature = 0.2
or environment:
export HEXAI_PROVIDER="anthropic"
export HEXAI_ANTHROPIC_API_KEY="your-key"
Amp-Thread-ID: https://ampcode.com/threads/T-019c0af1-f215-72cf-9940-b014b1a9576b
Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'internal/appconfig/config.go')
| -rw-r--r-- | internal/appconfig/config.go | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/internal/appconfig/config.go b/internal/appconfig/config.go index 59ffd89..f41d4d9 100644 --- a/internal/appconfig/config.go +++ b/internal/appconfig/config.go @@ -68,6 +68,10 @@ type App struct { CopilotModel string `json:"copilot_model" toml:"copilot_model"` // Default temperature for Copilot requests (nil means use provider default) CopilotTemperature *float64 `json:"copilot_temperature" toml:"copilot_temperature"` + AnthropicBaseURL string `json:"anthropic_base_url" toml:"anthropic_base_url"` + AnthropicModel string `json:"anthropic_model" toml:"anthropic_model"` + // Default temperature for Anthropic requests (nil means use provider default) + AnthropicTemperature *float64 `json:"anthropic_temperature" toml:"anthropic_temperature"` // Per-surface provider/model configurations (ordered; first entry is primary) CompletionConfigs []SurfaceConfig `json:"-" toml:"-"` @@ -137,6 +141,7 @@ func newDefaultConfig() App { OpenAITemperature: &t, OllamaTemperature: &t, CopilotTemperature: &t, + AnthropicTemperature: &t, ManualInvokeMinPrefix: 0, CompletionDebounceMs: 800, CompletionThrottleMs: 0, @@ -235,6 +240,7 @@ type fileConfig struct { OpenRouter sectionOpenRouter `toml:"openrouter"` Copilot sectionCopilot `toml:"copilot"` Ollama sectionOllama `toml:"ollama"` + Anthropic sectionAnthropic `toml:"anthropic"` Prompts sectionPrompts `toml:"prompts"` Tmux sectionTmux `toml:"tmux"` Stats sectionStats `toml:"stats"` @@ -331,6 +337,12 @@ type sectionOllama struct { Temperature *float64 `toml:"temperature"` } +type sectionAnthropic struct { + Model string `toml:"model"` + BaseURL string `toml:"base_url"` + Temperature *float64 `toml:"temperature"` +} + // Prompts sections type sectionPrompts struct { Completion sectionPromptsCompletion `toml:"completion"` @@ -486,6 +498,16 @@ func (fc *fileConfig) toApp() App { out.mergeProviderFields(&tmp) } + // anthropic + if (fc.Anthropic != sectionAnthropic{}) || fc.Anthropic.Temperature != nil { + tmp := App{ + AnthropicBaseURL: fc.Anthropic.BaseURL, + AnthropicModel: fc.Anthropic.Model, + AnthropicTemperature: fc.Anthropic.Temperature, + } + out.mergeProviderFields(&tmp) + } + // prompts // completion if (fc.Prompts.Completion != sectionPromptsCompletion{}) { @@ -1292,6 +1314,19 @@ func loadFromEnv(logger *log.Logger) *App { any = true } + if s := getenv("HEXAI_ANTHROPIC_BASE_URL"); s != "" { + out.AnthropicBaseURL = s + any = true + } + if model, ok := pickModel("anthropic", getenv("HEXAI_ANTHROPIC_MODEL")); ok { + out.AnthropicModel = model + any = true + } + if f, ok := parseFloatPtr("HEXAI_ANTHROPIC_TEMPERATURE"); ok { + out.AnthropicTemperature = f + any = true + } + // Per-surface overrides buildEntry := func(modelKey, tempKey, providerKey string) ([]SurfaceConfig, bool) { model := getenv(modelKey) |
