@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 2ec18f5df4
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 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: