@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 05ac829629
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 2, 17:01 UTC

fix: parse Qwen3 XML JSON arguments first (#43243)

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Co-authored-by: Flora Feng <4florafeng@gmail.com>

Yufeng He committed May 28, 2026 at 11:35 UTC 05ac8296298258006c847610e8ee918e1dbbe591
2 files changed +66 -2
tests/tool_parsers/test_qwen3coder_tool_parser.py
+62 -1
@@ -24,7 +24,10 @@ from vllm.tokenizers.detokenizer_utils import detokenize_incrementally
24 from vllm.tool_parsers.qwen3coder_tool_parser import (
25 Qwen3CoderToolParser,
26 )
27 -from vllm.tool_parsers.qwen3xml_tool_parser import Qwen3XMLToolParser
27 +from vllm.tool_parsers.qwen3xml_tool_parser import (
28 + Qwen3XMLToolParser,
29 + StreamingXMLToolCallParser,
30 +)
31
32 MODEL = "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8"
33
@@ -72,6 +75,23 @@ AREA_PARAMS = {
75 },
76 }
77
78 +QUESTION_PARAMS = {
79 + "type": "object",
80 + "properties": {
81 + "questions": {
82 + "type": "array",
83 + "items": {
84 + "type": "object",
85 + "properties": {
86 + "question": {"type": "string"},
87 + "multiSelect": {"type": "boolean"},
88 + "answer": {"type": "string"},
89 + },
90 + },
91 + },
92 + },
93 +}
94 +
95
96 @pytest.fixture(params=["chat_completion", "responses_api"])
97 def sample_tools(request):
@@ -148,6 +168,47 @@ def assert_tool_calls(
168 )
169
170
171 +def test_qwen3xml_deferred_array_parses_json_literals():
172 + parser = StreamingXMLToolCallParser()
173 + parser.set_tools(
174 + [
175 + ChatCompletionToolsParam(
176 + type="function",
177 + function={
178 + "name": "AskUserQuestion",
179 + "parameters": QUESTION_PARAMS,
180 + },
181 + )
182 + ]
183 + )
184 +
185 + delta = parser.parse_single_streaming_chunks(
186 + """<tool_call>
187 +<function=AskUserQuestion>
188 +<parameter=questions>
189 +[{"question": "Pick a color", "multiSelect": false, "answer": null}]
190 +</parameter>
191 +</function>
192 +</tool_call>"""
193 + )
194 +
195 + arguments = "".join(
196 + tool_call.function.arguments or ""
197 + for tool_call in delta.tool_calls or []
198 + if tool_call.function and tool_call.function.arguments is not None
199 + )
200 +
201 + assert json.loads(arguments) == {
202 + "questions": [
203 + {
204 + "question": "Pick a color",
205 + "multiSelect": False,
206 + "answer": None,
207 + }
208 + ]
209 + }
210 +
211 +
212 def stream_delta_message_generator(
213 qwen3_tool_parser,
214 qwen3_tokenizer: TokenizerLike,
vllm/tool_parsers/qwen3xml_tool_parser.py
+4 -1
@@ -821,7 +821,10 @@ class StreamingXMLToolCallParser:
821 raw_for_parse = raw_text + "\n"
822 else:
823 raw_for_parse = raw_text
824 - parsed_value = ast.literal_eval(raw_for_parse)
824 + try:
825 + parsed_value = json.loads(raw_for_parse)
826 + except json.JSONDecodeError:
827 + parsed_value = ast.literal_eval(raw_for_parse)
828 output_arguments = json.dumps(parsed_value, ensure_ascii=False)
829 except Exception:
830 # Fallback: output as string as-is