fix(parsing): vLLM-parity type ladder for XML tool-call arg coercion#52
Open
hallerite wants to merge 1 commit into
Open
fix(parsing): vLLM-parity type ladder for XML tool-call arg coercion#52hallerite wants to merge 1 commit into
hallerite wants to merge 1 commit into
Conversation
Renderers' Qwen3.5/GLM/MiniMax/Laguna XML parsers flagged
``<parameter=x>True</parameter>`` as INVALID_JSON for boolean params
because ``json.loads("True")`` fails. Both SGLang's
``Qwen3CoderDetector`` and vLLM's ``Qwen3CoderToolParser`` accept it via
``param_value.lower() == "true"`` — so Pythonic literals the model
freely emits round-trip cleanly at inference but came back malformed
through renderers.
Replaces the string-or-``json.loads`` dispatch with the full
``_convert_param_value`` ladder shared by both reference parsers:
case-insensitive ``null``, ``int()`` / ``float()`` for numeric families
with int demotion, case-folded bool, ``json.loads`` → ``ast.literal_eval``
for objects/arrays/anyOf, ``ast.literal_eval`` catch-all. INVALID_JSON
remains as the verifier / RL-loss signal for values that had to
degenerate (e.g. ``yes`` for bool, ``abc`` for int).
One deliberate deviation from vLLM/SGLang: declared ``string`` types
preserve ``"null"`` verbatim instead of coercing to Python ``None``, so
the existing ``test_tool_arg_type_preservation`` string-verbatim
contract still holds.
Fixes #47.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #47. Renderers' XML-style parsers (Qwen3.5, GLM, MiniMax, Laguna) flagged
<parameter=x>True</parameter>asINVALID_JSONfor boolean params becausejson.loads("True")fails — even though both SGLang'sQwen3CoderDetectorand vLLM'sQwen3CoderToolParseraccept Pythonic literals via case-folded comparison. Models freely emitTrue/Falseat inference because the reference parsers normalize them, but the same outputs came back malformed through renderers.This swaps
_coerce_arg_valuefrom a string-or-json.loadsdispatch to the full_convert_param_valueladder both reference parsers share:json.loads, fallback to raw text +INVALID_JSON(historical behavior preserved)string/str/text/varchar/char/enumint/uint/long/short/unsignedint(text), fall back to string +INVALID_JSONnum/floatfloat(text)with SGLang's source-string int-demotion heuristic, fall back to string +INVALID_JSONboolean/bool/binarytext.lower() == "true"; non-true/false→False+INVALID_JSONobject/array/dict*/list*/anyOfjson.loads, thenast.literal_evalfallback (Python literals like{'k': 1}), then string +INVALID_JSONast.literal_eval, fall back to string +INVALID_JSONnullliteral (any case)None— except for declaredstring-family types (see deviation below)INVALID_JSONis preserved as the verifier / RL-loss signal whenever the value had to degenerate (e.g.yesfor a bool,abcfor an int).Deliberate deviation from vLLM/SGLang
Both reference parsers null-coerce
"null"before checking type, so a string-typed arg of"null"returns PythonNone. Renderers keeps"null"verbatim fortype: "string"so the existingtests/test_tool_arg_type_preservation.py::test_string_arg_preserves_type[*-string-null]contract holds across all five XML renderers. The XML wire format already can't distinguish the string"null"from JSON null, but when the schema explicitly saystype: "string"we honour it.Reference parser sources
python/sglang/srt/function_call/qwen3_coder_detector.pyvllm/tool_parsers/qwen3coder_tool_parser.py::_convert_param_valueThe two are functionally near-identical for this code path; vLLM is slightly more permissive (extra
anyOf → objectbranch). Renderers now matches vLLM's superset, with the one string-nulldeviation noted above.Test plan
pytest tests/test_arg_coercion.py— 43 new unit tests cover every branch of the ladder, including the issue'sTrue/Falseregression through fullparse_qwen35round-trip.pytest tests/test_tool_arg_type_preservation.py— 30/30 string-preservation cases pass across all five XML renderers (thestring-nullcase still round-trips verbatim).🤖 Generated with Claude Code