@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 42b4f1fdf7
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, 08:00 UTC

[Refactor] Extract extract_types_from_schema utility from Minimax M2 tool parser (#43025)

Signed-off-by: sfeng33 <4florafeng@gmail.com>

Flora Feng committed May 19, 2026 at 11:21 UTC 42b4f1fdf7269de8aa83755a805555fe78add28b
3 files changed +123 -106
tests/tool_parsers/test_utils.py
+67 -1
@@ -3,7 +3,10 @@
3
4 import pytest
5
6 -from vllm.tool_parsers.utils import coerce_to_schema_type
6 +from vllm.tool_parsers.utils import (
7 + coerce_to_schema_type,
8 + extract_types_from_schema,
9 +)
10
11
12 class TestCoerceToSchemaType:
@@ -146,3 +149,66 @@ class TestCoerceToSchemaType:
149
150 def test_unrecognized_type_falls_back_to_json(self):
151 assert coerce_to_schema_type("42", "interval") == 42
152 +
153 +
154 +class TestExtractTypesFromSchema:
155 + def test_direct_type_string(self):
156 + assert extract_types_from_schema({"type": "string"}) == ["string"]
157 +
158 + def test_direct_type_integer(self):
159 + assert extract_types_from_schema({"type": "integer"}) == ["integer"]
160 +
161 + def test_type_array(self):
162 + result = set(extract_types_from_schema({"type": ["string", "null"]}))
163 + assert result == {"string", "null"}
164 +
165 + def test_anyof(self):
166 + schema = {"anyOf": [{"type": "object"}, {"type": "null"}]}
167 + result = set(extract_types_from_schema(schema))
168 + assert result == {"object", "null"}
169 +
170 + def test_oneof(self):
171 + schema = {"oneOf": [{"type": "integer"}, {"type": "string"}]}
172 + result = set(extract_types_from_schema(schema))
173 + assert result == {"integer", "string"}
174 +
175 + def test_allof(self):
176 + schema = {"allOf": [{"type": "object"}]}
177 + assert extract_types_from_schema(schema) == ["object"]
178 +
179 + def test_enum_infers_types(self):
180 + schema = {"enum": [1, "a", None]}
181 + result = set(extract_types_from_schema(schema))
182 + assert result == {"integer", "string", "null"}
183 +
184 + def test_enum_with_bool(self):
185 + schema = {"enum": [True, False]}
186 + assert extract_types_from_schema(schema) == ["boolean"]
187 +
188 + def test_enum_with_float(self):
189 + schema = {"enum": [1.5, 2.5]}
190 + assert extract_types_from_schema(schema) == ["number"]
191 +
192 + def test_enum_with_list_and_dict(self):
193 + schema = {"enum": [[1, 2], {"a": 1}]}
194 + result = set(extract_types_from_schema(schema))
195 + assert result == {"array", "object"}
196 +
197 + def test_none_schema_defaults_to_string(self):
198 + assert extract_types_from_schema(None) == ["string"]
199 +
200 + def test_non_dict_schema_defaults_to_string(self):
201 + assert extract_types_from_schema("string") == ["string"]
202 +
203 + def test_empty_dict_defaults_to_string(self):
204 + assert extract_types_from_schema({}) == ["string"]
205 +
206 + def test_nested_anyof(self):
207 + schema = {
208 + "anyOf": [
209 + {"anyOf": [{"type": "integer"}, {"type": "null"}]},
210 + {"type": "string"},
211 + ]
212 + }
213 + result = set(extract_types_from_schema(schema))
214 + assert result == {"integer", "null", "string"}
vllm/tool_parsers/minimax_m2_tool_parser.py
+10 -105
@@ -4,7 +4,6 @@
4 import json
5 import uuid
6 from collections.abc import Sequence
7 -from typing import Any
7
8 import regex as re
9
@@ -25,7 +24,11 @@ from vllm.tool_parsers.abstract_tool_parser import (
24 Tool,
25 ToolParser,
26 )
28 -from vllm.tool_parsers.utils import coerce_to_schema_type
27 +from vllm.tool_parsers.utils import (
28 + coerce_to_schema_type,
29 + extract_types_from_schema,
30 + find_tool_properties,
31 +)
32
33 logger = init_logger(__name__)
34
@@ -87,89 +90,6 @@ class MinimaxM2ToolParser(ToolParser):
90 return name_str[1:-1]
91 return name_str
92
90 - def _extract_types_from_schema(self, schema: Any) -> list[str]:
91 - """
92 - Extract all possible types from a JSON schema definition.
93 - Handles anyOf, oneOf, allOf, type arrays, and enum fields.
94 -
95 - Args:
96 - schema: The JSON schema definition for a parameter
97 -
98 - Returns:
99 - List of type strings (e.g., ["string", "integer", "null"])
100 - """
101 - if schema is None:
102 - return ["string"]
103 -
104 - if not isinstance(schema, dict):
105 - return ["string"]
106 -
107 - types: set[str] = set()
108 -
109 - # Handle direct "type" field
110 - if "type" in schema:
111 - type_value = schema["type"]
112 - if isinstance(type_value, str):
113 - types.add(type_value)
114 - elif isinstance(type_value, list):
115 - for t in type_value:
116 - if isinstance(t, str):
117 - types.add(t)
118 -
119 - # Handle enum - infer types from enum values
120 - if "enum" in schema and isinstance(schema["enum"], list) and schema["enum"]:
121 - for value in schema["enum"]:
122 - if value is None:
123 - types.add("null")
124 - elif isinstance(value, bool):
125 - types.add("boolean")
126 - elif isinstance(value, int):
127 - types.add("integer")
128 - elif isinstance(value, float):
129 - types.add("number")
130 - elif isinstance(value, str):
131 - types.add("string")
132 - elif isinstance(value, list):
133 - types.add("array")
134 - elif isinstance(value, dict):
135 - types.add("object")
136 -
137 - # Handle anyOf, oneOf, allOf - recursively extract types
138 - for choice_field in ("anyOf", "oneOf", "allOf"):
139 - if choice_field in schema and isinstance(schema[choice_field], list):
140 - for choice in schema[choice_field]:
141 - extracted = self._extract_types_from_schema(choice)
142 - types.update(extracted)
143 -
144 - # If no types found, default to string
145 - if not types:
146 - return ["string"]
147 -
148 - return list(types)
149 -
150 - def _get_param_types_from_config(
151 - self, param_name: str, param_config: dict
152 - ) -> list[str]:
153 - """
154 - Get parameter types from parameter configuration.
155 - Handles anyOf, oneOf, allOf, and direct type definitions.
156 -
157 - Args:
158 - param_name: The name of the parameter
159 - param_config: The properties dict from the tool schema
160 -
161 - Returns:
162 - List of type strings
163 - """
164 - if param_name not in param_config:
165 - return ["string"]
166 -
167 - param_schema = param_config[param_name]
168 - if not isinstance(param_schema, dict):
169 - return ["string"]
170 -
171 - return self._extract_types_from_schema(param_schema)
172 -
93 def _parse_single_invoke(
94 self, invoke_str: str, tools: list | None
95 ) -> ToolCall | None:
@@ -180,20 +100,7 @@ class MinimaxM2ToolParser(ToolParser):
100 return None
101
102 function_name = self._extract_name(name_match.group(1))
183 -
184 - # Get parameter configuration
185 - param_config = {}
186 - if tools:
187 - for tool in tools:
188 - if (
189 - hasattr(tool, "function")
190 - and tool.function.name == function_name
191 - and hasattr(tool.function, "parameters")
192 - ):
193 - params = tool.function.parameters
194 - if isinstance(params, dict) and "properties" in params:
195 - param_config = params["properties"]
196 - break
103 + tool_properties = find_tool_properties(tools, function_name)
104
105 # Extract parameters
106 param_dict = {}
@@ -202,12 +109,10 @@ class MinimaxM2ToolParser(ToolParser):
109 if param_match:
110 param_name = self._extract_name(param_match.group(1))
111 param_value = param_match.group(2).strip()
205 -
206 - # Get parameter types (supports anyOf/oneOf/allOf)
207 - param_type = self._get_param_types_from_config(param_name, param_config)
208 -
209 - # Convert value
210 - param_dict[param_name] = coerce_to_schema_type(param_value, param_type)
112 + param_types = extract_types_from_schema(
113 + tool_properties.get(param_name, {})
114 + )
115 + param_dict[param_name] = coerce_to_schema_type(param_value, param_types)
116
117 return ToolCall(
118 type="function",
vllm/tool_parsers/utils.py
+46
@@ -450,6 +450,52 @@ def make_valid_python(text: str) -> tuple[str, str] | None:
450 return candidate, added_text
451
452
453 +def extract_types_from_schema(schema: Any) -> list[str]:
454 + """Extract all possible type strings from a JSON Schema definition.
455 +
456 + Handles ``type`` (string or list), ``enum`` value inference, and
457 + recursive ``anyOf``/``oneOf``/``allOf``. Returns ``["string"]``
458 + when no type information can be determined.
459 + """
460 + if schema is None or not isinstance(schema, dict):
461 + return ["string"]
462 +
463 + types: set[str] = set()
464 +
465 + if "type" in schema:
466 + type_value = schema["type"]
467 + if isinstance(type_value, str):
468 + types.add(type_value)
469 + elif isinstance(type_value, list):
470 + for t in type_value:
471 + if isinstance(t, str):
472 + types.add(t)
473 +
474 + if "enum" in schema and isinstance(schema["enum"], list) and schema["enum"]:
475 + for value in schema["enum"]:
476 + if value is None:
477 + types.add("null")
478 + elif isinstance(value, bool):
479 + types.add("boolean")
480 + elif isinstance(value, int):
481 + types.add("integer")
482 + elif isinstance(value, float):
483 + types.add("number")
484 + elif isinstance(value, str):
485 + types.add("string")
486 + elif isinstance(value, list):
487 + types.add("array")
488 + elif isinstance(value, dict):
489 + types.add("object")
490 +
491 + for choice_field in ("anyOf", "oneOf", "allOf"):
492 + if choice_field in schema and isinstance(schema[choice_field], list):
493 + for choice in schema[choice_field]:
494 + types.update(extract_types_from_schema(choice))
495 +
496 + return list(types) if types else ["string"]
497 +
498 +
499 _TYPE_ALIASES: dict[str, str] = {
500 "str": "string",
501 "text": "string",