-
-
Notifications
You must be signed in to change notification settings - Fork 17.7k
Fa/fix json schema + tool calls #41178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,13 +21,20 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from vllm.entrypoints.openai.responses.protocol import ResponsesRequest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from vllm.logger import init_logger | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from vllm.sampling_params import StructuredOutputsParams | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from vllm.tokenizers import TokenizerLike | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from vllm.tool_parsers.abstract_tool_parser import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Tool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ToolParser, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from vllm.tool_parsers.utils import partial_tag_overlap | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Marker the model emits to close the thinking section. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # FSM allows any content up to and including this marker before schema starts. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Used to separate reasoning from schema-conforming JSON output. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _THINKING_END_TRIGGER = "</think>" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _THINKING_END_BEGIN = "</think>\n\n" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger = init_logger(__name__) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -95,8 +102,50 @@ def adjust_request( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # setting skip_special_tokens=False ensures proper handling in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # transformers 5.x where decoding behavior may have changed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.skip_special_tokens = False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # When thinking mode is enabled, the chat template opens a <think> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # block before the first generated token. The base adjust_request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # installs a whole-sequence JSON schema constraint for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # `tool_choice: "required"` and named-function tool_choice, which | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # makes </think> unreachable in the constrained vocabulary. Wrap | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # the schema in a structural tag so the FSM only engages after | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # the model emits </think>\n\n. See vllm-project/vllm#33215. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isinstance(request, ChatCompletionRequest) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and self._thinking_enabled(request) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and request.structured_outputs is not None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and request.structured_outputs.json is not None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schema = request.structured_outputs.json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if isinstance(schema, str): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schema = json.loads(schema) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.structured_outputs = StructuredOutputsParams( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| structural_tag=json.dumps( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "triggers": [_THINKING_END_TRIGGER], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "structures": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "begin": _THINKING_END_BEGIN, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "schema": schema, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "end": "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+122
to
+135
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overwriting
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @staticmethod | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _thinking_enabled( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request: ChatCompletionRequest | ResponsesRequest, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Mirror DeepSeekV3ReasoningParser's thinking-enable detection.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chat_kwargs = getattr(request, "chat_template_kwargs", None) or {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return bool( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chat_kwargs.get("thinking", False) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or chat_kwargs.get("enable_thinking", False) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _generate_tool_call_id(self) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Generate a unique tool call ID.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return f"call_{uuid.uuid4().hex[:24]}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
adjust_requestimplementation has two significant issues:structured_outputssupport: It only checksrequest.response_format. If a user provides a JSON schema directly via thestructured_outputsparameter (a common vLLM-specific usage), this logic is skipped, and the model will likely fail to close the reasoning block because the schema constraint will block the</think>tag.request.structured_outputsobject with a new one. This causes the loss of any other structured output settings the user might have provided (e.g.,disable_any_whitespace,whitespace_pattern).You should check both
response_formatandstructured_outputs, and usevllm.config.utils.replaceto update the parameters while preserving existing settings.