diff --git a/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs b/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs index ea1a660cce..dc9859d001 100644 --- a/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs +++ b/rust/src/parser/src/tool/deepseek_json/deepseek_v3.rs @@ -238,6 +238,9 @@ mod tests { let error = parser.parse_chunk(&input).unwrap_err(); - expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string()); + expect![[ + r#"tool parser parsing failed: near "tool<|tool▁sep|>get_weather\n```json\n{}": "# + ]] + .assert_eq(&error.to_report_string()); } } diff --git a/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs b/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs index cf2ea19628..088e6d53db 100644 --- a/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs +++ b/rust/src/parser/src/tool/deepseek_json/deepseek_v31.rs @@ -242,6 +242,7 @@ mod tests { let error = parser.parse_chunk(&input).unwrap_err(); - expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string()); + expect![[r#"tool parser parsing failed: near "<|tool▁sep|>{}": "#]] + .assert_eq(&error.to_report_string()); } } diff --git a/rust/src/parser/src/tool/json/granite4.rs b/rust/src/parser/src/tool/json/granite4.rs index 4989578011..0f7bb69021 100644 --- a/rust/src/parser/src/tool/json/granite4.rs +++ b/rust/src/parser/src/tool/json/granite4.rs @@ -540,8 +540,10 @@ mod tests { .parse_chunk(r#"{"name":"f","arguments":42}"#) .unwrap_err(); - expect!["tool parser parsing failed: invalid Granite4 arguments"] - .assert_eq(&error.to_report_string()); + expect![[ + r#"tool parser parsing failed: near "42}": invalid Granite4 arguments"# + ]] + .assert_eq(&error.to_report_string()); } #[test] diff --git a/rust/src/parser/src/tool/json/internlm2.rs b/rust/src/parser/src/tool/json/internlm2.rs index aae3b9f6a0..25bc65911a 100644 --- a/rust/src/parser/src/tool/json/internlm2.rs +++ b/rust/src/parser/src/tool/json/internlm2.rs @@ -335,7 +335,7 @@ mod tests { let error = parser.parse_chunk(&input).unwrap_err(); expect![[r#" - tool parser parsing failed: invalid InternLM2 + tool parser parsing failed: near "{\"name\":\"get_weather\",\"params\":{\"location\":\"Tokyo\"}}<|action_end|>": invalid InternLM2 expected `parameters`, `arguments`"#]] .assert_eq(&error.to_report_string()); } diff --git a/rust/src/parser/src/tool/json/llama.rs b/rust/src/parser/src/tool/json/llama.rs index b736f27e30..9f30bbe84a 100644 --- a/rust/src/parser/src/tool/json/llama.rs +++ b/rust/src/parser/src/tool/json/llama.rs @@ -336,7 +336,7 @@ mod tests { .unwrap_err(); expect![[r#" - tool parser parsing failed: invalid Llama JSON + tool parser parsing failed: near "{\"name\":\"get_weather\",\"arguments\":{\"location\":\"Tokyo\"}}": invalid Llama JSON expected `parameters`"#]] .assert_eq(&error.to_report_string()); } @@ -474,7 +474,7 @@ mod tests { let error = parser.parse_chunk(r#"{"parameters":{},"name":"get_weather"}"#).unwrap_err(); expect![[r#" - tool parser parsing failed: invalid Llama JSON + tool parser parsing failed: near "{\"parameters\":{},\"name\":\"get_weather\"}": invalid Llama JSON expected `name`"#]] .assert_eq(&error.to_report_string()); } @@ -489,7 +489,7 @@ mod tests { )) .unwrap_err(); - expect!["tool parser parsing failed: invalid Llama JSON"] + expect![[r#"tool parser parsing failed: near " trailing": invalid Llama JSON"#]] .assert_eq(&error.to_report_string()); } } diff --git a/rust/src/parser/src/tool/json/mistral.rs b/rust/src/parser/src/tool/json/mistral.rs index 8a20b4db7b..ab99afc7ef 100644 --- a/rust/src/parser/src/tool/json/mistral.rs +++ b/rust/src/parser/src/tool/json/mistral.rs @@ -240,7 +240,7 @@ mod tests { .unwrap_err(); expect![[r#" - tool parser parsing failed: invalid Mistral + tool parser parsing failed: near "{\"arguments\":{},\"name\":\"get_weather\"}]": invalid Mistral expected `name`"#]] .assert_eq(&error.to_report_string()); } diff --git a/rust/src/parser/src/tool/json/qwen.rs b/rust/src/parser/src/tool/json/qwen.rs index 7fa53c9007..a69798f785 100644 --- a/rust/src/parser/src/tool/json/qwen.rs +++ b/rust/src/parser/src/tool/json/qwen.rs @@ -280,7 +280,7 @@ mod tests { .unwrap_err(); expect![[r#" - tool parser parsing failed: invalid Qwen XML + tool parser parsing failed: near "{\"arguments\":{},\"name\":\"get_weather\"}\n": invalid Qwen XML expected `name`"#]] .assert_eq(&error.to_report_string()); } diff --git a/rust/src/parser/src/tool/kimi_k2.rs b/rust/src/parser/src/tool/kimi_k2.rs index b692c1ec59..e4730f96bc 100644 --- a/rust/src/parser/src/tool/kimi_k2.rs +++ b/rust/src/parser/src/tool/kimi_k2.rs @@ -594,6 +594,9 @@ mod tests { let error = parser.parse_chunk(&input).unwrap_err(); - expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string()); + expect![[ + r#"tool parser parsing failed: near "get_weather<|tool_call_argument_begin|>{}": "# + ]] + .assert_eq(&error.to_report_string()); } } diff --git a/rust/src/parser/src/tool/minimax_m2.rs b/rust/src/parser/src/tool/minimax_m2.rs index 5c5411775a..c56b4bd1f6 100644 --- a/rust/src/parser/src/tool/minimax_m2.rs +++ b/rust/src/parser/src/tool/minimax_m2.rs @@ -593,6 +593,7 @@ mod tests { let mut parser = MinimaxM2ToolParser::new(&test_tools()); let error = parser.parse_chunk("").unwrap_err(); - expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string()); + expect![[r#"tool parser parsing failed: near "": "#]] + .assert_eq(&error.to_report_string()); } } diff --git a/rust/src/parser/src/tool/minimax_m3.rs b/rust/src/parser/src/tool/minimax_m3.rs index a1ab375b73..e2a0cada2c 100644 --- a/rust/src/parser/src/tool/minimax_m3.rs +++ b/rust/src/parser/src/tool/minimax_m3.rs @@ -878,7 +878,10 @@ mod tests { )) .unwrap_err(); - expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string()); + expect![[ + r#"tool parser parsing failed: near "]<]minimax[>[]<]minimax[>[": "# + ]] + .assert_eq(&error.to_report_string()); } #[test] diff --git a/rust/src/parser/src/tool/qwen_coder.rs b/rust/src/parser/src/tool/qwen_coder.rs index d67a5c42f1..c2e1b0c794 100644 --- a/rust/src/parser/src/tool/qwen_coder.rs +++ b/rust/src/parser/src/tool/qwen_coder.rs @@ -685,7 +685,8 @@ mod tests { let mut parser = Qwen3CoderToolParser::new(&test_tools()); let error = parser.parse_chunk("\n\n").unwrap_err(); - expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string()); + expect![[r#"tool parser parsing failed: near "\n\n": "#]] + .assert_eq(&error.to_report_string()); } #[test] @@ -697,7 +698,7 @@ mod tests { ) .unwrap_err(); - expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string()); + expect![[r#"tool parser parsing failed: near "\n\nSF\n": "#]].assert_eq(&error.to_report_string()); } #[test] diff --git a/rust/src/parser/src/utils.rs b/rust/src/parser/src/utils.rs index bd8f6f48e9..c692a57c88 100644 --- a/rust/src/parser/src/utils.rs +++ b/rust/src/parser/src/utils.rs @@ -395,9 +395,9 @@ pub fn parse_buffered_event( Ok(event) => event, Err(ErrMode::Incomplete(_)) => return Ok(None), Err(ErrMode::Backtrack(e) | ErrMode::Cut(e)) => { - // TODO: enrich context for error reporting + let snippet = buffer.char_indices().nth(80).map_or(buffer, |(i, _)| &buffer[..i]); return Err(ToolParserError::ParsingFailed { - message: e.to_string(), + message: format!("near {snippet:?}: {e}"), }); } }; @@ -423,8 +423,9 @@ mod tests { use winnow::stream::{Offset, Partial, Stream}; use super::{ - JsonObjectScanState, JsonStringScanState, MarkerScanState, json_str, partial_prefix_len, - safe_text_len, safe_text_len_mul, take_json_object, take_json_string, take_until_marker, + JsonObjectScanState, JsonStringScanState, MarkerScanState, json_str, parse_buffered_event, + partial_prefix_len, safe_text_len, safe_text_len_mul, take_json_object, take_json_string, + take_until_marker, }; #[test] @@ -832,4 +833,27 @@ mod tests { assert!(matches!(error, ErrMode::Incomplete(_))); } + + #[test] + fn parse_buffered_event_error_includes_input_snippet() { + let result = parse_buffered_event(" {\"x\":1}", |input| { + take_json_object(input, &mut JsonObjectScanState::default()) + }); + let err = result.unwrap_err().to_string(); + assert!(err.contains("near \""), "error must include snippet"); + } + + #[test] + fn parse_buffered_event_error_truncates_long_input() { + let long_input = format!(" {}", "x".repeat(100)); + let result = parse_buffered_event(&long_input, |input| { + take_json_object(input, &mut JsonObjectScanState::default()) + }); + let err = result.unwrap_err().to_string(); + assert!(err.contains("near \""), "error must include snippet"); + assert!( + !err.contains(&long_input), + "snippet must be truncated for long input" + ); + } }