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 11, 03:45 UTC
Update MiniMax-M2 ToolCall and add MiniMax-M2.1 in Docs (#31083)
Signed-off-by: xuebi <xuebi@minimaxi.com> Co-authored-by: xuebi <xuebi@minimaxi.com>
Roger Young committed
Dec 22, 2025 at 13:28 UTC
c02a2705f9ceeb00b5d32453621f997b2ceafbea
2 files changed
+167
-48
docs/models/supported_models.md
+1
-1
@@ -418,7 +418,7 @@ th {
418
| `MiMoV2FlashForCausalLM` | MiMoV2Flash | `XiaomiMiMo/MiMo-V2-Flash`, etc. | ︎| ✅︎ |
419
| `MiniCPMForCausalLM` | MiniCPM | `openbmb/MiniCPM-2B-sft-bf16`, `openbmb/MiniCPM-2B-dpo-bf16`, `openbmb/MiniCPM-S-1B-sft`, etc. | ✅︎ | ✅︎ |
420
| `MiniCPM3ForCausalLM` | MiniCPM3 | `openbmb/MiniCPM3-4B`, etc. | ✅︎ | ✅︎ |
421
-| `MiniMaxM2ForCausalLM` | MiniMax-M2 |`MiniMaxAI/MiniMax-M2`, etc. | | ✅︎ |
421
+| `MiniMaxM2ForCausalLM` | MiniMax-M2, MiniMax-M2.1 |`MiniMaxAI/MiniMax-M2`, etc. | | ✅︎ |
422
| `MistralForCausalLM` | Ministral-3, Mistral, Mistral-Instruct | `mistralai/Ministral-3-3B-Instruct-2512`, `mistralai/Mistral-7B-v0.1`, `mistralai/Mistral-7B-Instruct-v0.1`, etc. | ✅︎ | ✅︎ |
423
| `MistralLarge3ForCausalLM` | Mistral-Large-3-675B-Base-2512, Mistral-Large-3-675B-Instruct-2512 | `mistralai/Mistral-Large-3-675B-Base-2512`, `mistralai/Mistral-Large-3-675B-Instruct-2512`, etc. | ✅︎ | ✅︎ |
424
| `MixtralForCausalLM` | Mixtral-8x7B, Mixtral-8x7B-Instruct | `mistralai/Mixtral-8x7B-v0.1`, `mistralai/Mixtral-8x7B-Instruct-v0.1`, `mistral-community/Mixtral-8x22B-v0.1`, etc. | ✅︎ | ✅︎ |
vllm/tool_parsers/minimax_m2_tool_parser.py
+166
-47
@@ -138,37 +138,167 @@ class MinimaxM2ToolParser(ToolParser):
138
return name_str
139
140
def _convert_param_value(self, value: str, param_type: str) -> Any:
141
- """Convert parameter value to the correct type."""
141
+ """Convert parameter value to the correct type (legacy single-type version)."""
142
+ return self._convert_param_value_with_types(value, [param_type])
143
+
144
+ def _extract_types_from_schema(self, schema: Any) -> list[str]:
145
+ """
146
+ Extract all possible types from a JSON schema definition.
147
+ Handles anyOf, oneOf, allOf, type arrays, and enum fields.
148
+
149
+ Args:
150
+ schema: The JSON schema definition for a parameter
151
+
152
+ Returns:
153
+ List of type strings (e.g., ["string", "integer", "null"])
154
+ """
155
+ if schema is None:
156
+ return ["string"]
157
+
158
+ if not isinstance(schema, dict):
159
+ return ["string"]
160
+
161
+ types: set[str] = set()
162
+
163
+ # Handle direct "type" field
164
+ if "type" in schema:
165
+ type_value = schema["type"]
166
+ if isinstance(type_value, str):
167
+ types.add(type_value)
168
+ elif isinstance(type_value, list):
169
+ for t in type_value:
170
+ if isinstance(t, str):
171
+ types.add(t)
172
+
173
+ # Handle enum - infer types from enum values
174
+ if "enum" in schema and isinstance(schema["enum"], list) and schema["enum"]:
175
+ for value in schema["enum"]:
176
+ if value is None:
177
+ types.add("null")
178
+ elif isinstance(value, bool):
179
+ types.add("boolean")
180
+ elif isinstance(value, int):
181
+ types.add("integer")
182
+ elif isinstance(value, float):
183
+ types.add("number")
184
+ elif isinstance(value, str):
185
+ types.add("string")
186
+ elif isinstance(value, list):
187
+ types.add("array")
188
+ elif isinstance(value, dict):
189
+ types.add("object")
190
+
191
+ # Handle anyOf, oneOf, allOf - recursively extract types
192
+ for choice_field in ("anyOf", "oneOf", "allOf"):
193
+ if choice_field in schema and isinstance(schema[choice_field], list):
194
+ for choice in schema[choice_field]:
195
+ extracted = self._extract_types_from_schema(choice)
196
+ types.update(extracted)
197
+
198
+ # If no types found, default to string
199
+ if not types:
200
+ return ["string"]
201
+
202
+ return list(types)
203
+
204
+ def _convert_param_value_with_types(
205
+ self, value: str, param_types: list[str]
206
+ ) -> Any:
207
+ """
208
+ Convert parameter value to the correct type based on a list of possible types.
209
+ Tries each type in order until one succeeds.
210
+
211
+ Args:
212
+ value: The string value to convert
213
+ param_types: List of possible type strings
214
+
215
+ Returns:
216
+ The converted value
217
+ """
218
if value.lower() == "null":
219
return None
220
145
- param_type = param_type.lower()
146
- if param_type in ["string", "str", "text"]:
147
- return value
148
- elif param_type in ["integer", "int"]:
149
- try:
150
- return int(value)
151
- except (ValueError, TypeError):
152
- return value
153
- elif param_type in ["number", "float"]:
154
- try:
155
- val = float(value)
156
- return val if val != int(val) else int(val)
157
- except (ValueError, TypeError):
158
- return value
159
- elif param_type in ["boolean", "bool"]:
160
- return value.lower() in ["true", "1"]
161
- elif param_type in ["object", "array"]:
162
- try:
163
- return json.loads(value)
164
- except json.JSONDecodeError:
165
- return value
166
- else:
167
- # Try JSON parse first, fallback to string
168
- try:
169
- return json.loads(value)
170
- except json.JSONDecodeError:
221
+ # Normalize types
222
+ normalized_types = [t.lower() for t in param_types]
223
+
224
+ # Try null first if it's in the list
225
+ if "null" in normalized_types or value.lower() in ("null", "none", "nil"):
226
+ return None
227
+
228
+ # Try each type in order of preference (most specific first, string as fallback)
229
+ # Priority: integer > number > boolean > object > array > string
230
+ type_priority = [
231
+ "integer",
232
+ "int",
233
+ "number",
234
+ "float",
235
+ "boolean",
236
+ "bool",
237
+ "object",
238
+ "array",
239
+ "string",
240
+ "str",
241
+ "text",
242
+ ]
243
+
244
+ for param_type in type_priority:
245
+ if param_type not in normalized_types:
246
+ continue
247
+
248
+ if param_type in ["string", "str", "text"]:
249
return value
250
+ elif param_type in ["integer", "int"]:
251
+ try:
252
+ return int(value)
253
+ except (ValueError, TypeError):
254
+ continue
255
+ elif param_type in ["number", "float"]:
256
+ try:
257
+ val = float(value)
258
+ return val if val != int(val) else int(val)
259
+ except (ValueError, TypeError):
260
+ continue
261
+ elif param_type in ["boolean", "bool"]:
262
+ lower_val = value.lower().strip()
263
+ if lower_val in ["true", "1", "yes", "on"]:
264
+ return True
265
+ elif lower_val in ["false", "0", "no", "off"]:
266
+ return False
267
+ continue
268
+ elif param_type in ["object", "array"]:
269
+ try:
270
+ return json.loads(value)
271
+ except json.JSONDecodeError:
272
+ continue
273
+
274
+ # Fallback: try JSON parse, then return as string
275
+ try:
276
+ return json.loads(value)
277
+ except json.JSONDecodeError:
278
+ return value
279
+
280
+ def _get_param_types_from_config(
281
+ self, param_name: str, param_config: dict
282
+ ) -> list[str]:
283
+ """
284
+ Get parameter types from parameter configuration.
285
+ Handles anyOf, oneOf, allOf, and direct type definitions.
286
+
287
+ Args:
288
+ param_name: The name of the parameter
289
+ param_config: The properties dict from the tool schema
290
+
291
+ Returns:
292
+ List of type strings
293
+ """
294
+ if param_name not in param_config:
295
+ return ["string"]
296
+
297
+ param_schema = param_config[param_name]
298
+ if not isinstance(param_schema, dict):
299
+ return ["string"]
300
+
301
+ return self._extract_types_from_schema(param_schema)
302
303
def _parse_single_invoke(
304
self, invoke_str: str, tools: list | None
@@ -207,17 +337,11 @@ class MinimaxM2ToolParser(ToolParser):
337
if param_value.endswith("\n"):
338
param_value = param_value[:-1]
339
210
- # Get parameter type
211
- param_type = "string"
212
- if (
213
- param_name in param_config
214
- and isinstance(param_config[param_name], dict)
215
- and "type" in param_config[param_name]
216
- ):
217
- param_type = param_config[param_name]["type"]
340
+ # Get parameter types (supports anyOf/oneOf/allOf)
341
+ param_type = self._get_param_types_from_config(param_name, param_config)
342
343
# Convert value
220
- param_dict[param_name] = self._convert_param_value(
344
+ param_dict[param_name] = self._convert_param_value_with_types(
345
param_value, param_type
346
)
347
@@ -593,7 +717,7 @@ class MinimaxM2ToolParser(ToolParser):
717
# Store raw value for later processing
718
self.accumulated_params[self.current_param_name] = param_value
719
596
- # Get parameter configuration for type conversion
720
+ # Get parameter configuration with anyOf support
721
param_config = {}
722
if self.streaming_request and self.streaming_request.tools:
723
for tool in self.streaming_request.tools:
@@ -610,17 +734,12 @@ class MinimaxM2ToolParser(ToolParser):
734
param_config = params["properties"]
735
break
736
613
- # Get parameter type
614
- param_type = "string"
615
- if (
616
- self.current_param_name in param_config
617
- and isinstance(param_config[self.current_param_name], dict)
618
- and "type" in param_config[self.current_param_name]
619
- ):
620
- param_type = param_config[self.current_param_name]["type"]
737
+ # Get parameter types (supports anyOf/oneOf/allOf)
738
+ param_type = self._get_param_types_from_config(
739
+ self.current_param_name, param_config
740
+ )
741
622
- # Convert param value to appropriate type
623
- converted_value = self._convert_param_value(
742
+ converted_value = self._convert_param_value_with_types(
743
param_value, param_type
744
)
745