From 3ccb6e5e911d34538b63ed4e107bb25abcdb145d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 19 Sep 2026 01:51:47 -0400 Subject: [PATCH] fix(config): reasoning_effort accepts mixed case `reasoning_effort = "High"` in config.toml failed the whole parse: the derived Deserialize matched lowercase variants only, while FromStr lowercased its input. Deserialize now routes through FromStr, so both paths share one vocabulary. Unknown tokens and non-strings still error. Ported from upstream (grok-build sampling-types/types.rs, 1.0.35). --- .../codegen/kigi-sampling-types/src/types.rs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/codegen/kigi-sampling-types/src/types.rs b/crates/codegen/kigi-sampling-types/src/types.rs index 0fcbf21..fadf424 100644 --- a/crates/codegen/kigi-sampling-types/src/types.rs +++ b/crates/codegen/kigi-sampling-types/src/types.rs @@ -897,7 +897,7 @@ impl CompactionsRemaining { } /// Reasoning effort level. `None`/`Minimal` are omitted on the Anthropic Messages API. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize)] #[serde(rename_all = "lowercase")] pub enum ReasoningEffort { None, @@ -982,6 +982,16 @@ impl std::str::FromStr for ReasoningEffort { } } +impl<'de> serde::Deserialize<'de> for ReasoningEffort { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let raw = String::deserialize(deserializer)?; + raw.parse().map_err(serde::de::Error::custom) + } +} + /// Canonical wire parse; remapped menu ids need a model catalog. pub fn parse_canonical_effort_token(token: &str) -> Option { token.parse().ok() @@ -1849,6 +1859,24 @@ mod tests { assert!(serde_json::from_str::("\"BOGUS\"").is_err()); } + #[test] + fn reasoning_effort_deserializes_mixed_case() { + for (token, want) in [ + ("\"High\"", ReasoningEffort::High), + ("\"XHIGH\"", ReasoningEffort::Xhigh), + ("\"mAx\"", ReasoningEffort::Max), + ] { + let got: ReasoningEffort = serde_json::from_str(token).unwrap(); + assert_eq!(got, want, "deserialize {token}"); + } + let err = serde_json::from_str::("\"Highest\"").unwrap_err(); + assert!( + err.to_string().contains("invalid reasoning effort"), + "{err}" + ); + assert!(serde_json::from_str::("7").is_err()); + } + #[test] fn reasoning_effort_from_str_max_and_xhigh_are_distinct() { assert_eq!(