mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 06:17:41 +00:00
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:
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"})
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user