Repository navigation
[Bugfix][Parser] Preserve quoted Qwen3 tool markup in reasoning - #59821
denelbjohn18 wants to merge 2 commits into
Conversation
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
|
Hi @aarnphm, @sfeng33, and @bbrowning - when you have a chance, could you take a look at this PR? I added the regression tests and checked the existing parser tests. I'm happy to make changes if needed. |
|
This pull request has merge conflicts that must be resolved before it can be |
…oning Related to vllm-project#58147. Tests drafted with AI assistance (Cursor agent). Signed-off-by: Denel John <denelbjohn18@gmail.com>
Defer tool-start parsing in Qwen3 reasoning until the parser sees an explicit reasoning close or the stream ends. Preserve tool-shaped examples inside explicitly closed reasoning, while keeping implicit-end tool calls working through the reasoning adapter. Related to vllm-project#58147. Signed-off-by: Denel John <denelbjohn18@gmail.com>
6df47e8 to
8a9dfb8
Compare
|
An independent repro, plus one path this does not cover yet. On Qwen3.6-27B and Qwen3.6-35B-A3B (3 tool-call cases x 40 samples, T=0.6, The gap: this PR defers the transition in the streaming parser, but AI assistance was used in preparing this comment. |
|
@thediymaker thanks for the detailed repro and for catching the structured-output path. I checked it locally on CPU with an external probe using the real qwen_3_coder grammar through StructuredOutputManager. With tool_choice required or a named tool, the quoted <tool_call> flips reasoning_ended and starts grammar enforcement. This also broke the case with a quoted opener followed by and a real call. A genuine call without passed the same probe. It relies on that same <tool_call> reasoning-end id for grammar enforcement. Dropping the id while deferral applies would fix the quoted cases, but remove grammar enforcement from that currently working path. Before I make that change, is the intended behaviour to require before grammar enforcement starts when deferral applies, even for required/named calls? I'd appreciate a maintainer's read too, so I don't silently change the existing behaviour. These checks were local, with no model sampling. I haven't pushed a change yet. Cursor helped with the analysis and probe. |
|
Our read, for what it is worth (a maintainer should make the call): yes, when deferral applies, requiring The cost is narrower than it looks. With thinking disabled, the prompt already carries a closed think block, so reasoning has ended before the first token and enforcement starts as today. The path that loses enforcement is a model that emits a real call while still thinking and never closes For the Qwen3.6 models we measured, which close AI assistance was used in preparing this comment. |
|
This pull request has merge conflicts that must be resolved before it can be |
Overview
Fixes #58147. A
<tool_call>block quoted as an example inside explicitly closed Qwen3 reasoning was executed as a real tool call. The parser now defers a tool opener seen during reasoning until</think>or the end of the stream.Claims
</think>) keep working, including through the reasoning adapter.Validation
python -m pytest tests/parser/engine/: 6,030 passed across 23 files on the branch rebased onto main at 1a5b187.tests/parser,tests/reasoningandtests/tool_parsers: about 7,870 passed, 0 failures.tests/tool_parsers/test_llama3_json_tool_parser.pycould not run locally because it downloads a gated Hugging Face checkpoint (401 without a token).tests/parser/engine/test_qwen3.pyfail on main without the fix (10 failed, 76 passed) and pass with it (86 passed). They cover quoted markup in streaming reasoning, explicit close, a later real tool call, implicit-end calls, adapter finish with complete and partial spans, split markers, multiple quoted spans, empty spans, reset, and skip-tool-parsing mode.git diff --checkpass.Reproduce the bug and check the fix:
python -m pytest tests/parser/engine/test_qwen3.py tests/parser/engine/test_parser_engine.py -q(214 passed).Details
Root cause: the parser fired the implicit (REASONING, TOOL_START) transition on sight, so a quoted example inside reasoning became a real call. Design: hold the opener and its span while reasoning is open. An explicit
</think>returns the span as reasoning; reaching stream end replays it through the existing implicit-end path. The hold is an opt-in config flag, default off, enabled only for base Qwen3 with thinking on and a prompt inside an open think block.In the issue thread, I reproduced and diagnosed the behavior. ArtyomITA explored an environment-flag alternative, then abandoned it because it broke seven implicit-end tests and the use case in #39056. #57553 is related follow-up work for Qwen3-derived parsers.
Tradeoffs: for Qwen3 thinking requests, implicit-end tool calls resolve at stream end rather than incrementally; the held span stays buffered until resolution; a small finish option (
resolve_deferred_tool_start) was added to the parser engine so the reasoning adapter can resolve a held span at stream end. It is an internal parser-engine hook, not a serving/API option.AI-assisted with Cursor; all changes reviewed and verified by me.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.