Fix inline JSON being incorrectly detected as tool call

The bug was caused by mark_tool_calls_consumed() being called after
displaying each chunk, which advanced last_consumed_position to the
end of the current buffer. When the next chunk arrived with JSON,
the unchecked_buffer started at position 0 of the slice, causing
is_on_own_line() to return true (position 0 is always "on its own line").

Removed the problematic mark_tool_calls_consumed() call from the
"no tool executed" branch. The remaining call after actual tool
execution is correct and necessary.

Added integration test that verifies inline JSON in prose is not
detected as a tool call.
This commit is contained in:
Dhanji R. Prasanna
2026-01-19 14:35:01 +05:30
parent 292a3aa48d
commit 5caa101b84
4 changed files with 311 additions and 7 deletions

View File

@@ -432,3 +432,53 @@ mod streaming_repro {
assert_eq!(tools[0].tool, "shell");
}
}
/// Test that inline JSON is not detected even when stream finishes
/// This tests the try_parse_all_json_tool_calls_from_buffer path
#[test]
fn test_inline_json_not_detected_at_stream_end() {
use g3_core::StreamingToolParser;
use g3_providers::CompletionChunk;
fn chunk(content: &str, finished: bool) -> CompletionChunk {
CompletionChunk {
content: content.to_string(),
finished,
tool_calls: None,
usage: None,
stop_reason: if finished { Some("end_turn".to_string()) } else { None },
tool_call_streaming: None,
}
}
let mut parser = StreamingToolParser::new();
// Send chunks exactly as MockProvider would
let tools = parser.process_chunk(&chunk("To run a command, you can use the format ", false));
assert!(tools.is_empty(), "Chunk 1: no tools");
let tools = parser.process_chunk(&chunk(r#"{"tool": "shell", "args": {"command": "ls"}}"#, false));
assert!(tools.is_empty(), "Chunk 2: inline JSON should not trigger tool detection");
let tools = parser.process_chunk(&chunk(" in your request. ", false));
assert!(tools.is_empty(), "Chunk 3: no tools");
let tools = parser.process_chunk(&chunk("Let me know if you need help!", false));
assert!(tools.is_empty(), "Chunk 4: no tools");
// Finish chunk - this triggers try_parse_all_json_tool_calls_from_buffer
let tools = parser.process_chunk(&chunk("", true));
assert!(
tools.is_empty(),
"Finish chunk: inline JSON should NOT be detected as tool call. Found: {:?}",
tools.iter().map(|t| &t.tool).collect::<Vec<_>>()
);
// Verify the full buffer content
let buffer = parser.get_text_content();
assert!(
buffer.contains(r#"{"tool": "shell"#),
"Buffer should contain the inline JSON: {}",
buffer
);
}