@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 63ea11709b
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 3, 04:15 UTC

[CI] Add composed-schema regression tests for DeepSeek V3.2/V4 parsers (#43255)

Signed-off-by: Ace Eldeib <aeldeib@coreweave.com> Co-authored-by: Flora Feng <4florafeng@gmail.com>

Ace Eldeib committed May 21, 2026 at 02:36 UTC 63ea11709bd9e9b14669e3973dff92d2dcea3cb1
2 files changed +186
tests/tool_parsers/test_deepseekv32_tool_parser.py
+137
@@ -221,6 +221,99 @@ class TestExtractToolCalls:
221 assert args == {"value": 42}
222 assert isinstance(args["value"], int)
223
224 + @pytest.mark.skip_global_cleanup
225 + def test_composed_schema_converts_object_and_array_params(self):
226 + """Composed JSON Schema types must still drive DSML type coercion."""
227 + tool = ChatCompletionToolsParam(
228 + function=FunctionDefinition(
229 + name="set_timer",
230 + parameters={
231 + "type": "object",
232 + "properties": {
233 + "wait": {
234 + "anyOf": [
235 + {
236 + "type": "object",
237 + "properties": {
238 + "type": {"const": "until"},
239 + "date": {"type": "string"},
240 + },
241 + },
242 + {
243 + "type": "object",
244 + "properties": {
245 + "type": {"const": "for"},
246 + "minutes": {"type": "number"},
247 + },
248 + },
249 + ],
250 + },
251 + "patches": {
252 + "oneOf": [
253 + {"type": "array", "items": {"type": "object"}},
254 + {"type": "null"},
255 + ],
256 + },
257 + },
258 + },
259 + ),
260 + )
261 + parser = make_parser(tools=[tool])
262 + model_output = (
263 + f"{FC_START}\n"
264 + f'{INV_START}set_timer">\n'
265 + f'{PARAM_START}wait" string="false">'
266 + f'{{"type":"for","minutes":2880}}'
267 + f"{PARAM_END}\n"
268 + f'{PARAM_START}patches" string="false">'
269 + f'[{{"op":"replace","path":"/schedule","value":"quiet"}}]'
270 + f"{PARAM_END}\n"
271 + f"{INV_END}\n"
272 + f"{FC_END}"
273 + )
274 + result = parser.extract_tool_calls(model_output, None)
275 + assert result.tools_called
276 + args = json.loads(result.tool_calls[0].function.arguments)
277 + assert args == {
278 + "wait": {"type": "for", "minutes": 2880},
279 + "patches": [{"op": "replace", "path": "/schedule", "value": "quiet"}],
280 + }
281 + assert isinstance(args["wait"], dict)
282 + assert isinstance(args["patches"], list)
283 +
284 + @pytest.mark.skip_global_cleanup
285 + def test_string_attr_true_preserves_literal_for_composed_schema(self):
286 + tool = ChatCompletionToolsParam(
287 + function=FunctionDefinition(
288 + name="set_timer",
289 + parameters={
290 + "type": "object",
291 + "properties": {
292 + "wait": {
293 + "anyOf": [
294 + {"type": "object"},
295 + {"type": "null"},
296 + ],
297 + },
298 + },
299 + },
300 + ),
301 + )
302 + parser = make_parser(tools=[tool])
303 + model_output = (
304 + f"{FC_START}\n"
305 + f'{INV_START}set_timer">\n'
306 + f'{PARAM_START}wait" string="true">'
307 + f'{{"type":"for","minutes":2880}}'
308 + f"{PARAM_END}\n"
309 + f"{INV_END}\n"
310 + f"{FC_END}"
311 + )
312 + result = parser.extract_tool_calls(model_output, None)
313 + assert result.tools_called
314 + args = json.loads(result.tool_calls[0].function.arguments)
315 + assert args == {"wait": '{"type":"for","minutes":2880}'}
316 +
317 def test_arguments_wrapper_repaired(self):
318 """A single 'arguments' wrapper parameter must be unwrapped when it
319 is not part of the tool schema and the inner object matches schema fields."""
@@ -581,6 +674,50 @@ class TestExtractToolCallsStreaming:
674 assert args == {"value": "42"}
675 assert isinstance(args["value"], str)
676
677 + @pytest.mark.skip_global_cleanup
678 + def test_composed_schema_conversion_in_streaming(self):
679 + tool = ChatCompletionToolsParam(
680 + function=FunctionDefinition(
681 + name="set_timer",
682 + parameters={
683 + "type": "object",
684 + "properties": {
685 + "wait": {
686 + "anyOf": [
687 + {"type": "object"},
688 + {"type": "null"},
689 + ],
690 + },
691 + "patches": {
692 + "oneOf": [
693 + {"type": "array", "items": {"type": "object"}},
694 + {"type": "null"},
695 + ],
696 + },
697 + },
698 + },
699 + ),
700 + )
701 + parser = make_parser(tools=[tool])
702 + full_text = (
703 + f"{FC_START}\n"
704 + f'{INV_START}set_timer">\n'
705 + f'{PARAM_START}wait" string="false">'
706 + f'{{"type":"for","minutes":2880}}'
707 + f"{PARAM_END}\n"
708 + f'{PARAM_START}patches" string="false">'
709 + f'[{{"op":"replace","path":"/schedule","value":"quiet"}}]'
710 + f"{PARAM_END}\n"
711 + f"{INV_END}\n"
712 + f"{FC_END}"
713 + )
714 + deltas = self._stream(parser, full_text)
715 + args = json.loads(self._reconstruct_args(deltas))
716 + assert args == {
717 + "wait": {"type": "for", "minutes": 2880},
718 + "patches": [{"op": "replace", "path": "/schedule", "value": "quiet"}],
719 + }
720 +
721 def test_multiple_tools_streaming(self, parser):
722 full_text = (
723 f"{FC_START}\n"
tests/tool_parsers/test_deepseekv4_tool_parser.py
+49
@@ -236,3 +236,52 @@ def test_extract_tool_calls_arguments_wrapper():
236 assert result.tools_called
237 args = json.loads(result.tool_calls[0].function.arguments)
238 assert args == {"location": "Beijing"}
239 +
240 +
241 +@pytest.mark.skip_global_cleanup
242 +def test_composed_schema_converts_object_and_array_params():
243 + tool = ChatCompletionToolsParam(
244 + type="function",
245 + function={
246 + "name": "set_timer",
247 + "parameters": {
248 + "type": "object",
249 + "properties": {
250 + "wait": {
251 + "anyOf": [
252 + {"type": "object"},
253 + {"type": "null"},
254 + ],
255 + },
256 + "patches": {
257 + "allOf": [
258 + {"type": "array", "items": {"type": "object"}},
259 + ],
260 + },
261 + },
262 + },
263 + },
264 + )
265 + parser = make_parser(tools=[tool])
266 + request = make_request(tools=[tool])
267 + model_output = (
268 + f"{TC_START}\n"
269 + f'{INV_START}set_timer">\n'
270 + f'{PARAM_START}wait" string="false">'
271 + f'{{"type":"for","minutes":2880}}'
272 + f"{PARAM_END}\n"
273 + f'{PARAM_START}patches" string="false">'
274 + f'[{{"op":"replace","path":"/schedule","value":"quiet"}}]'
275 + f"{PARAM_END}\n"
276 + f"{INV_END}\n"
277 + f"{TC_END}"
278 + )
279 +
280 + result = parser.extract_tool_calls(model_output, request)
281 +
282 + assert result.tools_called
283 + args = json.loads(result.tool_calls[0].function.arguments)
284 + assert args == {
285 + "wait": {"type": "for", "minutes": 2880},
286 + "patches": [{"op": "replace", "path": "/schedule", "value": "quiet"}],
287 + }