Pull requests / #700
#700 serve: a <tool_call> named in prose before the real call is content, not a malformed call
closed · @T-Crypt · 0 commentaires · Sur GitHub
Description
## What
`OutputParser.feed` treated every `<tool_call>` in the content as the start of a call. A reply that names the tag in its prose before making the real call, for example
```
I will use the `<tool_call>` format now.
<tool_call>
<function=write>
...
```
took everything after the first tag as the call body, and `parse_tool_call` raised `ValueError("malformed tool call: ...")`, which ended the request.
A call is the tag followed (after whitespace) by `<function=`, which is how `serve/chat_template.jinja` writes it. With this change, a `<tool_call>` followed by anything else is content. While streaming, the tag is held until its follower arrives (the same way a partial tag is held), so streamed and whole output give the same events. A tag at the very end of the output is flushed as content by `finish()`, as before.
## How it was found
I was porting tolerant tool-call recovery from a Qwen engine (ninfer's `--tolerant-tool-calls`) and checked its three recovery cases against Strata's parser:
| case | before |
| --- | --- |
| a value that quotes `</parameter>` / `<tool_call>` | handled (#210) |
| prose after the calls | handled |
| `<tool_call>` named in prose before the real call | `ValueError`, request ends |
Only the third needed a change.
## Tests
- New `ToolCallTagInProse` in `serve/test_server.py`: three prose forms (backticks, bare, two tags), `stream_tools` on and off, steps 1 / 7 / whole; plus a tag alone at the end of the output.
- On `main` the new class fails (3 failures, 18 errors across subtests); with the change it passes.
- `python -m unittest serve.test_server`: 120 tests OK (Linux, Python 3.14).
- Not measured: a live model producing this output. The case is from ninfer's tool-call tests and agentic sessions, not from a Strata log.
Sur le site
Liens install, modèles, releases.