[Chore] Consolidate reasoning/tool parser attributes into unified Parser in chat serving (#45548)

Signed-off-by: sfeng33 <4florafeng@gmail.com>
This commit is contained in:
Flora Feng
2026-06-15 11:16:45 -04:00
committed by GitHub
parent 588db18362
commit 0d80979644
4 changed files with 47 additions and 45 deletions
@@ -1350,6 +1350,21 @@ class TestServingChatWithHarmony:
else serving_chat.chat_completion_full_generator
)
chat_template_kwargs = serving_chat._effective_chat_template_kwargs(req)
if stream:
extra_kwargs: dict[str, Any] = {
"chat_template_kwargs": chat_template_kwargs,
}
else:
parser = None
if serving_chat.parser_cls is not None:
parser = serving_chat.parser_cls(
tokenizer,
req.tools,
chat_template_kwargs=chat_template_kwargs,
)
extra_kwargs = {"parser": parser}
result = generator_func(
request=req,
result_generator=result_generator(),
@@ -1361,7 +1376,7 @@ class TestServingChatWithHarmony:
request_id=req.request_id,
model_name=req.model,
),
chat_template_kwargs=serving_chat._effective_chat_template_kwargs(req),
**extra_kwargs,
)
if stream:
@@ -25,7 +25,7 @@ from vllm.entrypoints.serve.utils.api_utils import get_max_tokens
from vllm.inputs import EngineInput
from vllm.logger import init_logger
from vllm.outputs import RequestOutput
from vllm.reasoning import ReasoningParser
from vllm.parser.abstract_parser import Parser
from vllm.tokenizers import TokenizerLike
from vllm.utils.async_utils import merge_async_iterators
from vllm.utils.collection_utils import as_list
@@ -119,14 +119,15 @@ class OpenAIServingChatBatch(OpenAIServingChat):
for messages in request.messages
]
reasoning_parser: ReasoningParser | None = None
if self.reasoning_parser_cls:
parser: Parser | None = None
if self.parser_cls is not None:
chat_template_kwargs = self._effective_chat_template_kwargs(
single_requests[0]
)
reasoning_parser = self.reasoning_parser_cls(
parser = self.parser_cls(
tokenizer,
chat_template_kwargs=chat_template_kwargs, # type: ignore[call-arg]
None, # tools
chat_template_kwargs=chat_template_kwargs,
)
render_result = await self.render_batch_chat_request(request)
@@ -194,7 +195,7 @@ class OpenAIServingChatBatch(OpenAIServingChat):
all_conversations,
tokenizer,
request_metadata,
reasoning_parser,
parser,
)
async def chat_completion_full_generator_batch(
@@ -206,7 +207,7 @@ class OpenAIServingChatBatch(OpenAIServingChat):
all_conversations: list[list[ConversationMessage]],
tokenizer: TokenizerLike,
request_metadata: RequestResponseMetadata,
reasoning_parser: ReasoningParser | None = None,
parser: Parser | None = None,
) -> ErrorResponse | ChatCompletionResponse:
"""Handle batched (non-streaming) chat completions.
@@ -262,12 +263,12 @@ class OpenAIServingChatBatch(OpenAIServingChat):
else:
logprobs = None
if reasoning_parser:
reasoning, content = reasoning_parser.extract_reasoning(
if parser is not None:
reasoning, content, _ = parser.parse(
output.text,
request=request, # type: ignore[arg-type]
)
if not getattr(request, "include_reasoning", True):
if not request.include_reasoning:
reasoning = None
else:
reasoning = None
@@ -955,6 +955,8 @@ class BatchChatCompletionRequest(OpenAIBaseModel):
temperature: float | None = 0.7
top_p: float | None = 1.0
user: str | None = None
tool_choice: Literal["none"] | None = "none"
include_reasoning: bool = True
# vLLM extensions
best_of: int | None = None
@@ -60,7 +60,6 @@ from vllm.logprobs import Logprob
from vllm.outputs import RequestOutput
from vllm.parser import ParserManager
from vllm.parser.abstract_parser import Parser
from vllm.reasoning import ReasoningParser
from vllm.renderers import ChatParams
from vllm.sampling_params import BeamSearchParams, SamplingParams
from vllm.tokenizers import TokenizerLike
@@ -145,17 +144,7 @@ class OpenAIServingChat(OpenAIServing):
self.enable_log_outputs = enable_log_outputs
self.enable_log_deltas = enable_log_deltas
# set up reasoning parser
self.reasoning_parser_cls = ParserManager.get_reasoning_parser(
reasoning_parser_name=reasoning_parser
)
# set up tool use
self.enable_auto_tools: bool = enable_auto_tools
self.tool_parser = ParserManager.get_tool_parser(
tool_parser_name=tool_parser,
enable_auto_tools=enable_auto_tools,
model_name=self.model_config.model,
)
self.parser_cls = ParserManager.get_parser(
tool_parser_name=tool_parser,
reasoning_parser_name=reasoning_parser,
@@ -164,8 +153,9 @@ class OpenAIServingChat(OpenAIServing):
is_harmony=self.model_config.hf_config.model_type == "gpt_oss",
)
if (
is_mistral_tool_parser(self.tool_parser)
and self.reasoning_parser_cls is not None
self.parser_cls is not None
and is_mistral_tool_parser(self.parser_cls.tool_parser_cls)
and self.parser_cls.reasoning_parser_cls is not None
):
from vllm.tool_parsers.mistral_tool_parser import MistralToolParser
@@ -267,11 +257,12 @@ class OpenAIServingChat(OpenAIServing):
tokenizer = self.renderer.tokenizer
assert tokenizer is not None
chat_template_kwargs = self._effective_chat_template_kwargs(request)
reasoning_parser: ReasoningParser | None = None
if self.reasoning_parser_cls:
reasoning_parser = self.reasoning_parser_cls(
parser: Parser | None = None
if self.parser_cls is not None:
parser = self.parser_cls(
tokenizer,
chat_template_kwargs=chat_template_kwargs, # type: ignore[call-arg]
request.tools,
chat_template_kwargs=chat_template_kwargs,
)
result = await self.render_chat_request(request)
if isinstance(result, ErrorResponse):
@@ -359,10 +350,8 @@ class OpenAIServingChat(OpenAIServing):
# `think?` rule that handles both reasoning and
# non-reasoning outputs.
reasoning_ended = True
elif reasoning_parser:
reasoning_ended = reasoning_parser.is_reasoning_end(
prompt_token_ids or []
)
elif parser is not None and parser.reasoning_parser is not None:
reasoning_ended = parser.is_reasoning_end(prompt_token_ids or [])
else:
reasoning_ended = None
@@ -378,7 +367,7 @@ class OpenAIServingChat(OpenAIServing):
reasoning_parser_kwargs={
"chat_template_kwargs": chat_template_kwargs,
}
if reasoning_parser
if parser is not None and parser.reasoning_parser is not None
else None,
)
@@ -408,7 +397,7 @@ class OpenAIServingChat(OpenAIServing):
conversation,
tokenizer,
request_metadata,
chat_template_kwargs=chat_template_kwargs,
parser=parser,
mm_token_counts=mm_token_counts,
)
@@ -835,7 +824,7 @@ class OpenAIServingChat(OpenAIServing):
conversation: list[ConversationMessage],
tokenizer: TokenizerLike,
request_metadata: RequestResponseMetadata,
chat_template_kwargs: dict[str, Any] | None = None,
parser: Parser | None = None,
mm_token_counts: dict[str, int] | None = None,
) -> ErrorResponse | ChatCompletionResponse:
created_time = int(time.time())
@@ -861,6 +850,9 @@ class OpenAIServingChat(OpenAIServing):
history_tool_call_cnt = 0
role = self.get_chat_request_role(request)
tool_parser_cls = (
self.parser_cls.tool_parser_cls if self.parser_cls is not None else None
)
for output in final_res.outputs:
# check for error finish reason and raise GenerationError
# finish_reason='error' indicates a retryable request-level internal error
@@ -880,14 +872,6 @@ class OpenAIServingChat(OpenAIServing):
else:
logprobs = None
parser: Parser | None = None
if self.parser_cls is not None:
parser = self.parser_cls(
tokenizer,
request.tools,
chat_template_kwargs=chat_template_kwargs,
)
if parser is not None:
reasoning, content, tool_calls = parser.parse(
output.text,
@@ -904,7 +888,7 @@ class OpenAIServingChat(OpenAIServing):
auto_tools_called = False
if (not self.enable_auto_tools or not self.tool_parser) and (
if (not self.enable_auto_tools or not tool_parser_cls) and (
not isinstance(request.tool_choice, ChatCompletionNamedToolChoiceParam)
and request.tool_choice != "required"
):
@@ -963,7 +947,7 @@ class OpenAIServingChat(OpenAIServing):
request.tools
and (request.tool_choice == "auto" or request.tool_choice is None)
and self.enable_auto_tools
and self.tool_parser
and tool_parser_cls
):
auto_tools_called = tool_calls is not None and len(tool_calls) > 0
if tool_calls: