From 55a0dd60191a172569a17a9c58d2b1f8f0246da9 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Tue, 31 Mar 2026 12:54:50 +0800 Subject: [PATCH] fix: clear thought_signature in DropReasoningDetails transformer DropReasoningDetails only cleared reasoning_details but left thought_signature intact. When switching from a provider like MiniMax back to Claude, the leftover signature caused a 400 error because Claude rejects unknown thinking block signatures. ReasoningNormalizer already clears both fields on model change, but DropReasoningDetails (used when the target model does not support reasoning at all) missed the thought_signature field. Signed-off-by: majiayu000 <1835304752@qq.com> --- .../src/transformer/drop_reasoning_details.rs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/forge_domain/src/transformer/drop_reasoning_details.rs b/crates/forge_domain/src/transformer/drop_reasoning_details.rs index e6a016feb7..6e02006b27 100644 --- a/crates/forge_domain/src/transformer/drop_reasoning_details.rs +++ b/crates/forge_domain/src/transformer/drop_reasoning_details.rs @@ -9,6 +9,7 @@ impl Transformer for DropReasoningDetails { context.messages.iter_mut().for_each(|message| { if let crate::ContextMessage::Text(text) = &mut **message { text.reasoning_details = None; + text.thought_signature = None; } }); @@ -181,4 +182,30 @@ mod tests { TransformationSnapshot::new("DropReasoningDetails_preserve_non_text", fixture, actual); assert_yaml_snapshot!(snapshot); } + + #[test] + fn test_drop_reasoning_clears_thought_signature() { + let reasoning_details = vec![ReasoningFull { + text: Some("thinking".to_string()), + signature: Some("sig_minimax".to_string()), + ..Default::default() + }]; + + let fixture = Context::default().add_message(ContextMessage::Text( + TextMessage::new(Role::Assistant, "response") + .model(crate::ModelId::new("MiniMax-M2.7")) + .reasoning_details(reasoning_details) + .thought_signature("minimax_thought_sig".to_string()), + )); + + let mut transformer = DropReasoningDetails; + let actual = transformer.transform(fixture); + + if let crate::ContextMessage::Text(text) = &*actual.messages[0] { + assert_eq!(text.reasoning_details, None); + assert_eq!(text.thought_signature, None); + } else { + panic!("expected TextMessage"); + } + } }