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