Skip to content

[Bugfix]: Fix the incompatibility issue with tool_choice 'required' when Thinking is enabled - #19075

Merged
DarkLight1337 merged 2 commits into
vllm-project:mainfrom
chaunceyjiang:tool_call_required_reason
Jun 3, 2025
Merged

DarkLight1337 merged 2 commits into
vllm-project:mainfrom
chaunceyjiang:tool_call_required_reason

Conversation

@chaunceyjiang

@chaunceyjiang chaunceyjiang commented Jun 3, 2025 •

Copy link
Copy Markdown
Contributor

FIX #19051

Introduced by PR #16577.

@mergify mergify Bot added the frontend label Jun 3, 2025
@github-actions

github-actions Bot commented Jun 3, 2025

Copy link
Copy Markdown

👋 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.

🚀

…hen Thinking is enabled

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
@chaunceyjiang

Copy link
Copy Markdown
Contributor Author

test

vllm serve /home/jovyan/qwen3-8b --reasoning-parser qwen3 --max-num-seqs 4 --tool-call-parser hermes --enable-auto-tool-choice --served-model-name model --max-model-len 32648 --guided-decoding-backend xgrammar --max-model-len 32648   -tp 2
from lmformatenforcer.external.jsonschemaobject import JsonSchemaObject

from openai import OpenAI

openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8000/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "Get the current weather in a given location",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description":
                        "The city to find the weather for, e.g. 'Vienna'",
                        "default": "Vienna",
                    },
                    "country": {
                        "type":
                        "string",
                        "description":
                        "The country that the city is in, e.g. 'Austria'",
                    },
                    "unit": {
                        "type": "string",
                        "description":
                        "The unit to fetch the temperature in",
                        "enum": ["celsius", "fahrenheit"],
                    },
                },
                "required": ["country", "unit"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_forecast",
            "description": "Get the weather forecast for a given location",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description":
                        "The city to get the forecast for, e.g. 'Vienna'",
                        "default": "Vienna",
                    },
                    "country": {
                        "type":
                        "string",
                        "description":
                        "The country that the city is in, e.g. 'Austria'",
                    },
                    "days": {
                        "type":
                        "integer",
                        "description":
                        "Number of days to get the forecast for (1-7)",
                    },
                    "unit": {
                        "type": "string",
                        "description":
                        "The unit to fetch the temperature in",
                        "enum": ["celsius", "fahrenheit"],
                    },
                },
                "required": ["country", "days", "unit"],
            },
        },
    },
]

messages = [
    {
        "role": "user",
        "content": "Hi! How are you doing today?"
    },
    {
        "role": "assistant",
        "content": "I'm doing well! How can I help you?"
    },
    {
        "role":
        "user",
        "content":
        "Can you tell me what the current weather is in Berlin and the "\
        "forecast for the next 5 days, in fahrenheit?",
    },
]


chat_completion = client.chat.completions.create(
    messages=messages,
    model='',
    tools=tools,
    tool_choice="required"
)
print("Chat completion response:")
print(f"Chat completion: {chat_completion}")
for choice in chat_completion.choices:
    if choice.message.tool_calls:
        print(
            f"Tool calls: {choice.message.tool_calls}")
    else:
        print("No tool calls found.")
assert chat_completion.choices[0].message.tool_calls is not None
assert len(chat_completion.choices[0].message.tool_calls) > 0


output

Chat completion response:
Chat completion: ChatCompletion(id='chatcmpl-13503360686a458ea55e3a1f93897ea0', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='chatcmpl-tool-387aad93bc9345ac846534e05aa43b1d', function=Function(arguments='{"city": "Berlin", "country": "Germany", "unit": "fahrenheit"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='chatcmpl-tool-363045d0bf3d40cfa386329ec357aec2', function=Function(arguments='{"city": "Berlin", "country": "Germany", "days": 5, "unit": "fahrenheit"}', name='get_forecast'), type='function')], reasoning_content=None), stop_reason=None)], created=1748949549, model='model', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=530, prompt_tokens=474, total_tokens=1004, completion_tokens_details=None, prompt_tokens_details=None), prompt_logprobs=None, kv_transfer_params=None)
Tool calls: [ChatCompletionMessageToolCall(id='chatcmpl-tool-387aad93bc9345ac846534e05aa43b1d', function=Function(arguments='{"city": "Berlin", "country": "Germany", "unit": "fahrenheit"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='chatcmpl-tool-363045d0bf3d40cfa386329ec357aec2', function=Function(arguments='{"city": "Berlin", "country": "Germany", "days": 5, "unit": "fahrenheit"}', name='get_forecast'), type='function')]

@chaunceyjiang

Copy link
Copy Markdown
Contributor Author

/cc @meffmadd @aarnphm @DarkLight1337 PTAL.

@chaunceyjiang
chaunceyjiang marked this pull request as ready for review June 3, 2025 14:28

@aarnphm aarnphm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks. One comment abt the test model

Comment thread tests/entrypoints/openai/test_completion_with_function_calling.py Outdated
@chaunceyjiang
chaunceyjiang force-pushed the tool_call_required_reason branch from ec0560e to 3c670d5 Compare June 3, 2025 14:34
…hen Thinking is enabled

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
@chaunceyjiang
chaunceyjiang force-pushed the tool_call_required_reason branch from 3c670d5 to f8cb8df Compare June 3, 2025 14:39
@DarkLight1337
DarkLight1337 enabled auto-merge (squash) June 3, 2025 16:16
@github-actions github-actions Bot added the ready ONLY add when PR is ready to merge/full CI is needed label Jun 3, 2025
@DarkLight1337
DarkLight1337 merged commit 4de790f into vllm-project:main Jun 3, 2025
@chaunceyjiang
chaunceyjiang deleted the tool_call_required_reason branch June 4, 2025 03:18
huiqiwa pushed a commit to huiqiwa/vllm-fork that referenced this pull request Oct 21, 2025
…hen Thinking is enabled (vllm-project#19075)

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
0826joyce pushed a commit to 0826joyce/vllm-serving-optimization that referenced this pull request May 19, 2026
…hen Thinking is enabled (vllm-project#19075)

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
philippesic pushed a commit to philippesic/vllm-semantic-cache that referenced this pull request Jul 19, 2026
…hen Thinking is enabled (vllm-project#19075)

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
plasticchris pushed a commit to plasticchris/vllm that referenced this pull request Jul 20, 2026
…hen Thinking is enabled (vllm-project#19075)

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
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.

[Bug]: 400 response when using Qwen3 + reasoning + tool calling + tool_choice "required"

3 participants