Skip to content

fix: stop-string check misses early matches during speculative decoding - #23802

Merged
hnyls2002 merged 5 commits into
sgl-project:mainfrom
xythink:fix/stop-str-speculative-window
Jun 9, 2026
Merged

hnyls2002 merged 5 commits into
sgl-project:mainfrom
xythink:fix/stop-str-speculative-window

Conversation

@xythink

@xythink xythink commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Motivation

When speculative decoding accepts multiple tokens in one step, _check_str_based_finish() can miss stop strings that appear early in the accepted batch.

Root cause: tail_str() decodes only the last stop_str_max_len + 1 tokens, but speculative decoding may accept far more tokens per step. A stop string near the beginning of the accepted tokens falls outside this window and is never detected.

Impact: Affects all speculative decoding backends (EAGLE/MTP/DFlash/NGRAM) when using string-based stop parameters. stop_token_ids is unaffected because _check_token_based_finish() already iterates over all newly accepted tokens.

Modifications

  • tail_str(new_accepted_len): expand the decode window to max(stop_str_max_len, new_accepted_len + stop_str_max_len)
  • _check_str_based_finish(new_accepted_len): pass through
  • check_finished(): pass new_accepted_len to string check

Three call sites changed, no new dependencies.

Checklist

  • Unit test added in test/registered/unit/managers/
  • Backward compatible (default new_accepted_len=1 preserves existing behavior for non-speculative paths)

CI States

Latest PR Test (Base): ⏳ Run #27187587256
Latest PR Test (Extra): ❌ Run #27187587213

…ve decoding

The tail_str() window for stop-string matching was sized only by
stop_str_max_len, ignoring how many tokens were accepted in one
speculative decoding step. When multiple tokens were accepted at once,
an early stop string could fall outside the tail window and never be
detected.

This fix passes new_accepted_len through to tail_str() so the window
always covers all newly accepted tokens plus the stop-string length.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@gq112 gq112 mentioned this pull request May 21, 2026
5 tasks
hnyls2002 added a commit that referenced this pull request Jun 9, 2026
Co-Authored-By: xythink <xythink@users.noreply.github.com>

@hnyls2002 hnyls2002 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

/tag-and-rerun-ci

@hnyls2002
hnyls2002 merged commit d145a61 into sgl-project:main Jun 9, 2026
57 of 73 checks passed
minkail-m pushed a commit to modal-projects/sglang that referenced this pull request Jul 17, 2026
…ng (sgl-project#23802)

Clean cherry-pick of upstream d145a61 (sgl-project/sglang PR sgl-project#23802)
onto our pre-landing DFlash spec branch. No deviations from the upstream
diff: the hunks applied cleanly and land in the same code paths — the
widened tail window in Req.tail_str / _check_str_based_finish is driven
by new_accepted_len from Req.update_finish_state, which our Spec V2
chunked-accept path (batch_result_processor._process_batch_result_decode)
already calls with the full accepted length. Spec V1 paths (eagle/ngram/
dflash verify loops) append + check one token at a time, so the default
new_accepted_len=1 keeps their behavior identical.

Co-authored-by: xythink <xythink@users.noreply.github.com>
Co-authored-by: hnyls2002 <lsyincs@gmail.com>
Co-authored-by: Liangsheng Yin <hnyls2002@gmail.com>
(cherry picked from commit d145a61)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
minkail-m pushed a commit to modal-projects/sglang that referenced this pull request Jul 17, 2026
Clean cherry-pick of upstream 2218622 (sgl-project/sglang PR sgl-project#25980),
companion to sgl-project#23802: when a stop string/regex matches inside a multi-token
accepted chunk, record Req.finished_len at the match position so tokens
accepted after the stop are not emitted, and make the detokenizer's
trim_matched_stop end the text exactly at the match (keeping the stop
itself under no_stop_trim). No deviations from the upstream diff: our
fork already has the finished_len machinery this hooks into
(Req.output_ids_through_stop, output_streamer truncation at finished_len),
so the fix composes without adaptation.

Co-authored-by: gss <2783977641@qq.com>
Co-authored-by: hnyls2002 <lsyincs@gmail.com>
Co-authored-by: Liangsheng Yin <hnyls2002@gmail.com>
(cherry picked from commit 2218622)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Chronostasys pushed a commit to MindLab-Research/sglang that referenced this pull request Aug 24, 2026
…ng (sgl-project#23802)

Co-authored-by: xythink <xythink@users.noreply.github.com>
Co-authored-by: hnyls2002 <lsyincs@gmail.com>
Co-authored-by: Liangsheng Yin <hnyls2002@gmail.com>
jakki-amd pushed a commit to jakki-amd/sglang that referenced this pull request Sep 9, 2026
…ng (sgl-project#23802)

Co-authored-by: xythink <xythink@users.noreply.github.com>
Co-authored-by: hnyls2002 <lsyincs@gmail.com>
Co-authored-by: Liangsheng Yin <hnyls2002@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants