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
)