Repository navigation
[Rust Frontend] Split argument grammars into schema resolution and per-model rendering - #59395
Merged
Merged
Conversation
BugenZhao
added this pull request to stack #59397
September 30, 2026 08:21
BugenZhao
force-pushed
the
bz/argument-grammar
branch
2 times, most recently
from
September 30, 2026 09:13
faca9f7 to
9c0f582
Compare
BugenZhao
marked this pull request as ready for review
September 30, 2026 12:27
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
njhill
approved these changes
Oct 1, 2026
Member
Author
|
/ci run --allow-stale |
|
✅ Triggered Buildkite CI #92339 for commit
|
Add `output_grammar::arguments`, which builds the arguments grammar of keyed-parameter protocols from a tool's JSON schema. It owns what follows from the schema: declared-order parameters with required ones mandatory, additional parameters, and the value options of each parameter, with local `$ref`s resolved and unions, type arrays, and enums split per option. Each parser renders one parameter through the `ArgumentSyntax` trait; nested values stay JSON schema regions. Kimi K3 now renders its XTML arguments through this instead of handwritten typed arguments. Its parameters follow the declared order with required ones mandatory, and a string enum containing a close marker drops only that value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bugen Zhao <i@bugenzhao.com>
`ArgumentSyntax` methods no longer take the derivation context, which is now private to value options, and `raw_string` returns the format directly: a raw string's grammar need not admit whitespace just because a parser would strip it. The whitespace helpers and the conversion from a styled `JsonSchemaFormat`, which only the removed schema replacement used, go away with them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Collecting a value's options walked the schema as a tree, so a definition shared by several union alternatives expanded once per path to it. With four alternatives per level, twelve levels of references yielded 4^12 options and took over 30 s, from a few kilobytes of tool schema. Expand each reference target at most once per value, which a union allows because a repeated alternative adds no values. A schema whose references only lead back into it keeps accepting any value, and the depth bound now only guards the stack against long reference chains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bugen Zhao <i@bugenzhao.com>
BugenZhao
force-pushed
the
bz/argument-grammar
branch
from
October 1, 2026 05:09
2eb9de5 to
cc8bd98
Compare
Member
Author
|
/ci run |
|
✅ Triggered Buildkite CI #92346 for commit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Kimi K3 day-zero support (#50104) shipped with a handwritten argument grammar, because XGrammar had no K3 style. XGrammar 0.2.7 later added
kimi_k3_xml, but it still disagrees with the K3 parser: it pins thetypeattribute only for single-type schemas, so under a union it accepts a label that does not match the value, for exampletype="number"followed byabc, while the parser decodes each value by its label. The XGrammar 0.2.7 upgrade (#57272) therefore kept the handwritten grammar.The underlying problem is that depending on a backend style ties every new or fixed argument format to an XGrammar change, release, and upgrade. For keyed-parameter protocols, the frontend can build the argument grammar itself from structural-tag primitives, except for a few relaxations listed below. That work splits into two independent parts: resolving the JSON schema into parameters and their value options, which is generic, and rendering one parameter, which is model-specific. This PR makes that split and moves K3 onto it.
Stacked on #59393; only the last three commits belong to this PR. The open-PR search found no other Rust work on schema-derived argument grammars.
output_grammar::arguments::arguments(schema, syntax, options)owns the parameter list and value options:any_orderallows any sequence instead;$refs resolved andanyOf/oneOf, type arrays, and enums split per option, each with its JSON type;ArgumentSyntaxto render one parameter from its value options, using building blocks rather than configuration:ValueOption::json()for a JSON region with the root definitions,ValueOption::raw_string(terminators)for raw text, and small helpers (one_of,group_by,ParameterKey::tag). Values nested below a parameter stay XGrammar JSON schema regions.XtmlArgumentsreplaces the handwritten typed arguments. The call tag and the raw JSON alternative are unchanged. Behavior changes:$refs resolve before thetypelabel is chosen;properties: {}allows no parameters.Deliberate differences from XGrammar's XML styles, documented in the module: additional keys are not required to differ from declared ones, string
formatand length constraints andminProperties/maxPropertiesare not enforced, andallOfwith several schemas accepts any value. Recursive non-JSON nesting is out of scope.Test Plan
cd rust cargo nextest run -p vllm-parser -p vllm-chat cargo clippy --workspace --all-targets --all-features -- -D warningsTest Result
vllm-parser+vllm-chat: 874 tests passed, including all 36 XGrammar replay cases. K3 is stillfinal_output_onlyon this base, so its three cases are compiled only; [Rust Frontend] Build the Kimi K3 full-output grammar from its parser mode #59190 replays them from token zero.kimi_k3_xmlstyle. Both grammars judged 44 handwritten argument strings over 9 schemas, and they agreed on all but 6. In each of those 6,kimi_k3_xmlaccepted atypelabel that does not match the value, and this grammar rejected it. The samples come from a handwritten renderer, not the chat template, so this is supporting evidence only.AI assistance: implementation and validation assisted by Claude Code.
🤖 Generated with Claude Code