Skip to content

Conversation

@chaunceyjiang
Copy link
Collaborator

@chaunceyjiang chaunceyjiang commented May 8, 2025

FIX #16887

Test

vllm serve stelterlab/Mistral-Small-24B-Instruct-2501-AWQ --tool-call-parser mistral   --enable-auto-tool-choice  --tokenizer-mode mistral --guided-decoding-backend xgrammar

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?",
    },
]

# Non-streaming test
chat_completion = client.chat.completions.create(
    messages=messages,
    model='',
    tools=tools,
    tool_choice="required",
    # tool_choice="auto",
    # extra_body=dict(guided_decoding_backend="outlines"),
)
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
# python test.py
Chat completion response:
Chat completion: ChatCompletion(id='chatcmpl-e61a0dac3e9343cebc6243c8549a245b', 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='a5Yaj3JNh', function=Function(arguments='{"city": "Berlin", "country": "Germany", "unit": "fahrenheit"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='K54xN6Hm0', function=Function(arguments='{"city": "Berlin", "country": "Germany", "days": 5, "unit": "fahrenheit"}', name='get_forecast'), type='function')], reasoning_content=None), stop_reason=None)], created=1746757583, model='stelterlab/Mistral-Small-24B-Instruct-2501-AWQ', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=71, prompt_tokens=384, total_tokens=455, completion_tokens_details=None, prompt_tokens_details=None), prompt_logprobs=None)
Tool calls: [ChatCompletionMessageToolCall(id='a5Yaj3JNh', function=Function(arguments='{"city": "Berlin", "country": "Germany", "unit": "fahrenheit"}', name='get_current_weather'), type='function'), ChatCompletionMessageToolCall(id='K54xN6Hm0', function=Function(arguments='{"city": "Berlin", "country": "Germany", "days": 5, "unit": "fahrenheit"}', name='get_forecast'), type='function')]

@github-actions
Copy link

github-actions bot commented May 8, 2025

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

🚀

@chaunceyjiang chaunceyjiang changed the title [Feature] mistral supports tool_choice: required [Feature] Support tool_choice: required when using Xgrammar as the StructuredOutputBackend. May 8, 2025
@mergify mergify bot added the frontend label May 9, 2025
@chaunceyjiang chaunceyjiang marked this pull request as ready for review May 9, 2025 02:20
@chaunceyjiang chaunceyjiang changed the title [Feature] Support tool_choice: required when using Xgrammar as the StructuredOutputBackend. [Feature][V1] Support tool_choice: required when using Xgrammar as the StructuredOutputBackend. May 9, 2025
@chaunceyjiang chaunceyjiang force-pushed the mistral_required branch 3 times, most recently from b1f1075 to 741ff88 Compare May 9, 2025 08:39
@russellb
Copy link
Member

russellb commented May 9, 2025

Where is the tool calling fix exactly? Is it that a bug fix was needed in xgrammar 0.19?

@chaunceyjiang
Copy link
Collaborator Author

chaunceyjiang commented May 11, 2025

Where is the tool calling fix exactly?

Here. @russellb

if self.tool_choice == "required":
# Pydantic schema generation cannot be used since the JSON schema
# has to be constructed for a specific instantiation of a tool list
# so that parameters of a function are correctly generated
# based on the chosen function name
def get_tool_schema(tool: ChatCompletionToolsParam) -> dict:
return {
"properties": {
"name": {
"type": "string",
"enum": [tool.function.name]
},
# parameters are always generated as '{}' in the final
# output if they are missing from the request
# (i.e. are None or '{}') so the schema is
# updated to produce an empty object in that case
"parameters": tool.function.parameters
if tool.function.parameters else {
"type": "object",
"properties": {}
}
},
"required": ["name", "parameters"]
}
json_schema = {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"anyOf": [get_tool_schema(tool) for tool in self.tools]
}
}
return json_schema

"tool_choice: required depends on minItems, but xgrammar v0.18 does not support it."

Is it that a bug fix was needed in xgrammar 0.19?

xgrammar 0.19 supports minItems.

@chaunceyjiang
Copy link
Collaborator Author

/cc @russellb @DarkLight1337 PTAL.

Copy link
Member

@russellb russellb left a comment

Choose a reason for hiding this comment

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

thanks!

@russellb russellb enabled auto-merge (squash) May 12, 2025 14:03
@github-actions github-actions bot added the ready ONLY add when PR is ready to merge/full CI is needed label May 12, 2025
@mergify
Copy link

mergify bot commented May 12, 2025

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @chaunceyjiang.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify bot added the needs-rebase label May 12, 2025
auto-merge was automatically disabled May 13, 2025 00:15

Head branch was pushed to by a user without write access

@chaunceyjiang
Copy link
Collaborator Author

Hi, @DarkLight1337 It seems that the failed CI checks are unrelated to my code. Could you help retry the CI?

@chaunceyjiang
Copy link
Collaborator Author

I think this can be merged. The failed CI seems to be environment-related. Other PRs have similar errors — for example, #18047 also failed on buildkite/ci/pr/distributed-tests.
@DarkLight1337

@vllm-bot vllm-bot merged commit dc1a821 into vllm-project:main May 13, 2025
85 of 89 checks passed
mawong-amd pushed a commit to ROCm/vllm that referenced this pull request May 14, 2025
zzzyq pushed a commit to zzzyq/vllm that referenced this pull request May 24, 2025
…the `StructuredOutputBackend`. (vllm-project#17845)

Signed-off-by: chaunceyjiang <[email protected]>
Signed-off-by: Yuqi Zhang <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/build frontend ready ONLY add when PR is ready to merge/full CI is needed structured-output v1

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: tool_choice: "required" does not work for mistral

3 participants