Repository navigation
fix(tokenizers): don't end a Qwen3-Coder function at </function> inside a parameter value - #9097
pcbeingused333 wants to merge 2 commits into
Conversation
…de a parameter value The function extraction used a non-greedy regex that stopped at the first </function>, even inside a complete <parameter>...</parameter> block, which truncated the call and dropped later parameters. Reuse the parameter-aware scan already used for </tool_call>. Fixes NVIDIA#8802 Signed-off-by: Alex Castillo <alex.castillog33@gmail.com>
|
This PR has been automatically converted to draft because all PRs must start as drafts. When you are ready for review, click Ready for Review to begin the review process. This will:
See the contribution guide for more details. |
| body_start = start_match.end() | ||
| # Like `</tool_call>`, a `</function>` inside a complete | ||
| # <parameter>...</parameter> block is part of the value. | ||
| end_index = self._find_tool_call_end(tool_call, body_start, self._function_end_re) |
There was a problem hiding this comment.
Using _find_tool_call_end for every </function> makes long, otherwise valid function payloads quadratic in their parameter count: inside the loop, end_re.search(text, pos) scans from each parameter to the final closing tag. With '<function=fn>' + '<parameter=p>x</parameter>'*N + '</function>', I measured ~0.006 s at N=1,000, ~0.022 s at 2,000, ~0.086 s at 4,000 and ~0.34 s at 8,000 (roughly 4x per doubling). This undermines the claimed linear scan and can stall larger generated calls. Could we locate the candidate end tag once and advance the parameter scan without re-searching the same suffix, with a scaling regression?
There was a problem hiding this comment.
Thanks for the update, @pcbeingused333. I rechecked c7ffa3e using the exact _find_tool_call_end method in an isolated Python harness. A 2,000-parameter payload took about 1.7 ms and 16,000 parameters about 5.8 ms (best of three), and an embedded </function> stayed inside its parameter value. That resolves the repeated-suffix-scan issue I raised. Thanks for adding the scaling regression. I haven't run the full Megatron suite.
Reuse the candidate end tag across parameters instead of re-searching the same suffix after every parameter, which made long calls quadratic in their parameter count. Add a scaling regression test. Signed-off-by: Alex Castillo <alex.castillog33@gmail.com>
|
Thanks for the review. This comment was drafted with Claude Code. |
Fixes #8802
What does this PR do?
Qwen3CoderToolParserno longer cuts a tool call at a</function>that sits inside a<parameter>…</parameter>value.Root cause
_get_function_callsextracted function bodies with<function=(.*?)</function>, which stops at the first</function>even when it is part of a parameter value (e.g. the model writes a snippet containing</function>). The call was truncated and every later parameter was dropped.</tool_call>inside a parameter was already handled by_find_tool_call_end;</function>was not.Fix
_find_tool_call_endnow takes the closing-tag pattern, and function extraction reuses that parameter-aware scan for</function>. Unclosed functions still take the rest of the text, as before.Test
Added
test_qwen3_coder_function_end_inside_value_does_not_terminate_the_function(fails before, passes after); the existing Qwen3-Coder/tool-parser tests intests/unit_tests/tokenizers/test_text_parsers.pystill pass. With the issue's repro, both</function>and</function><function=other></function>now yield a singlewrite_filecall withcontentandpathintact.Linked issue: Fixes #8802
Code provenance
No code copied from other repositories.
Contribution process
Pre-checks
Disclosure: this change was prepared with the assistance of an AI coding agent (Claude Code).