From 3a058138c1e4b7e18e9282588d0a898b64f4a12b Mon Sep 17 00:00:00 2001 From: Oleksiy Syvokon Date: Mon, 3 Nov 2025 12:22:51 +0200 Subject: [PATCH] Fix Sonnet's regression with inserting `` (#41800) Sometimes, inside the edit agent, Sonnet thinks that it's doing a tool call and closes its response with `` instead of properly closing . A better but more labor-intensive way of fixing this would be switching to streaming tool calls for LLMs that support it. Closes #39921 Release Notes: - Fixed Sonnet's regression with inserting `` sometimes --- crates/agent/src/edit_agent/edit_parser.rs | 46 +++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/crates/agent/src/edit_agent/edit_parser.rs b/crates/agent/src/edit_agent/edit_parser.rs index 8411171ba4ea491d2603014a0715ce471b34e36f..425bf93efff115d4daef380e3f82abcdb8c0746f 100644 --- a/crates/agent/src/edit_agent/edit_parser.rs +++ b/crates/agent/src/edit_agent/edit_parser.rs @@ -13,7 +13,15 @@ const EDITS_END_TAG: &str = ""; const SEARCH_MARKER: &str = "<<<<<<< SEARCH"; const SEPARATOR_MARKER: &str = "======="; const REPLACE_MARKER: &str = ">>>>>>> REPLACE"; -const END_TAGS: [&str; 3] = [OLD_TEXT_END_TAG, NEW_TEXT_END_TAG, EDITS_END_TAG]; +const SONNET_PARAMETER_INVOKE_1: &str = "\n"; +const SONNET_PARAMETER_INVOKE_2: &str = ""; +const END_TAGS: [&str; 5] = [ + OLD_TEXT_END_TAG, + NEW_TEXT_END_TAG, + EDITS_END_TAG, + SONNET_PARAMETER_INVOKE_1, // Remove this after switching to streaming tool call + SONNET_PARAMETER_INVOKE_2, +]; #[derive(Debug)] pub enum EditParserEvent { @@ -547,6 +555,37 @@ mod tests { ); } + #[gpui::test(iterations = 1000)] + fn test_xml_edits_with_closing_parameter_invoke(mut rng: StdRng) { + // This case is a regression with Claude Sonnet 4.5. + // Sometimes Sonnet thinks that it's doing a tool call + // and closes its response with '' + // instead of properly closing + + let mut parser = EditParser::new(EditFormat::XmlTags); + assert_eq!( + parse_random_chunks( + indoc! {" + some textupdated text + "}, + &mut parser, + &mut rng + ), + vec![Edit { + old_text: "some text".to_string(), + new_text: "updated text".to_string(), + line_hint: None, + },] + ); + assert_eq!( + parser.finish(), + EditParserMetrics { + tags: 2, + mismatched_tags: 1 + } + ); + } + #[gpui::test(iterations = 1000)] fn test_xml_nested_tags(mut rng: StdRng) { let mut parser = EditParser::new(EditFormat::XmlTags); @@ -1035,6 +1074,11 @@ mod tests { last_ix = chunk_ix; } + if new_text.is_some() { + pending_edit.new_text = new_text.take().unwrap(); + edits.push(pending_edit); + } + edits } }