fix(providers): omit temperature for Claude Opus/Sonnet 4.6+ (#1180)

Anthropic returns HTTP 400 when temperature is sent to claude-opus-4-6,
claude-sonnet-4-6, and newer 4.x models. Agent summoning and chat both
hardcode temperature; skip the field for affected model IDs.

Co-authored-by: Ojus Save <ojusave@users.noreply.github.com>
This commit is contained in:
Ojus M SaveandOjus Save authored and GitHub committed 2026-06-05 01:18:07 +07:00
1 parent 43837afca3
commit 8abc16ee88
2 files changed
+69 -1

No files matched your search

@@ -150,6 +150,48 @@ func TestAnthropicAdapterToRequest_Thinking(t *testing.T) {
}
}
func TestAnthropicAdapterToRequest_SkipsTemperatureForClaude46(t *testing.T) {
adapter, _ := NewAnthropicAdapter(ProviderConfig{APIKey: "sk-test"})
for _, model := range []string{"claude-opus-4-6", "claude-sonnet-4-6", "claude-opus-4-7-20260501"} {
t.Run(model, func(t *testing.T) {
req := ChatRequest{
Model: model,
Messages: []Message{{Role: "user", Content: "hi"}},
Options: map[string]any{OptTemperature: 0.7},
}
data, _, err := adapter.ToRequest(req)
if err != nil {
t.Fatal(err)
}
var body map[string]any
if err := json.Unmarshal(data, &body); err != nil {
t.Fatal(err)
}
if _, hasTemp := body["temperature"]; hasTemp {
t.Errorf("model %q: temperature should be omitted", model)
}
})
}
req := ChatRequest{
Model: "claude-sonnet-4-5-20250929",
Messages: []Message{{Role: "user", Content: "hi"}},
Options: map[string]any{OptTemperature: 0.7},
}
data, _, err := adapter.ToRequest(req)
if err != nil {
t.Fatal(err)
}
var body map[string]any
if err := json.Unmarshal(data, &body); err != nil {
t.Fatal(err)
}
if body["temperature"] != 0.7 {
t.Errorf("sonnet 4.5 should keep temperature, got %v", body["temperature"])
}
}
func TestAnthropicAdapterFromResponse_ToolCalls(t *testing.T) {
adapter, _ := NewAnthropicAdapter(ProviderConfig{APIKey: "sk-test"})
+27 -1
View File
@@ -213,7 +213,9 @@ func (p *AnthropicProvider) buildRequestBody(model string, req ChatRequest, stre
body["max_tokens"] = v
}
if v, ok := req.Options[OptTemperature]; ok {
body["temperature"] = v
if !anthropicSkipsTemperature(model) {
body["temperature"] = v
}
}
// Enable extended thinking if thinking_level is set
@@ -234,6 +236,30 @@ func (p *AnthropicProvider) buildRequestBody(model string, req ChatRequest, stre
return body
}
// anthropicSkipsTemperature reports whether the Messages API rejects sampling
// parameters for this model. Claude Opus/Sonnet 4.6+ and Opus 4.7+ return HTTP
// 400 when temperature (and top_p/top_k) are included; omit them entirely.
func anthropicSkipsTemperature(model string) bool {
m := strings.ToLower(model)
for _, family := range []string{"claude-opus-4-", "claude-sonnet-4-"} {
after, ok := strings.CutPrefix(m, family)
if !ok {
continue
}
minor := 0
for _, c := range after {
if c < '0' || c > '9' {
break
}
minor = minor*10 + int(c-'0')
}
if minor >= 6 {
return true
}
}
return false
}
// anthropicThinkingBudget maps a thinking level to a token budget.
func anthropicThinkingBudget(level string) int {
switch level {