@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 752ddeacaa
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 13, 18:15 UTC

[Core] add support for reasoning parser plugins (#28075)

Signed-off-by: walter beller-morales <walter.beller.morales@gmail.com>

Walter Beller-Morales committed Nov 5, 2025 at 12:15 UTC 752ddeacaa7d759f5a9c105532e53762ff601721
6 files changed +62 -15
vllm/config/structured_outputs.py
+19
@@ -37,6 +37,9 @@ class StructuredOutputsConfig:
37 reasoning_parser: str = ""
38 """Select the reasoning parser depending on the model that you're using.
39 This is used to parse the reasoning content into OpenAI API format."""
40 + reasoning_parser_plugin: str = ""
41 + """Path to a dynamically reasoning parser plugin that can be dynamically
42 + loaded and registered."""
43 enable_in_reasoning: bool = False
44 """Whether to use structured input for reasoning."""
45
@@ -60,6 +63,22 @@ class StructuredOutputsConfig:
63
64 @model_validator(mode="after")
65 def _validate_structured_output_config(self) -> Self:
66 + # Import here to avoid circular import
67 + from vllm.reasoning.abs_reasoning_parsers import ReasoningParserManager
68 +
69 + if self.reasoning_parser_plugin and len(self.reasoning_parser_plugin) > 3:
70 + ReasoningParserManager.import_reasoning_parser(self.reasoning_parser_plugin)
71 +
72 + valid_reasoning_parsers = ReasoningParserManager.list_registered()
73 + if (
74 + self.reasoning_parser != ""
75 + and self.reasoning_parser not in valid_reasoning_parsers
76 + ):
77 + raise ValueError(
78 + f"invalid reasoning parser: {self.reasoning_parser} "
79 + f"(chose from {{ {','.join(valid_reasoning_parsers)} }})"
80 + )
81 +
82 if self.disable_any_whitespace and self.backend not in ("xgrammar", "guidance"):
83 raise ValueError(
84 "disable_any_whitespace is only supported for "
vllm/engine/arg_utils.py
+11 -4
@@ -80,7 +80,6 @@ from vllm.logger import init_logger
80 from vllm.platforms import CpuArchEnum, current_platform
81 from vllm.plugins import load_general_plugins
82 from vllm.ray.lazy_utils import is_in_ray_actor, is_ray_initialized
83 -from vllm.reasoning import ReasoningParserManager
83 from vllm.transformers_utils.config import (
84 get_model_path,
85 is_interleaved,
@@ -495,7 +494,7 @@ class EngineArgs:
494 VllmConfig, "structured_outputs_config"
495 )
496 reasoning_parser: str = StructuredOutputsConfig.reasoning_parser
498 -
497 + reasoning_parser_plugin: str | None = None
498 # Deprecated guided decoding fields
499 guided_decoding_backend: str | None = None
500 guided_decoding_disable_fallback: bool | None = None
@@ -707,10 +706,13 @@ class EngineArgs:
706 )
707 structured_outputs_group.add_argument(
708 "--reasoning-parser",
710 - # This choice is a special case because it's not static
711 - choices=list(ReasoningParserManager.list_registered()),
709 + # Choices need to be validated after parsing to include plugins
710 **structured_outputs_kwargs["reasoning_parser"],
711 )
712 + structured_outputs_group.add_argument(
713 + "--reasoning-parser-plugin",
714 + **structured_outputs_kwargs["reasoning_parser_plugin"],
715 + )
716 # Deprecated guided decoding arguments
717 for arg, type in [
718 ("--guided-decoding-backend", str),
@@ -1629,6 +1631,11 @@ class EngineArgs:
1631 if self.reasoning_parser:
1632 self.structured_outputs_config.reasoning_parser = self.reasoning_parser
1633
1634 + if self.reasoning_parser_plugin:
1635 + self.structured_outputs_config.reasoning_parser_plugin = (
1636 + self.reasoning_parser_plugin
1637 + )
1638 +
1639 # Forward the deprecated CLI args to the StructuredOutputsConfig
1640 so_config = self.structured_outputs_config
1641 if self.guided_decoding_backend is not None:
vllm/entrypoints/openai/api_server.py
+9 -3
@@ -1944,13 +1944,13 @@ def validate_api_server_args(args):
1944 f"(chose from {{ {','.join(valid_tool_parses)} }})"
1945 )
1946
1947 - valid_reasoning_parses = ReasoningParserManager.list_registered()
1947 + valid_reasoning_parsers = ReasoningParserManager.list_registered()
1948 if (
1949 reasoning_parser := args.structured_outputs_config.reasoning_parser
1950 - ) and reasoning_parser not in valid_reasoning_parses:
1950 + ) and reasoning_parser not in valid_reasoning_parsers:
1951 raise KeyError(
1952 f"invalid reasoning parser: {reasoning_parser} "
1953 - f"(chose from {{ {','.join(valid_reasoning_parses)} }})"
1953 + f"(chose from {{ {','.join(valid_reasoning_parsers)} }})"
1954 )
1955
1956
@@ -1964,6 +1964,9 @@ def setup_server(args):
1964 if args.tool_parser_plugin and len(args.tool_parser_plugin) > 3:
1965 ToolParserManager.import_tool_parser(args.tool_parser_plugin)
1966
1967 + if args.reasoning_parser_plugin and len(args.reasoning_parser_plugin) > 3:
1968 + ReasoningParserManager.import_reasoning_parser(args.reasoning_parser_plugin)
1969 +
1970 validate_api_server_args(args)
1971
1972 # workaround to make sure that we bind the port before the engine is set up.
@@ -2013,6 +2016,9 @@ async def run_server_worker(
2016 if args.tool_parser_plugin and len(args.tool_parser_plugin) > 3:
2017 ToolParserManager.import_tool_parser(args.tool_parser_plugin)
2018
2019 + if args.reasoning_parser_plugin and len(args.reasoning_parser_plugin) > 3:
2020 + ReasoningParserManager.import_reasoning_parser(args.reasoning_parser_plugin)
2021 +
2022 # Load logging config for uvicorn if specified
2023 log_config = load_log_config(args.log_config_file)
2024 if log_config is not None:
vllm/entrypoints/openai/run_batch.py
+3 -3
@@ -334,13 +334,13 @@ async def run_request(
334
335
336 def validate_run_batch_args(args):
337 - valid_reasoning_parses = ReasoningParserManager.list_registered()
337 + valid_reasoning_parsers = ReasoningParserManager.list_registered()
338 if (
339 reasoning_parser := args.structured_outputs_config.reasoning_parser
340 - ) and reasoning_parser not in valid_reasoning_parses:
340 + ) and reasoning_parser not in valid_reasoning_parsers:
341 raise KeyError(
342 f"invalid reasoning parser: {reasoning_parser} "
343 - f"(chose from {{ {','.join(valid_reasoning_parses)} }})"
343 + f"(chose from {{ {','.join(valid_reasoning_parsers)} }})"
344 )
345
346
vllm/reasoning/basic_parsers.py
+11 -5
@@ -3,15 +3,21 @@
3
4 from abc import abstractmethod
5 from collections.abc import Sequence
6 +from typing import TYPE_CHECKING, Any
7
7 -from vllm.entrypoints.openai.protocol import (
8 - ChatCompletionRequest,
9 - DeltaMessage,
10 - ResponsesRequest,
11 -)
8 +from vllm.entrypoints.openai.protocol import DeltaMessage
9 from vllm.reasoning.abs_reasoning_parsers import ReasoningParser
10 from vllm.transformers_utils.tokenizer import AnyTokenizer
11
12 +if TYPE_CHECKING:
13 + from vllm.entrypoints.openai.protocol import (
14 + ChatCompletionRequest,
15 + ResponsesRequest,
16 + )
17 +else:
18 + ChatCompletionRequest = Any
19 + ResponsesRequest = Any
20 +
21
22 class BaseThinkingReasoningParser(ReasoningParser):
23 """
vllm/v1/structured_output/__init__.py
+9
@@ -64,6 +64,15 @@ class StructuredOutputManager:
64 self.tokenizer = init_tokenizer_from_configs(
65 model_config=self.vllm_config.model_config
66 )
67 + reasoning_parser = (
68 + self.vllm_config.structured_outputs_config.reasoning_parser
69 + )
70 + reasoning_parser_plugin = (
71 + self.vllm_config.structured_outputs_config.reasoning_parser_plugin
72 + )
73 + if reasoning_parser_plugin and len(reasoning_parser_plugin) > 3:
74 + ReasoningParserManager.import_reasoning_parser(reasoning_parser_plugin)
75 +
76 reasoning_parser = (
77 self.vllm_config.structured_outputs_config.reasoning_parser
78 )