[Frontend] Port the MiniMax M3 tool parser to the parser engine (#59743)

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Bugen Zhao
2026-10-06 05:46:27 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 6fddc9b21e
commit 5145e35e50
7 changed files with 658 additions and 41 deletions
+62
View File
@@ -42,6 +42,7 @@ from vllm.parser.engine.registered_adapters import (
KimiK2Parser,
MiMoParser,
MinimaxM2Parser,
MinimaxM3Parser,
NemotronV3Parser,
Plamo3Parser,
Qwen3Parser,
@@ -539,6 +540,66 @@ def _build_minimax_m2(scenario: Scenario, validate: bool = True) -> Sample:
return sample
# ── MiniMax M3 (namespaced XML elements, no reasoning) ───────────────
_MINIMAX_M3_NS = "]<]minimax[>["
def _minimax_m3_value(value: Any) -> str:
"""Render a value the way the M3 chat template's ``to_xml`` does."""
ns = _MINIMAX_M3_NS
if isinstance(value, dict):
return "".join(
f"{ns}<{key}>{_minimax_m3_value(item)}{ns}</{key}>"
for key, item in value.items()
)
if isinstance(value, list):
return "".join(
f"{ns}<item>{_minimax_m3_value(item)}{ns}</item>" for item in value
)
if isinstance(value, bool):
return "true" if value else "false"
return str(value)
def _minimax_m3_segments(scenario: Scenario) -> list[tuple[str, bool]]:
ns = _MINIMAX_M3_NS
segs: list[tuple[str, bool]] = []
if scenario.content:
# The tool parser receives reasoning-stripped content.
segs.append((scenario.content, False))
if scenario.tool_calls:
invokes = "".join(
f'{ns}<invoke name="{tc.name}">'
f"{_minimax_m3_value(tc.arguments)}{ns}</invoke>\n"
for tc in scenario.tool_calls
)
segs.append((f"{ns}<tool_call>\n{invokes}{ns}</tool_call>", False))
return segs
def _minimax_m3_expected_content(scenario: Scenario) -> str | None:
if scenario.tool_calls and not (scenario.content or "").strip():
return None
return scenario.content
def _build_minimax_m3(scenario: Scenario, validate: bool = True) -> Sample:
sample = _make_sample(
sample_id=f"minimax_m3-{scenario.id}",
description=scenario.description,
vocab={},
segments=_minimax_m3_segments(scenario),
expected_reasoning=None,
expected_content=_minimax_m3_expected_content(scenario),
expected_tool_calls=_expected_tc(scenario),
tools=_expected_tools(scenario),
)
if validate:
_validate_sample(sample, MinimaxM3Parser)
return sample
# ── Gemma4 (channel reasoning, custom arg format) ────────────────────
_GEMMA4_VOCAB: dict[str, int] = {
@@ -1275,6 +1336,7 @@ _BUILDERS: dict[str, Any] = {
"gemma4": _build_gemma4,
"granite": _build_granite,
"minimax_m2": _build_minimax_m2,
"minimax_m3": _build_minimax_m3,
"nemotron_v3": _build_nemotron_v3,
"granite_thinking_parser": _build_granite_thinking,
"seed_oss": _build_seed_oss,