Skip incomplete stream roots during tool extraction
Reject response snapshots without complete object boundaries before invoking the dirty JSON root scanner. This avoids repeated scans of incomplete streamed prefixes while preserving strict tool-call acceptance and Context Doctor repair behavior.
Alessandro committed
Aug 27, 2026 at 00:45 UTC
a9e39ac04a3959c6207be6e25adad1454a3c16d6
3 files changed
+23
helpers/extract_tools.py
+3
@@ -25,6 +25,9 @@ def extract_tool_request(content: str) -> dict[str, Any] | None:
25
return None
26
27
content = content.strip()
28
+ if not (content.startswith("{") and content.endswith("}")):
29
+ return None
30
+
31
root = extract_json_root_string(content)
32
if root != content:
33
return None
helpers/extract_tools.py.dox.md
+1
@@ -29,6 +29,7 @@
29
- Dirty parsing scans complete JSON object roots in prose and prefers the first object that normalizes as a valid tool request for permissive repair and legacy callers.
30
Normalization accepts canonical `tool_name`/`tool_args`, legacy `tool`/`args`, native `type="function"` `name`/`parameters`, and a single-item `actions` wrapper; malformed or multi-action wrappers are rejected.
31
- `extract_tool_request` is the execution boundary: it accepts a request only when the complete trimmed content is one valid tool object. Plain text, ordinary JSON, and tool-shaped JSON embedded in prose remain final text.
32
+- `extract_tool_request` rejects content that does not have complete object boundaries before invoking the dirty root scanner. This keeps incomplete streaming prefixes cheap without changing which complete canonical tool objects are accepted.
33
- `is_misformatted_tool_request` identifies a tool request wrapped in a JSON code fence, concatenated complete roots containing tool intent, or a complete Agent Zero envelope that starts with `thoughts` and whose dirty parser has absorbed `headline`, `tool_name`, and `tool_args` into that list. It routes that output to the existing repair prompt without executing it.
34
- Streaming tool snapshots use `extract_tool_request`; the permissive root helpers remain available for repair and legacy callers, not tool execution.
35
- Root extraction ignores objects nested inside an open parent object, so streamed wrapper tools such as `parallel` cannot stop early on the first nested `tool_calls` item.
tests/test_stream_tool_early_stop.py
+19
@@ -87,6 +87,25 @@ def test_extract_json_root_string_returns_canonical_snapshot():
87
assert extract_tools.extract_json_root_string('[{"tool_name":"response"}]') is None
88
89
90
+@pytest.mark.parametrize(
91
+ "content",
92
+ [
93
+ '{"tool_name":"response","tool_args":{"text":"partial"',
94
+ 'prefix {"tool_name":"response","tool_args":{}}',
95
+ '[{"tool_name":"response","tool_args":{}}]',
96
+ '```json\n{"tool_name":"response","tool_args":{}}\n```',
97
+ ],
98
+)
99
+def test_extract_tool_request_skips_noncanonical_boundaries(monkeypatch, content):
100
+ monkeypatch.setattr(
101
+ extract_tools,
102
+ "extract_json_root_string",
103
+ lambda _content: pytest.fail("noncanonical content reached the root scanner"),
104
+ )
105
+
106
+ assert extract_tools.extract_tool_request(content) is None
107
+
108
+
109
def test_json_parse_dirty_prefers_valid_tool_request_after_preamble_object():
110
text = (
111
'I will call the tool after this note {"note":"not the tool"}.\n'