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 12, 04:45 UTC
[BUGFIX] Fix regex pattern for Mistral Tool Call (#29918)
Signed-off-by: juliendenize <julien.denize@mistral.ai>
Julien Denize committed
Dec 2, 2025 at 23:51 UTC
1b1e35aaf9d9561e1b5bf5b8e08b03565188e537
2 files changed
+36
-1
tests/models/language/generation/test_mistral.py
+35
@@ -315,3 +315,38 @@ def test_mistral_function_call_nested_json():
315
assert json.loads(parsed.tool_calls[0].function.arguments) == args_dict
316
# No additional content outside the tool call should be returned.
317
assert parsed.content is None
318
+
319
+ # multiple calls
320
+ multiple_args_dict = [
321
+ {
322
+ "city": "Dallas",
323
+ "state": "TX",
324
+ "unit": "fahrenheit",
325
+ "sub_dict": {"foo": "bar", "inner": {"x": 1, "y": 2}},
326
+ },
327
+ {},
328
+ {"a": 0},
329
+ {"a": 1, "b": "c"},
330
+ ]
331
+ names = ["get_current_weather", "get_current_weather_2", "random", "random_2"]
332
+
333
+ model_output = "".join(
334
+ [
335
+ f"{parser.bot_token}{name}{json.dumps(args)}"
336
+ for name, args in zip(names, multiple_args_dict)
337
+ ]
338
+ )
339
+
340
+ parsed = parser.extract_tool_calls(model_output, None)
341
+
342
+ # Assertions: the tool call is detected and the full nested JSON is parsed
343
+ # without truncation.
344
+ assert parsed.tools_called
345
+ assert len(parsed.tool_calls) == len(multiple_args_dict)
346
+
347
+ for i, tool_call in enumerate(parsed.tool_calls):
348
+ assert MistralToolCall.is_valid_id(tool_call.id)
349
+ assert tool_call.function.name == names[i]
350
+ assert json.loads(tool_call.function.arguments) == multiple_args_dict[i]
351
+ # No additional content outside the tool call should be returned.
352
+ assert parsed.content is None
vllm/entrypoints/openai/tool_parsers/mistral_tool_parser.py
+1
-1
@@ -80,7 +80,7 @@ class MistralToolParser(ToolParser):
80
self.tool_call_regex = re.compile(r"\[{.*}\]", re.DOTALL)
81
if _is_fn_name_regex_support(self.model_tokenizer):
82
self.fn_name_regex = re.compile(
83
- r"([a-zA-Z0-9_-]+)(\{[\s\S]*?\})(?=\s*$|,|\s)?", re.DOTALL
83
+ r"([a-zA-Z0-9_-]+)(\{[\s\S]*?\}+)", re.DOTALL
84
)
85
else:
86
self.fn_name_regex = None