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 4, 16:15 UTC
[Bugfix][Parser] Fix Mistral tool parser for HF tokenizers (#39294)
Signed-off-by: thomasmaindron <thomasmaindron@users.noreply.github.com> Co-authored-by: thomasmaindron <thomasmaindron@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Chauncey <chaunceyjiang@gmail.com>
Thomas committed
Apr 24, 2026 at 13:01 UTC
2ec18f5df43e7f6c51e95125759904d39bd01630
1 file changed
+13
-4
vllm/tool_parsers/mistral_tool_parser.py
+13
-4
@@ -91,7 +91,12 @@ class MistralToolCall(ToolCall):
91
92
93
def _is_pre_v11_tokeniser(model_tokenizer: TokenizerLike) -> bool:
94
- return not (is_mistral_tokenizer(model_tokenizer) and model_tokenizer.version >= 11)
94
+ if is_mistral_tokenizer(model_tokenizer):
95
+ return model_tokenizer.version < 11
96
+ # For HF tokenizers, check if [ARGS] token exists in vocab
97
+ # which indicates a v11+ equivalent tokenizer
98
+ vocab: dict[str, int] = getattr(model_tokenizer, "get_vocab", lambda: {})()
99
+ return "[ARGS]" not in vocab
100
101
102
@dataclass
@@ -139,7 +144,8 @@ class MistralToolParser(ToolParser):
144
self.current_tool_name: str | None = None
145
self.current_tool_mistral_id: str | None = None
146
self.starting_new_tool = False
142
- if _is_pre_v11_tokeniser(self.model_tokenizer):
147
+ self._is_pre_v11 = _is_pre_v11_tokeniser(self.model_tokenizer)
148
+ if self._is_pre_v11:
149
self.parse_coro = ijson.parse_coro(
150
self.update_stream_state_pre_v11_tokenizer()
151
)
@@ -147,7 +153,6 @@ class MistralToolParser(ToolParser):
153
self.bot_token = "[TOOL_CALLS]"
154
self.bot_token_id = self.vocab.get(self.bot_token)
155
self.tool_call_regex = re.compile(r"\[{.*}\]", re.DOTALL)
150
- self._is_pre_v11 = _is_pre_v11_tokeniser(self.model_tokenizer)
156
157
if self.bot_token_id is None:
158
raise RuntimeError(
@@ -470,6 +475,8 @@ class MistralToolParser(ToolParser):
475
raw_tool_call[end_name:],
476
)
477
478
+ # HF tokenizers may include [ARGS] in the text
479
+ tool_name = tool_name.replace("[ARGS]", "")
480
tool_calls.append({"name": tool_name, "arguments": args})
481
482
# < v11: content[BOT] [{tool_call1},{tool_call2}]
@@ -558,7 +565,7 @@ class MistralToolParser(ToolParser):
565
# if the tool call token IS in the tokens generated so far, that
566
# means we're parsing as tool calls now
567
try:
561
- if _is_pre_v11_tokeniser(self.model_tokenizer):
568
+ if self._is_pre_v11:
569
return self._extract_tool_calls_streaming_pre_v11_tokenizer(
570
delta_text=delta_text,
571
delta_token_ids=delta_token_ids,
@@ -646,6 +653,8 @@ class MistralToolParser(ToolParser):
653
tool_id = MistralToolCall.generate_random_id()
654
delta_function_name = delta_text.split("{")[0]
655
self.current_tool_name += delta_function_name
656
+ # HF tokenizers may include [ARGS] in the text
657
+ self.current_tool_name = self.current_tool_name.replace("[ARGS]", "")
658
delta_text = delta_text[len(delta_function_name) :]
659
self.streaming_state = StreamingState.PARSING_ARGUMENTS
660
else: