Skip to content

Conversation

n0gu-furiosa
Copy link
Contributor

@n0gu-furiosa n0gu-furiosa commented Jul 16, 2025

Purpose

When a user specifies tool_choice="required" with an empty tools, the OpenAI-compatible server does not currently handle this scenario in the most appropriate way. The response returned is as follows:

$ curl http://localhost:8000/v1/chat/completions -H 'Content-type: application/json' -d '
{
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "messages": [{"role": "user", "content": ""}],
    "tool_choice": "required",
    "tools": []
}'
{"object":"error","message":"Grammar error: Unsatisfiable schema: required item is unsatisfiable","type":"BadRequestError","param":null,"code":400}

This BadRequestError is not raised by the model validator but by the guided decoding backend, because the request generates an unsatisfiable JSON schema:

{"type":"array","minItems":1,"items":{"type":"object","anyOf":[]}

Although this behavior technically blocks invalid requests, it is not the most user-friendly. The issue becomes more problematic when stream=True: in this case, the request is not validated before upgrading to SSE. Instead, the error surfaces after the model has begun generating results:

$ curl http://localhost:8000/v1/chat/completions -H 'Content-type: application/json' -d '
{
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "messages": [{"role": "user", "content": ""}],
    "tool_choice": "required",
    "tools": [],
    "stream": true
}'
data: {"error": {"object": "error", "message": "Grammar error: Unsatisfiable schema: required item is unsatisfiable", "type": "BadRequestError", "param": null, "code": 400}}

data: [DONE]

This PR adds explicit validation of tool length when tool_choice="required".

Test Plan

Manually ran vllm serve meta-llama/Llama-3.1-8B-Instruct --enable-auto-tool-choice --tool-call-parser llama3_json and executed the curl command above.

Test Result

$ curl http://localhost:8000/v1/chat/completions -H 'Content-type: application/json' -d '
{
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "messages": [{"role": "user", "content": ""}],
    "tool_choice": "required",
    "tools": [],
    "stream": true
}'
{"object":"error","message":"[{'type': 'value_error', 'loc': ('body',), 'msg': 'Value error, When using `tool_choice` as \"required\", `tools` must contain at least one tool', 'input': {'model': 'meta-llama/Llama-3.1-8B-Instruct', 'messages': [{'role': 'user', 'content': ''}], 'tool_choice': 'required', 'tools': [], 'stream': True}, 'ctx': {'error': ValueError('When using `tool_choice` as \"required\", `tools` must contain at least one tool')}}]","type":"Bad Request","param":null,"code":400}

@n0gu-furiosa n0gu-furiosa requested a review from aarnphm as a code owner July 16, 2025 11:57
Copy link

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

Just a reminder: PRs would not trigger full CI run by default. Instead, it would only run fastcheck CI which starts running only a small and essential subset of CI tests to quickly catch errors. You can run other CI tests on top of those by going to your fastcheck build on Buildkite UI (linked in the PR checks section) and unblock them. If you do not have permission to unblock, ping simon-mo or khluu to add you in our Buildkite org.

Once the PR is approved and ready to go, your PR reviewer(s) can run CI to test the changes comprehensively before merging.

To run CI, PR reviewers can either: Add ready label to the PR or enable auto-merge.

🚀

@mergify mergify bot added the frontend label Jul 16, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an explicit validation check for tool_choice in the OpenAI server implementation. Specifically, it ensures that if tool_choice is set to "required", the tools list cannot be empty. This change prevents a downstream error from the guided decoding backend and provides a much clearer error message to the user, which is a significant improvement, especially for streaming responses. The implementation is correct, robust, and placed appropriately within the Pydantic model validator. I find no issues with the proposed change.

@n0gu-furiosa n0gu-furiosa changed the title [Frontend] Add explicit validation of tool length when tool_choice is required in OpenAI server [Frontend] Add explicit validation of tool length when tool_choice="required" in OpenAI server Jul 16, 2025
@n0gu-furiosa n0gu-furiosa force-pushed the tools-required-validation branch from 50f3403 to 7fb607c Compare July 16, 2025 12:08
@n0gu-furiosa n0gu-furiosa force-pushed the tools-required-validation branch from 7fb607c to c4e552b Compare July 16, 2025 12:10
Copy link
Contributor

@chaunceyjiang chaunceyjiang left a comment

Choose a reason for hiding this comment

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

Could you help verify how the OpenAI online service handles this situation?

@n0gu-furiosa
Copy link
Contributor Author

@chaunceyjiang I checked, and it turns out that the OpenAI API behaves as if tool_choice were "none" when tools is an empty list (or "auto", I believe they behave the same way in this context).

Request payload:

{
    "model": "gpt-4o",
    "messages": [
        {
            "role": "user",
            "content": "How is the weather today in Seoul?"
        }
    ],
    "stream": false,
    "tool_choice": "required",
    "tools": []
}

Response body:

    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "I currently don't have real-time weather data. You might want to check a reliable weather website or use a weather app for the most current information in Seoul.",
                "refusal": null,
                "annotations": []
            },
            "logprobs": null,
            "finish_reason": "stop"
        }
    ],

