Skip to content

Commit 011f824

Browse files
bjornclaude
authored andcommitted
fix(providers): clamp unsupported temperatures in Claude Code provider (zeroclaw-labs#3961)
The Claude Code CLI only supports temperatures 0.7 and 1.0, but internal subsystems (memory consolidation, context summarizer) use lower values like 0.1 and 0.2. Previously the provider rejected these with a hard error, triggering retries and WARN-level log noise. Clamp to the nearest supported value instead, since the CLI ignores temperature anyway. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ccbff49 commit 011f824

1 file changed

Lines changed: 38 additions & 14 deletions

File tree

src/providers/claude_code.rs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,31 @@ impl ClaudeCodeProvider {
9090
.any(|v| (temperature - v).abs() < TEMP_EPSILON)
9191
}
9292

93-
fn validate_temperature(temperature: f64) -> anyhow::Result<()> {
93+
fn validate_temperature(temperature: f64) -> anyhow::Result<f64> {
9494
if !temperature.is_finite() {
9595
anyhow::bail!("Claude Code provider received non-finite temperature value");
9696
}
97-
if !Self::supports_temperature(temperature) {
98-
anyhow::bail!(
99-
"temperature unsupported by Claude Code CLI: {temperature}. \
100-
Supported values: 0.7 or 1.0"
101-
);
97+
if Self::supports_temperature(temperature) {
98+
return Ok(temperature);
10299
}
103-
Ok(())
100+
// Clamp to the nearest supported value — the CLI ignores temperature
101+
// anyway, so a hard error just blocks callers like memory consolidation
102+
// that legitimately request low temperatures.
103+
let clamped = *CLAUDE_CODE_SUPPORTED_TEMPERATURES
104+
.iter()
105+
.min_by(|a, b| {
106+
(temperature - **a)
107+
.abs()
108+
.partial_cmp(&(temperature - **b).abs())
109+
.unwrap()
110+
})
111+
.unwrap();
112+
tracing::debug!(
113+
requested = temperature,
114+
clamped = clamped,
115+
"Clamped unsupported temperature to nearest Claude Code CLI value"
116+
);
117+
Ok(clamped)
104118
}
105119

106120
fn redact_stderr(stderr: &[u8]) -> String {
@@ -353,11 +367,18 @@ mod tests {
353367
}
354368

355369
#[test]
356-
fn validate_temperature_rejects_custom_value() {
357-
let err = ClaudeCodeProvider::validate_temperature(0.2).unwrap_err();
358-
assert!(err
359-
.to_string()
360-
.contains("temperature unsupported by Claude Code CLI"));
370+
fn validate_temperature_clamps_custom_value() {
371+
let clamped = ClaudeCodeProvider::validate_temperature(0.2).unwrap();
372+
assert!((clamped - 0.7).abs() < 1e-9, "0.2 should clamp to 0.7");
373+
374+
let clamped = ClaudeCodeProvider::validate_temperature(0.9).unwrap();
375+
assert!((clamped - 1.0).abs() < 1e-9, "0.9 should clamp to 1.0");
376+
}
377+
378+
#[test]
379+
fn validate_temperature_rejects_non_finite() {
380+
assert!(ClaudeCodeProvider::validate_temperature(f64::NAN).is_err());
381+
assert!(ClaudeCodeProvider::validate_temperature(f64::INFINITY).is_err());
361382
}
362383

363384
#[tokio::test]
@@ -471,10 +492,13 @@ mod tests {
471492
}
472493

473494
#[tokio::test]
474-
async fn chat_with_history_rejects_bad_temperature() {
495+
async fn chat_with_history_clamps_bad_temperature() {
475496
let provider = echo_provider();
476497
let messages = vec![ChatMessage::user("test")];
477498
let result = provider.chat_with_history(&messages, "default", 0.5).await;
478-
assert!(result.is_err());
499+
assert!(
500+
result.is_ok(),
501+
"unsupported temperature should be clamped, not rejected"
502+
);
479503
}
480504
}

0 commit comments

Comments
 (0)