[Fix] Simple fix: pass tokenizer stop tokens to XGrammar so constrained requests can terminate - #31534
Open
qifanchriswu001 wants to merge 1 commit into
Open
[Fix] Simple fix: pass tokenizer stop tokens to XGrammar so constrained requests can terminate#31534qifanchriswu001 wants to merge 1 commit into
qifanchriswu001 wants to merge 1 commit into
Conversation
qifanchriswu001
requested review from
DarkSharpness,
JustinTong0323 and
hnyls2002
as code owners
July 17, 2026 04:56
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
4 tasks
Author
|
@hnyls2002 @DarkSharpness @JustinTong0323 would appreciate a review ^ |
21 tasks
qifanchriswu001
force-pushed
the
fix-xgrammar-eos-stop-tokens
branch
2 times, most recently
from
September 10, 2026 06:56
f1ea0c2 to
cc69a75
Compare
…can terminate XGrammar only unmasks stop tokens from the list SGLang passes it, but create_grammar_backend forwarded only the model config's eos_token_id. Server-side stop detection (Req._check_token_based_finish) additionally honors tokenizer.eos_token_id and tokenizer.additional_stop_token_ids, so any checkpoint whose real EOS lives only in tokenizer_config.json (common for fine-tunes shipped without generation_config.json) could never emit EOS under a grammar constraint and ran until max_new_tokens. Pass the union of all three sources instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
qifanchriswu001
force-pushed
the
fix-xgrammar-eos-stop-tokens
branch
from
September 10, 2026 06:56
cc69a75 to
336f9dd
Compare
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.
Motivation
Fixes #31533.
With the xgrammar backend, a structured-outputs request (regex / JSON schema / EBNF) never terminates when the model's real chat EOS is declared only in
tokenizer_config.jsonand not in the model config'seos_token_id— as with checkpoints fine-tuned from a base model and shipped withoutgeneration_config.json, whereconfig.jsonkeeps the base EOS. XGrammar only unmasks stop tokens from the list SGLang passes it, andcreate_grammar_backendforwarded onlymodel_config.hf_eos_token_id, while server-side stop detection (Req._check_token_based_finish) additionally honorstokenizer.eos_token_idandtokenizer.additional_stop_token_ids. Any EOS covered by stop detection but missing from the grammar's list stays masked at accepting states, so the request runs untilmax_new_tokens.See #31533 for a minimal repro that drives xgrammar exactly as the scheduler does, plus the production symptom (
17\n2\n3\n...at temperature 0 until the length cap; identical request without the constraint stops at 3 tokens). The fix has been verified end to end on the production setup — see Accuracy Tests below.Environment where this was hit in production:
lmsysorg/sglang:v0.5.15.post1-cu130(the affected code path is unchanged on currentmain, 37f94cb)config.jsoneos 248044<|endoftext|>, tokenizer eos<|im_end|>248046, nogeneration_config.jsonModifications
create_grammar_backend(xgrammar branch): pass the union of model-config EOS ids,tokenizer.eos_token_id, andtokenizer.additional_stop_token_idsasmodel_eos_token_ids— the same setReq._check_token_based_finishaccepts. When no EOS information exists at all, keep passingNoneso XGrammar auto-detects from the tokenizer.test/registered/unit/constrained/test_base_grammar_backend.pycovering the union and theNonepassthrough.Not addressed (possible follow-up): per-request
sampling_params.stop_token_idsstill does not reach the grammar matcher; supporting that would need per-requestoverride_stop_tokenson theGrammarMatcher.Accuracy Tests
No change to model forward or kernels. Verified end to end on the production setup (B200, SGLang nightly dev build 2026-06-19, xgrammar backend, speculative decoding disabled, temperature 0) by hand-applying the exact stop-token union this PR computes: adding a
generation_config.jsonwith"eos_token_id": [248046, 248044]to the affected checkpoint, which makesmodel_config.hf_eos_token_idcontain the tokenizer EOS — the same setcreate_grammar_backendpasses to xgrammar after this change. Everything else (image, weights, request) identical:17\n2\n3\n...,finish_reason=length, 64/64 tokens"17",finish_reason=stop, 3 tokens"17",finish_reason=stop, 3 tokensSpeed Tests and Profiling
Not applicable — a few set operations at grammar-backend construction time (once per server start).
Checklist
CI States
Latest PR Test (Base): ❌ Run #34447546015
Latest PR Test (Extra): ❌ Run #34447545847
Latest PR Test (AMD ROCm 10): ❌ Run #34447546000