To align with this behavior, I think the simplest approach would be to override tool_choice to "none" if tool_choice is "required" and tools is an empty list, since much of the code assumes that tool_choice "required" must return tool calls. If you agree, I can update the PR this way. Do you have any thoughts or concerns?

@chaunceyjiang
Copy link
Contributor

Sounds good.
/cc @aarnphm

@n0gu-furiosa n0gu-furiosa changed the title [Frontend] Add explicit validation of tool length when tool_choice="required" in OpenAI server [Frontend] Align tool_choice="required" behavior with OpenAI when tools is empty Jul 17, 2025
@n0gu-furiosa
Copy link
Contributor Author

@chaunceyjiang Applied in 781573d. Also changed the title of this PR.

Updated test result:

$ curl http://localhost:8000/v1/chat/completions -H 'Content-type: application/json' -d '
{
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "messages": [{"role": "user", "content": "How is the weather today in Seoul?"}],
    "max_completion_tokens": 20,
    "tool_choice": "required",
    "tools": []
}'
{
  "id": "chatcmpl-40d600203d804754a8d4c58d4cae5c8b",
  "object": "chat.completion",
  "created": 1752726568,
  "model": "meta-llama/Llama-3.1-8B-Instruct",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "I'm not able to access real-time information. However, I can suggest some ways to find the",
        "refusal": null,
        "annotations": null,
        "audio": null,
        "function_call": null,
        "tool_calls": [],
        "reasoning_content": null
      },
...

@n0gu-furiosa
Copy link
Contributor Author

Hi @chaunceyjiang, just a friendly reminder to take a look at this when you have a chance. Thanks in advance.

Copy link
Contributor

@chaunceyjiang chaunceyjiang left a comment

Choose a reason for hiding this comment

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

Thanks~

LGTM.

/cc @DarkLight1337 @aarnphm

@DarkLight1337 DarkLight1337 enabled auto-merge (squash) July 29, 2025 01:52
@github-actions github-actions bot added the ready ONLY add when PR is ready to merge/full CI is needed label Jul 29, 2025
@DarkLight1337 DarkLight1337 merged commit 98df153 into vllm-project:main Aug 1, 2025
41 checks passed
@n0gu-furiosa n0gu-furiosa deleted the tools-required-validation branch August 1, 2025 07:55
wenscarl pushed a commit to wenscarl/vllm that referenced this pull request Aug 4, 2025
wenscarl pushed a commit to wenscarl/vllm that referenced this pull request Aug 4, 2025
juuice-lee pushed a commit to juuice-lee/vllm-moe.code that referenced this pull request Aug 5, 2025
x22x22 pushed a commit to x22x22/vllm that referenced this pull request Aug 5, 2025
x22x22 pushed a commit to x22x22/vllm that referenced this pull request Aug 5, 2025
x22x22 pushed a commit to x22x22/vllm that referenced this pull request Aug 5, 2025
npanpaliya pushed a commit to odh-on-pz/vllm-upstream that referenced this pull request Aug 6, 2025
jingyu-ml pushed a commit to jingyu-ml/vllm that referenced this pull request Aug 8, 2025
jinzhen-lin pushed a commit to jinzhen-lin/vllm that referenced this pull request Aug 9, 2025
noamgat pushed a commit to noamgat/vllm that referenced this pull request Aug 9, 2025
paulpak58 pushed a commit to paulpak58/vllm that referenced this pull request Aug 13, 2025
taneem-ibrahim pushed a commit to taneem-ibrahim/vllm that referenced this pull request Aug 14, 2025
BoyuanFeng pushed a commit to BoyuanFeng/vllm that referenced this pull request Aug 14, 2025
diegocastanibm pushed a commit to diegocastanibm/vllm that referenced this pull request Aug 15, 2025
epwalsh pushed a commit to epwalsh/vllm that referenced this pull request Aug 28, 2025
zhewenl pushed a commit to zhewenl/vllm that referenced this pull request Aug 28, 2025
googlercolin pushed a commit to googlercolin/vllm that referenced this pull request Aug 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
frontend ready ONLY add when PR is ready to merge/full CI is needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants