diff --git a/tests/parser/test_streaming.py b/tests/parser/test_streaming.py
index 71f214f3ee65..6830d694e1a5 100644
--- a/tests/parser/test_streaming.py
+++ b/tests/parser/test_streaming.py
@@ -7,7 +7,7 @@
from vllm.entrypoints.openai.chat_completion.protocol import ChatCompletionRequest
from vllm.entrypoints.openai.engine.protocol import DeltaMessage
-from vllm.parser.abstract_parser import _WrappedParser
+from vllm.parser.abstract_parser import DelegatingParser
from vllm.reasoning.basic_parsers import BaseThinkingReasoningParser
from vllm.tool_parsers.hermes_tool_parser import Hermes2ProToolParser
@@ -45,9 +45,11 @@ def request_obj():
def make_parser(tokenizer, reasoning=False, tool=False):
- _WrappedParser.reasoning_parser_cls = ThinkReasoningParser if reasoning else None
- _WrappedParser.tool_parser_cls = Hermes2ProToolParser if tool else None
- return _WrappedParser(tokenizer)
+ class TestParser(DelegatingParser):
+ reasoning_parser_cls = ThinkReasoningParser if reasoning else None
+ tool_parser_cls = Hermes2ProToolParser if tool else None
+
+ return TestParser(tokenizer)
def stream_text(parser, tokenizer, text, request, prompt_token_ids=None):
diff --git a/vllm/parser/__init__.py b/vllm/parser/__init__.py
index dc256daaa7e2..de815b2e1fd4 100644
--- a/vllm/parser/__init__.py
+++ b/vllm/parser/__init__.py
@@ -4,7 +4,6 @@
from vllm.parser.abstract_parser import (
DelegatingParser,
Parser,
- _WrappedParser,
)
from vllm.parser.parser_manager import ParserManager
@@ -12,21 +11,4 @@
"Parser",
"DelegatingParser",
"ParserManager",
- "_WrappedParser",
]
-
-_PARSERS_TO_REGISTER = {
- "minimax_m2": ( # name
- "minimax_m2_parser", # filename
- "MiniMaxM2Parser", # class_name
- ),
-}
-
-
-def register_lazy_parsers():
- for name, (file_name, class_name) in _PARSERS_TO_REGISTER.items():
- module_path = f"vllm.parser.{file_name}"
- ParserManager.register_lazy_module(name, module_path, class_name)
-
-
-register_lazy_parsers()
diff --git a/vllm/parser/abstract_parser.py b/vllm/parser/abstract_parser.py
index 96c04805bcda..ed052ceb7682 100644
--- a/vllm/parser/abstract_parser.py
+++ b/vllm/parser/abstract_parser.py
@@ -37,12 +37,11 @@
from vllm.logger import init_logger
from vllm.reasoning.abs_reasoning_parsers import ReasoningParser
from vllm.tokenizers import TokenizerLike
-from vllm.tool_parsers.abstract_tool_parser import ToolParser
+from vllm.tool_parsers.abstract_tool_parser import Tool, ToolParser
from vllm.tool_parsers.streaming import (
extract_named_tool_call_streaming,
extract_required_tool_call_streaming,
)
-from vllm.tool_parsers.utils import Tool
from vllm.utils import random_uuid
logger = init_logger(__name__)
@@ -90,19 +89,25 @@ class Parser:
reasoning_parser_cls: type[ReasoningParser] | None = None
tool_parser_cls: type[ToolParser] | None = None
- def __init__(self, tokenizer: TokenizerLike, *args, **kwargs):
- """
- Initialize the Parser.
-
- Args:
- tokenizer: The tokenizer used by the model. This is required for
- token-based parsing operations.
- """
+ def __init__(
+ self,
+ tokenizer: TokenizerLike,
+ tools: list[Tool] | None = None,
+ *args,
+ **kwargs,
+ ):
self.model_tokenizer = tokenizer
self._reasoning_parser: ReasoningParser | None = None
self._tool_parser: ToolParser | None = None
self._stream_state = StreamState()
+ if self.__class__.reasoning_parser_cls is not None:
+ self._reasoning_parser = self.__class__.reasoning_parser_cls(
+ tokenizer, *args, **kwargs
+ )
+ if self.__class__.tool_parser_cls is not None:
+ self._tool_parser = self.__class__.tool_parser_cls(tokenizer, tools)
+
@cached_property
def vocab(self) -> dict[str, int]:
"""Get the vocabulary mapping from tokens to IDs."""
@@ -767,34 +772,3 @@ def parse_delta(
self._append_unstreamed_tool_args(delta_message)
return delta_message
-
-
-class _WrappedParser(DelegatingParser):
- """
- A DelegatingParser subclass that instantiates parsers from class attributes.
-
- This class is used to dynamically create a parser that wraps individual
- ReasoningParser and ToolParser classes. The class attributes
- `reasoning_parser_cls` and `tool_parser_cls` should be set before
- instantiation.
-
- Usage:
- _WrappedParser.reasoning_parser_cls = MyReasoningParser
- _WrappedParser.tool_parser_cls = MyToolParser
- parser = _WrappedParser(tokenizer)
- """
-
- reasoning_parser_cls: type[ReasoningParser] | None = None
- tool_parser_cls: type[ToolParser] | None = None
-
- def __init__(
- self, tokenizer: TokenizerLike, tools: list[Tool] | None = None, **kwargs
- ):
- super().__init__(tokenizer)
- # Instantiate the underlying parsers from class attributes
- if self.__class__.reasoning_parser_cls is not None:
- self._reasoning_parser = self.__class__.reasoning_parser_cls(
- tokenizer, **kwargs
- )
- if self.__class__.tool_parser_cls is not None:
- self._tool_parser = self.__class__.tool_parser_cls(tokenizer, tools)
diff --git a/vllm/parser/minimax_m2_parser.py b/vllm/parser/minimax_m2_parser.py
deleted file mode 100644
index 34aaa7268446..000000000000
--- a/vllm/parser/minimax_m2_parser.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
-
-"""
-MiniMax M2 Parser - A unified parser for MiniMax M2 models.
-
-This parser combines the existing MiniMaxM2ReasoningParser and
-MinimaxM2ToolParser into a single unified interface by delegating
-to those implementations.
-"""
-
-from vllm.logger import init_logger
-from vllm.parser.abstract_parser import DelegatingParser
-from vllm.reasoning.minimax_m2_reasoning_parser import MiniMaxM2ReasoningParser
-from vllm.tokenizers import TokenizerLike
-from vllm.tool_parsers.abstract_tool_parser import (
- Tool,
-)
-from vllm.tool_parsers.minimax_m2_tool_parser import MinimaxM2ToolParser
-
-logger = init_logger(__name__)
-
-
-class MiniMaxM2Parser(DelegatingParser):
- """
- Unified parser for MiniMax M2 models that handles both reasoning
- extraction and tool call parsing.
-
- This parser delegates to the existing implementations:
- - MiniMaxM2ReasoningParser for reasoning extraction
- - MinimaxM2ToolParser for tool call parsing
-
- MiniMax M2 models have two special behaviors:
- 1. Reasoning: They don't generate start token, only end
- token. All content before is reasoning, content after is the
- actual response.
- 2. Tool Calls: They use ... tags
- with ... and ...
- syntax.
- """
-
- # Class-level parser classes for compatibility
- reasoning_parser_cls = MiniMaxM2ReasoningParser
- tool_parser_cls = MinimaxM2ToolParser
-
- def __init__(
- self,
- tokenizer: TokenizerLike,
- tools: list[Tool] | None = None,
- *args,
- **kwargs,
- ):
- super().__init__(tokenizer, *args, **kwargs)
-
- # Initialize the underlying parsers
- self._reasoning_parser = MiniMaxM2ReasoningParser(tokenizer, *args, **kwargs)
- self._tool_parser = MinimaxM2ToolParser(tokenizer, tools)
-
- logger.debug(
- "vLLM Successfully initialized parser %s!", self.__class__.__name__
- )
diff --git a/vllm/parser/parser_manager.py b/vllm/parser/parser_manager.py
index f8bded62d590..7afd39d4feab 100644
--- a/vllm/parser/parser_manager.py
+++ b/vllm/parser/parser_manager.py
@@ -3,14 +3,9 @@
from __future__ import annotations
-import importlib
-import os
-from collections.abc import Callable
from typing import TYPE_CHECKING
from vllm.logger import init_logger
-from vllm.utils.collection_utils import is_list_of
-from vllm.utils.import_utils import import_from_path
if TYPE_CHECKING:
from vllm.parser.abstract_parser import Parser
@@ -22,170 +17,10 @@
class ParserManager:
"""
- Central registry for Parser implementations.
-
- Supports two registration modes:
- - Eager registration via `register_module`
- - Lazy registration via `register_lazy_module`
+ Provides a unified Parser by composing individual reasoning and tool
+ parsers from their respective registries.
"""
- parsers: dict[str, type[Parser]] = {}
- lazy_parsers: dict[str, tuple[str, str]] = {} # name -> (module_path, class_name)
-
- @classmethod
- def get_parser_internal(cls, name: str) -> type[Parser]:
- """
- Retrieve a registered or lazily registered Parser class.
-
- Args:
- name: The registered name of the parser.
-
- Returns:
- The Parser class.
-
- Raises:
- KeyError: If no parser is found under the given name.
- """
- if name in cls.parsers:
- return cls.parsers[name]
-
- if name in cls.lazy_parsers:
- return cls._load_lazy_parser(name)
-
- registered = ", ".join(cls.list_registered())
- raise KeyError(f"Parser '{name}' not found. Available parsers: {registered}")
-
- @classmethod
- def _load_lazy_parser(cls, name: str) -> type[Parser]:
- """Import and register a lazily loaded parser."""
- from vllm.parser.abstract_parser import Parser
-
- module_path, class_name = cls.lazy_parsers[name]
- try:
- mod = importlib.import_module(module_path)
- parser_cls = getattr(mod, class_name)
- if not issubclass(parser_cls, Parser):
- raise TypeError(
- f"{class_name} in {module_path} is not a Parser subclass."
- )
- cls.parsers[name] = parser_cls # cache
- return parser_cls
- except Exception as e:
- logger.exception(
- "Failed to import lazy parser '%s' from %s: %s",
- name,
- module_path,
- e,
- )
- raise
-
- @classmethod
- def _register_module(
- cls,
- module: type[Parser],
- module_name: str | list[str] | None = None,
- force: bool = True,
- ) -> None:
- """Register a Parser class immediately."""
- from vllm.parser.abstract_parser import Parser
-
- if not issubclass(module, Parser):
- raise TypeError(
- f"module must be subclass of Parser, but got {type(module)}"
- )
-
- if module_name is None:
- module_names = [module.__name__]
- elif isinstance(module_name, str):
- module_names = [module_name]
- elif is_list_of(module_name, str):
- module_names = module_name
- else:
- raise TypeError("module_name must be str, list[str], or None.")
-
- for name in module_names:
- if not force and name in cls.parsers:
- existed = cls.parsers[name]
- raise KeyError(f"{name} is already registered at {existed.__module__}")
- cls.parsers[name] = module
-
- @classmethod
- def register_lazy_module(cls, name: str, module_path: str, class_name: str) -> None:
- """
- Register a lazy module mapping for delayed import.
-
- Example:
- ParserManager.register_lazy_module(
- name="minimax_m2",
- module_path="vllm.parser.minimax_m2_parser",
- class_name="MiniMaxM2Parser",
- )
- """
- cls.lazy_parsers[name] = (module_path, class_name)
-
- @classmethod
- def register_module(
- cls,
- name: str | list[str] | None = None,
- force: bool = True,
- module: type[Parser] | None = None,
- ) -> type[Parser] | Callable[[type[Parser]], type[Parser]]:
- """
- Register a Parser class.
-
- Can be used as a decorator or called directly.
-
- Usage:
- @ParserManager.register_module("my_parser")
- class MyParser(Parser):
- ...
-
- Or:
- ParserManager.register_module(module=MyParser)
- """
- if not isinstance(force, bool):
- raise TypeError(f"force must be a boolean, but got {type(force)}")
-
- # Immediate registration
- if module is not None:
- cls._register_module(module=module, module_name=name, force=force)
- return module
-
- # Decorator usage
- def _decorator(obj: type[Parser]) -> type[Parser]:
- module_path = obj.__module__
- class_name = obj.__name__
-
- if isinstance(name, str):
- names = [name]
- elif name is not None and is_list_of(name, str):
- names = name
- else:
- names = [class_name]
-
- for n in names:
- cls.lazy_parsers[n] = (module_path, class_name)
-
- return obj
-
- return _decorator
-
- @classmethod
- def list_registered(cls) -> list[str]:
- """Return names of all registered parsers."""
- return sorted(set(cls.parsers.keys()) | set(cls.lazy_parsers.keys()))
-
- @classmethod
- def import_parser(cls, plugin_path: str) -> None:
- """Import a user-defined parser from an arbitrary path."""
- module_name = os.path.splitext(os.path.basename(plugin_path))[0]
- try:
- import_from_path(module_name, plugin_path)
- except Exception:
- logger.exception(
- "Failed to load module '%s' from %s.", module_name, plugin_path
- )
-
@classmethod
def get_tool_parser(
cls,
@@ -246,12 +81,10 @@ def get_parser(
model_name: str | None = None,
) -> type[Parser] | None:
"""
- Get a unified Parser that handles both reasoning and tool parsing.
+ Get a Parser that handles both reasoning and tool parsing.
- This method checks if a unified Parser exists that can handle both
- reasoning extraction and tool call parsing. If no unified parser
- exists, it creates a DelegatingParser that wraps the individual
- reasoning and tool parsers.
+ Composes individual reasoning and tool parsers into a single
+ DelegatingParser subclass.
Args:
tool_parser_name: The name of the tool parser.
@@ -262,37 +95,9 @@ def get_parser(
Returns:
A Parser class, or None if neither parser is specified.
"""
- from vllm.parser.abstract_parser import _WrappedParser
-
if not tool_parser_name and not reasoning_parser_name:
return None
- # Strategy 1: If both names match, check for a unified parser with that name
- if tool_parser_name and tool_parser_name == reasoning_parser_name:
- try:
- parser = cls.get_parser_internal(tool_parser_name)
- logger.info(
- "Using unified parser '%s' for both reasoning and tool parsing.",
- tool_parser_name,
- )
- return parser
- except KeyError:
- pass # No unified parser with this name
-
- # Strategy 2: Check for parser with either name
- for name in [tool_parser_name, reasoning_parser_name]:
- if name:
- try:
- parser = cls.get_parser_internal(name)
- logger.info(
- "Using unified parser '%s' for reasoning and tool parsing.",
- name,
- )
- return parser
- except KeyError:
- pass
-
- # Strategy 3: Create a DelegatingParser with the individual parser classes
reasoning_parser_cls = cls.get_reasoning_parser(reasoning_parser_name)
tool_parser_cls = cls.get_tool_parser(
tool_parser_name, enable_auto_tools, model_name
@@ -301,8 +106,13 @@ def get_parser(
if reasoning_parser_cls is None and tool_parser_cls is None:
return None
- # Set the class-level attributes on the imported _WrappedParser
- _WrappedParser.reasoning_parser_cls = reasoning_parser_cls
- _WrappedParser.tool_parser_cls = tool_parser_cls
+ from vllm.parser.abstract_parser import DelegatingParser
+
+ r_cls = reasoning_parser_cls
+ t_cls = tool_parser_cls
+
+ class _Parser(DelegatingParser):
+ reasoning_parser_cls = r_cls
+ tool_parser_cls = t_cls
- return _WrappedParser
+ return _Parser