[Refactor] Remove dead code from parser infrastructure (#44279)

Signed-off-by: sfeng33 <4florafeng@gmail.com>
This commit is contained in:
Flora Feng
2026-06-02 12:08:27 -04:00
committed by GitHub
parent cab5c9a2a9
commit 478b49ddec
5 changed files with 35 additions and 328 deletions
+6 -4
View File
@@ -7,7 +7,7 @@ import pytest
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):
-18
View File
@@ -4,7 +4,6 @@
from vllm.parser.abstract_parser import (
DelegatingParser,
Parser,
_WrappedParser,
)
from vllm.parser.parser_manager import ParserManager
@@ -12,21 +11,4 @@ __all__ = [
"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()
+15 -41
View File
@@ -37,12 +37,11 @@ from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
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
from vllm.utils.mistral import is_mistral_tool_parser
@@ -91,19 +90,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."""
@@ -893,34 +898,3 @@ class DelegatingParser(Parser):
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)
-61
View File
@@ -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 <think> start token, only </think> end
token. All content before </think> is reasoning, content after is the
actual response.
2. Tool Calls: They use <minimax:tool_call>...</minimax:tool_call> tags
with <invoke name="...">...</invoke> and <parameter name="...">...</parameter>
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__
)
+14 -204
View File
@@ -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 @@ logger = init_logger(__name__)
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 @@ class ParserManager:
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 @@ class ParserManager:
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 @@ class ParserManager:
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
return _WrappedParser
r_cls = reasoning_parser_cls
t_cls = tool_parser_cls
class _Parser(DelegatingParser):
reasoning_parser_cls = r_cls
tool_parser_cls = t_cls
return _Parser