[Rust Frontend] Add error context in tool parser failures (#46512)

Signed-off-by: Maria Guevara <kawaiiplush14@gmail.com>
This commit is contained in:
Maria Guevara
2026-07-01 12:48:55 +08:00
committed by GitHub
parent 9a08a5118e
commit aeb35b90f0
12 changed files with 57 additions and 19 deletions
@@ -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());
}
}
@@ -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());
}
}
+4 -2
View File
@@ -540,8 +540,10 @@ mod tests {
.parse_chunk(r#"<tool_call>{"name":"f","arguments":42}</tool_call>"#)
.unwrap_err();
expect!["tool parser parsing failed: invalid Granite4 arguments"]
.assert_eq(&error.to_report_string());
expect![[
r#"tool parser parsing failed: near "42}</tool_call>": invalid Granite4 arguments"#
]]
.assert_eq(&error.to_report_string());
}
#[test]
+1 -1
View File
@@ -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());
}
+3 -3
View File
@@ -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());
}
}
+1 -1
View File
@@ -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());
}
+1 -1
View File
@@ -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</tool_call>": invalid Qwen XML
expected `name`"#]]
.assert_eq(&error.to_report_string());
}
+4 -1
View File
@@ -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());
}
}
+2 -1
View File
@@ -593,6 +593,7 @@ mod tests {
let mut parser = MinimaxM2ToolParser::new(&test_tools());
let error = parser.parse_chunk("<minimax:tool_call><bad></minimax:tool_call>").unwrap_err();
expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string());
expect![[r#"tool parser parsing failed: near "<bad></minimax:tool_call>": "#]]
.assert_eq(&error.to_report_string());
}
}
+4 -1
View File
@@ -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[>[<bad>]<]minimax[>[</tool_call>": "#
]]
.assert_eq(&error.to_report_string());
}
#[test]
+3 -2
View File
@@ -685,7 +685,8 @@ mod tests {
let mut parser = Qwen3CoderToolParser::new(&test_tools());
let error = parser.parse_chunk("<tool_call>\n<bad>\n</tool_call>").unwrap_err();
expect!["tool parser parsing failed: "].assert_eq(&error.to_report_string());
expect![[r#"tool parser parsing failed: near "\n<bad>\n</tool_call>": "#]]
.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<function=get_weather>\n<parameter=location>SF</function>\n</tool_call>": "#]].assert_eq(&error.to_report_string());
}
#[test]
+28 -4
View File
@@ -395,9 +395,9 @@ pub fn parse_buffered_event<E>(
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"
);
}
}