Repository navigation
[Mistral] Use get_grammar_for_chat_request in MistralParser and require mistral-common 1.13.0 - #64169
Conversation
6b527c6 to
6c2d7ee
Compare
Replace the manual get_lark_from_jinja / get_lark_for_json_schema routing in MistralParser.adjust_request with the single-pass mistral-common entry point GrammarFactory.get_grammar_for_chat_request, available from mistral-common 1.13.0. - Build the mistral-common ChatCompletionRequest with from_openai from the request's messages, tools, tool_choice and parallel_tool_calls, and let mistral-common pick the grammar mode. - Pass the client's response_format through, so the schema is enforced according to mistral-common's defaults (only when `strict` is true). structured_outputs.json keeps enforcing its schema. - Convert mistral-common validation errors to VLLMValidationError. - Set no grammar, and leave _grammar_from_parser unset, for text requests without tools and with auto tool choice. - Require mistral-common >= 1.13.0. It accepts every reasoning_effort level, so the test that rejected "medium" is replaced. Committed with AI assistance. Signed-off-by: Julien Denize <40604584+juliendenize@users.noreply.github.com>
6c2d7ee to
8ee9916
Compare
| "MistralParser only accepts `text`, `json_object` or " | ||
| f"`json_schema`, got {request.response_format=}" | ||
| ) | ||
| return request.response_format.model_dump(by_alias=True, exclude_none=True) |
There was a problem hiding this comment.
Question for maintainers: this passes the client's response_format to mistral-common as-is, so the schema follows mistral-common's defaults. It is enforced by the grammar only when json_schema.strict is true; with strict unset or false the grammar accepts any JSON object or array. That matches OpenAI's meaning of strict and the prompt rendering in vllm/renderers/mistral.py, which already passes the same field.
The trade-off: structured_outputs_from_response_format enforces the schema for other models whatever strict says, and most clients do not send strict. Before this PR, Mistral models also always enforced it. structured_outputs.json stays enforced here because it has no strict field.
Should Mistral follow strict (this PR), or keep enforcing the schema regardless, like other models?
| lark_grammar = grammar_factory.get_grammar_for_chat_request( | ||
| request=mistral_request, template=template | ||
| ) | ||
| except (ValidationError, InvalidRequestException) as e: |
There was a problem hiding this comment.
Question for maintainers: mistral-common validates the request here. Its pydantic.ValidationError (e.g. a schema that is not valid Draft 7 JSON Schema) and InvalidRequestException (e.g. a JSON response format with tools and tool_choice="required") both become VLLMValidationError, so the client gets a 400 with mistral-common's message instead of a 500.
Is surfacing mistral-common's messages directly OK, or would you rather validate these cases in vLLM's request validators so the messages and parameter fields match the other models?
There was a problem hiding this comment.
We should pass parameter field if you could narrow it down. The error message itself could be forwarded as long as it doesn't leak the user's message (since errors may be stored in logs)
There was a problem hiding this comment.
As discussed offline, we could follow up on this later
|
/ci run |
|
✅ @juliendenize, CI is now available for this PR.
|
|
✅ Triggered Buildkite CI #97962 for commit ℹ️ This PR is 46 commits behind upstream |
|
Hi @juliendenize, the pre-commit checks have failed. Please run: uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-filesThen, commit the changes and push to your branch. For future commits, |
|
/ci run |
|
✅ Triggered Buildkite CI #98205 for commit |
|
/ci retry |
|
✅ No failed, timed-out, or expired jobs need retrying: https://buildkite.com/vllm/ci/builds/98205 |
|
/ci run |
|
✅ CI is already running for this commit: https://buildkite.com/vllm/ci/builds/98205 |
|
/ci retry |
|
✅ No failed, timed-out, or expired jobs need retrying: https://buildkite.com/vllm/ci/builds/98205 |
Overview
Switch
MistralParser.adjust_requestto mistral-common's single-pass grammar entry pointGrammarFactory.get_grammar_for_chat_request(new in mistral-common 1.13.0) and requiremistral_common >= 1.13.0.Claims
ChatCompletionRequestwithfrom_openai(**request.model_dump(include={messages, tools, tool_choice, parallel_tool_calls}, exclude_none=True), response_format=...)and mistral-common renders the grammar. This replaces the manualget_lark_from_jinja/get_lark_for_json_schemarouting.response_formatis passed to mistral-common unchanged and follows its defaults: ajson_schemais enforced by the grammar only whenstrictis true; otherwise any JSON object or array is accepted.structured_outputs.jsonhas nostrictfield and stays enforced. See the open question for maintainers on_mistral_response_format.check_json_nestingand the errors for unsupportedstructured_outputs/response_formattypes stay in vLLM.ValidationError, e.g. an invalid JSON schema, andInvalidRequestException, e.g. a JSON response format with tools andtool_choice="required") surface asVLLMValidationError(400) instead of 500. See the open question for maintainers on theexceptline.mistral_commonto>= 1.13.0inrequirements/common.txtand the test requirements; the compiled test pins move to1.13.0. Its dependency floors are already met by the existing pins, so no other pin changes.Behaviour changes for Mistral grammar-capable tokenizers
tool_choiceauto/None: no grammar is attached and_grammar_from_parserstaysFalse(previously a text grammar was attached).tool_choice="required"or a named tool: on the base template the model can no longer write text before the tool call; a named choice pins the tool name. Think templates are unchanged.tool_choice="auto": the grammar allows only the JSON answer or tool calls; plain text is rejected.json_schemawithoutstrict: true: the grammar constrains output to any JSON object or array instead of the schema (previously always enforced).json_schemawith no schema: 400 (previously fell back tojson_object).parallel_tool_calls: null: treated as allowed, as in OpenAI (previouslyFalse).Validation
macOS CPU,
VLLM_TARGET_DEVICE=cpu HF_HUB_OFFLINE=1, mistral-common 1.13.0, xgrammar 0.2.8, huggingface_hub 1.31.0.mainget_lark_from_jinjanow requiresforce_tool_names, andreasoning_effort="medium"is now acceptedadjust_requestreturns forrequired, a named tool, and JSON schema + tools +auto. They fail when the grammar is changed back to allow free text before the tool call.strictreaches mistral-common unchanged, that the schema appears in the grammar only whenstrictis true, and that an invalid schema raisesVLLMValidationError.pre-commit --hook-stage manualpasses, including the fourpip-compilehooks. In the compiled test requirements onlymistral-commonmoves to1.13.0, andtyping-extensionsdrops it from its "via" list (mistral-common 1.13.0 no longer depends on it).Details
tests/parser/mistral/test_tool_calls.py: existingadjust_requesttests now patchget_grammar_for_chat_requestand assert the request it receives. Added grammar-content,strict, invalid-schema and no-grammar tests. Removed thenull_tool_choicecase of the parametrized grammar test, since that request now correctly gets no grammar.tests/v1/structured_output/test_backend_xgrammar_lark.py: passforce_tool_names=False, now required byget_lark_from_jinja. This is the only directget_lark_from_jinjacall left in vLLM.tests/tokenizers_/test_mistral.py: mistral-common 1.13.0 accepts all OpenAIreasoning_effortlevels, so the test asserting"medium"is rejected is replaced by a check that every level is accepted._DEFAULT_JSON_SCHEMAand the manualtool_choice/ message / tool conversion.get_grammar_for_chat_requestor the mistral-common 1.13.0 bump.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.