fix: f32→f64 precision artifact in temperature causes provider 400 errors (#1450)

* fix: f32→f64 precision artifact in temperature causes provider 400 errors

Direct f32-as-f64 preserves the binary representation, producing values
like 0.699999988079071 instead of 0.7. Some OpenAI-compatible providers
(e.g. Zhipu GLM-5) reject these with a 400 error. Add round_f32_to_f64()
that formats to 6 decimal places before parsing back to f64.

* fix: address clippy redundant_closure lint (takeover #1418) [skip-regression-check]

Co-Authored-By: Boomboomdunce <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* fix: use numeric rounding, update doc comment, remove duplicate assertion [skip-regression-check]

Address review feedback on #1450:
- Replace format!+parse with numeric rounding to avoid allocation
- Update doc comment to only mention temperature (not top_p)
- Remove duplicate assert_eq in test

Co-Authored-By: Boomboomdunce <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

---------

Co-authored-by: Boomboomdunce <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-03-19 21:46:25 -07:00
committed by GitHub
co-authored by Boomboomdunce Claude Opus 4.6
parent 455f543ba5
commit 3a523347b0
+22 -1
View File
@@ -112,6 +112,16 @@ impl<M: CompletionModel> RigAdapter<M> {
// -- Type conversion helpers --
/// Round an f32 to f64 without precision artifacts.
///
/// Direct `f32 as f64` preserves the binary representation, producing values
/// like `0.699999988079071` instead of `0.7`. Some providers (e.g. Zhipu/GLM)
/// reject these values with a 400 error. Rounding to 6 decimal places removes
/// the artifact while preserving all meaningful precision for temperature.
fn round_f32_to_f64(val: f32) -> f64 {
((val as f64) * 1_000_000.0).round() / 1_000_000.0
}
/// Normalize a JSON Schema for OpenAI strict mode compliance.
///
/// OpenAI strict function calling requires:
@@ -542,7 +552,7 @@ fn build_rig_request(
chat_history,
documents: Vec::new(),
tools,
temperature: temperature.map(|t| t as f64),
temperature: temperature.map(round_f32_to_f64),
max_tokens: max_tokens.map(|t| t as u64),
tool_choice,
additional_params,
@@ -767,6 +777,17 @@ fn normalize_tool_name(name: &str, known_tools: &HashSet<String>) -> String {
mod tests {
use super::*;
#[test]
fn test_round_f32_to_f64_no_precision_artifacts() {
// Direct f32->f64 cast produces 0.699999988079071 instead of 0.7
assert_eq!(round_f32_to_f64(0.7_f32), 0.7_f64);
assert_eq!(round_f32_to_f64(0.5_f32), 0.5_f64);
assert_eq!(round_f32_to_f64(1.0_f32), 1.0_f64);
assert_eq!(round_f32_to_f64(0.0_f32), 0.0_f64);
// Original cast produces artifacts — our fix should not
assert_ne!(0.7_f32 as f64, 0.7_f64);
}
#[test]
fn test_convert_messages_system_to_preamble() {
let messages = vec![