mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-04-17 20:03:42 +00:00
Merge pull request #2509 from adamhelfgott/fix-claude-thinking-temperature
Normalize Claude temperature when thinking is enabled
This commit is contained in:
@@ -137,6 +137,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
|
||||
|
||||
// Disable thinking if tool_choice forces tool use (Anthropic API constraint)
|
||||
body = disableThinkingIfToolChoiceForced(body)
|
||||
body = normalizeClaudeTemperatureForThinking(body)
|
||||
|
||||
// Auto-inject cache_control if missing (optimization for ClawdBot/clients without caching support)
|
||||
if countCacheControls(body) == 0 {
|
||||
@@ -307,6 +308,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
|
||||
|
||||
// Disable thinking if tool_choice forces tool use (Anthropic API constraint)
|
||||
body = disableThinkingIfToolChoiceForced(body)
|
||||
body = normalizeClaudeTemperatureForThinking(body)
|
||||
|
||||
// Auto-inject cache_control if missing (optimization for ClawdBot/clients without caching support)
|
||||
if countCacheControls(body) == 0 {
|
||||
@@ -651,6 +653,25 @@ func disableThinkingIfToolChoiceForced(body []byte) []byte {
|
||||
return body
|
||||
}
|
||||
|
||||
// normalizeClaudeTemperatureForThinking keeps Anthropic message requests valid when
|
||||
// thinking is enabled. Anthropic rejects temperatures other than 1 when
|
||||
// thinking.type is enabled/adaptive/auto.
|
||||
func normalizeClaudeTemperatureForThinking(body []byte) []byte {
|
||||
if !gjson.GetBytes(body, "temperature").Exists() {
|
||||
return body
|
||||
}
|
||||
|
||||
thinkingType := strings.ToLower(strings.TrimSpace(gjson.GetBytes(body, "thinking.type").String()))
|
||||
switch thinkingType {
|
||||
case "enabled", "adaptive", "auto":
|
||||
if temp := gjson.GetBytes(body, "temperature"); temp.Exists() && temp.Type == gjson.Number && temp.Float() == 1 {
|
||||
return body
|
||||
}
|
||||
body, _ = sjson.SetBytes(body, "temperature", 1)
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
type compositeReadCloser struct {
|
||||
io.Reader
|
||||
closers []func() error
|
||||
|
||||
@@ -1833,3 +1833,43 @@ func TestApplyCloaking_PreservesConfiguredStrictModeAndSensitiveWordsWhenModeOmi
|
||||
t.Fatalf("expected configured sensitive word obfuscation to apply, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeClaudeTemperatureForThinking_AdaptiveCoercesToOne(t *testing.T) {
|
||||
payload := []byte(`{"temperature":0,"thinking":{"type":"adaptive"},"output_config":{"effort":"max"}}`)
|
||||
out := normalizeClaudeTemperatureForThinking(payload)
|
||||
|
||||
if got := gjson.GetBytes(out, "temperature").Float(); got != 1 {
|
||||
t.Fatalf("temperature = %v, want 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeClaudeTemperatureForThinking_EnabledCoercesToOne(t *testing.T) {
|
||||
payload := []byte(`{"temperature":0.2,"thinking":{"type":"enabled","budget_tokens":2048}}`)
|
||||
out := normalizeClaudeTemperatureForThinking(payload)
|
||||
|
||||
if got := gjson.GetBytes(out, "temperature").Float(); got != 1 {
|
||||
t.Fatalf("temperature = %v, want 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeClaudeTemperatureForThinking_NoThinkingLeavesTemperatureAlone(t *testing.T) {
|
||||
payload := []byte(`{"temperature":0,"messages":[{"role":"user","content":"hi"}]}`)
|
||||
out := normalizeClaudeTemperatureForThinking(payload)
|
||||
|
||||
if got := gjson.GetBytes(out, "temperature").Float(); got != 0 {
|
||||
t.Fatalf("temperature = %v, want 0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeClaudeTemperatureForThinking_AfterForcedToolChoiceKeepsOriginalTemperature(t *testing.T) {
|
||||
payload := []byte(`{"temperature":0,"thinking":{"type":"adaptive"},"output_config":{"effort":"max"},"tool_choice":{"type":"any"}}`)
|
||||
out := disableThinkingIfToolChoiceForced(payload)
|
||||
out = normalizeClaudeTemperatureForThinking(out)
|
||||
|
||||
if gjson.GetBytes(out, "thinking").Exists() {
|
||||
t.Fatalf("thinking should be removed when tool_choice forces tool use")
|
||||
}
|
||||
if got := gjson.GetBytes(out, "temperature").Float(); got != 0 {
|
||||
t.Fatalf("temperature = %v, want 0", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user