Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion vllm/parser/abstract_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,18 @@ def adjust_request(
request = self._tool_parser.adjust_request(request)
return request

def _reasoning_enabled(self) -> bool:
"""Whether thinking is enabled for this request.

The reasoning parser is built per request from its
``chat_template_kwargs`` and already reflects the thinking state.
Defaults to ``True`` to match xgrammar's default.
"""
reasoner = self._reasoning_parser
if reasoner is None:
return False
return bool(getattr(reasoner, "thinking_enabled", True))

def _apply_structural_tag(
self, request: ChatCompletionRequest | ResponsesRequest
) -> ChatCompletionRequest | ResponsesRequest:
Expand All @@ -484,9 +496,14 @@ def _apply_structural_tag(
if not need_tool_calling:
return request

reasoning_enabled = self._reasoning_enabled()

# The grammar must include the reasoning section when the model thinks,
# else the FSM rejects the reasoning tokens at the reasoning-to-tool
# boundary.
structure_tag = self._tool_parser.get_structural_tag(
request,
reasoning=False,
reasoning=reasoning_enabled,
)
if structure_tag is None:
return request
Expand All @@ -499,6 +516,14 @@ def _apply_structural_tag(
request.text = None
else:
request.response_format = None

# The tag models the reasoning prefix, so enforce the grammar from the
# first token rather than deferring past a reasoning section. Otherwise
# the tokens sampled right after the reasoning-end marker escape the
# bitmask and a forced tool call can be violated. The attr is a
# ChatCompletionRequest PrivateAttr, hence the guard.
if reasoning_enabled and hasattr(request, "_grammar_from_tool_parser"):
request._grammar_from_tool_parser = True
Comment on lines 499 to +526

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clean up the comments in this PR? They are quite specific and sloppy

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed in 80e6fe4. Kept the non-obvious rationale (per-request parser construction, the PrivateAttr guard, why the grammar is enforced from the first token) and dropped the restatement and symptom narration. Logic unchanged.

return request

def extract_reasoning_streaming(
Expand Down
10 changes: 10 additions & 0 deletions vllm/parser/engine/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ def adjust_request(
def has_engine_confirmed_reasoning_end(self) -> bool:
return self._parser_engine.reasoning_ended

@property
def thinking_enabled(self) -> bool:
"""Whether thinking is enabled for this request.

Delegates to the wrapped parser engine, which derives it from the
request's ``chat_template_kwargs``. Defaults to ``True`` to match
xgrammar's default.
"""
return bool(getattr(self._parser_engine, "thinking_enabled", True))

def finish_streaming(self) -> DeltaMessage | None:
return self._parser_engine.finish_streaming()

Expand Down
Loading