Skip to content

fix: Qwen3 XML streaming tool-call boundary fix - #552

Open
jkyamog wants to merge 1 commit into
1CatAI:mainfrom
jkyamog:fix/qwen-streaming-tools
Open

fix: Qwen3 XML streaming tool-call boundary fix#552
jkyamog wants to merge 1 commit into
1CatAI:mainfrom
jkyamog:fix/qwen-streaming-tools

Conversation

@jkyamog

@jkyamog jkyamog commented Sep 7, 2026

Copy link
Copy Markdown

Purpose

Fix phantom empty tool calls and malformed arguments in streamed Qwen3 XML
tool calls. When a parser chunk contained a closing tag followed by a new call
opening, the fallback closing logic could act on the newly opened call in the
same chunk. That produced the observed {}/empty-name calls and extra closing
braces in tool arguments.

The change captures the call ID at chunk entry and restricts fallback closure
to that call; the function fallback also requires an open function. It keeps the
native Qwen3 XML dialect and templates unchanged.

Test Plan

Run from the repository root in the parser/test container:

pytest -q tests/tool_parsers/test_qwen3xml_chunk_boundaries.py \
  tests/tool_parsers/test_qwen3xml_tool_parser.py
ruff check vllm/tool_parsers/qwen3xml_tool_parser.py \
  tests/tool_parsers/test_qwen3xml_chunk_boundaries.py
git diff --check origin/main...HEAD

Runtime contract checks:

python vllm/tmp/probe_stream_contract.py
node vllm/tmp/probe_pi_stream.mjs qwen-3.8-flash

Verify streamed and non-streamed responses produce valid JSON tool arguments,
no empty-name calls, preserved reasoning when enabled, and the exact requested
tool sequence read(path=/tmp/a) followed by bash(command=pwd).

Test Result

  • The expanded parser suite passed all 535 cases, including fixed-width,
    single-split, seeded-irregular chunk boundaries, public
    streaming/non-streaming agreement, empty arguments, and multiline values.
  • Ruff checks and changed-file format checks passed.
  • Repeated live Ark/Qwen streams produced exactly the requested read and
    bash calls with valid JSON, preserved reasoning, and
    finish_reason=tool_calls.
  • The Pi 0.84.4 adapter returned stopReason=toolUse with the same two calls;
    no generated tool request was executed.
  • The final combined-image NVIDIA Flash-Next stream reproduced the same
    parser contract, showing that the fix is independent of the Ark runtime.

The latest-upstream stack has been rebuilt locally alongside the NVIDIA loader;
rerun the focused suite and final live smoke after selecting that image before
submitting the PR.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Once the PR is approved and ready to go, your PR reviewer(s) can run CI to test the changes comprehensively before merging.

To run CI, PR reviewers can either: Add ready label to the PR or enable auto-merge.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: 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.

🚀

@jkyamog
jkyamog force-pushed the fix/qwen-streaming-tools branch from 8ffc8d0 to ae22169 Compare September 7, 2026 13:03
@areslp

areslp commented Sep 8, 2026

Copy link
Copy Markdown

Independent validation: this fixes a real OpenClaw tool-calling failure on our 1Cat-vLLM 1.5.0 deployment. Thank you for the patch; I would welcome its inclusion in a release.

We tested the parser change at ae22169a953064cdec8befb5a09630674247ed67 without additional parser modifications, using Qwen3.8-Flash-Next-NVFP4 on 4× V100, TP4, MTP4, and Python 3.12.13.

Before the fix: the model generated two complete XML tool calls, but the streamed response contained three slots: a valid call, an empty unnamed call, and a call with an extra } in its JSON arguments. OpenClaw rejected it with Provider returned an incomplete or malformed tool call. The response had a complete [DONE] terminator and no transport error.

Independent results:

  • Replaying the captured tool-call text at fixed chunk widths 1–200: the installed parser failed semantic validation at 113/200 widths, with phantom slots at 29 widths. With this patch: 200/200 passed, no phantom slots. The outer streaming parser also passed 200/200 widths.
  • Nine additional chunk strategies exercised through both the inner and outer parsers: 18/18 passed with the patch.
  • Our separate synthetic adjacent-call regression tests: 201 failed / 200 passed before the fix. With this PR's boundary tests plus ours: 936 passed, using pytest 8.4.2. Ruff checks and git diff --check also passed.
  • After loading the exact patched parser via --tool-parser-plugin, two streaming requests and one non-streaming request through OpenClaw's installed model transport each returned exactly the two requested synthetic tool calls, valid JSON, correct typed argument values, and finish_reason=tool_calls. No generated tool was executed.

The replay uses actual captured text with deterministic synthetic chunk boundaries, not a reconstruction of the original server chunk timing. One segmentation reproduced the exact malformed argument hashes seen on the wire. The live checks are focused protocol smoke tests, not a long-term reliability claim. Private conversation contents are not included here.

The repair is now deployed in our environment. This investigation and validation report were AI-assisted. These results support the value of fixing the legacy parser still shipped in 1Cat 1.5.0, even though upstream vLLM has moved to a replacement parser engine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants