Skip to content

fix(qwen3): separate EOS from request stop tokens - #865

Open
RicardoMin wants to merge 1 commit into
pegainfer-project:mainfrom
RicardoMin:fix/request-stop-token-policy
Open

fix(qwen3): separate EOS from request stop tokens#865
RicardoMin wants to merge 1 commit into
pegainfer-project:mainfrom
RicardoMin:fix/request-stop-token-policy

Conversation

@RicardoMin

@RicardoMin RicardoMin commented Aug 13, 2026

Copy link
Copy Markdown

Description

This PR introduces a typed stop contract for stepped engines and implements it end-to-end for Qwen3, currently the only model using the stepped-engine path.

I discovered the issue while validating Qwen3 through the OpenAI-compatible completions API: explicitly configured stop_token_ids did not terminate generation when the matching token was produced.

Problem

The stepped frontend did not carry primary EOS and request-level stop_token_ids as independent stopping conditions. As a result, Qwen3 could not distinguish whether generation stopped because of EOS or because of an explicit request stop token.

The intended contract is:

ignore_eos Primary EOS stops Explicit stop_token_ids stop
false Yes Yes
true No Yes

ignore_eos should only control primary EOS handling. It should not disable explicit stop tokens supplied by the request.

Changes

  • Added typed StopPolicy and StopCause contracts for stepped engines.
  • Propagated primary EOS and explicit stop tokens independently from the stepped frontend into Qwen3.
  • Applied the contract to Qwen3 prefill, decode, unified, and speculative paths.
  • Preserved the triggering token, its real logprob, and completion-token accounting.
  • Truncated speculative output after the first stopping token.
  • Made a token stop take precedence when it coincides with max_tokens.
  • Reported explicit stop tokens through stop_reason, while primary EOS has no stop_reason.
  • Rejected non-zero min_tokens before scheduler submission because stepped engines do not support its required logits masking yet.

Scope

This PR changes the stepped frontend contract and its Qwen3 implementation only.

Qwen3.5, Kimi-K2, DeepSeek-V2-Lite, GLM5.2, and Gemma4 still use the legacy EngineHandle path, so their runtime behavior is unchanged. If this contract direction is accepted, those model paths can be migrated and validated separately.

Validation

Check Result
cargo fmt --all -- --check Passed
git diff --check Passed
Frontend library tests 68 passed, 0 failed
Qwen3 library tests 92 passed, 0 failed
Real Qwen3-0.6B eager HTTP cases Passed
Eager mixed-policy concurrency 12/12 passed
CUDA Graph mixed-policy concurrency 12/12 passed
CUDA Graph EOS comparison Passed
Legacy interface compilation for Qwen3.5, DeepSeek-V2-Lite, Kimi-K2, and Gemma4 Passed

The real HTTP coverage included explicit stop tokens with both values of ignore_eos, length termination, streaming, include_stop_str_in_output, and primary-EOS behavior.

For stop_token_ids = [17], both values of ignore_eos produced:

token_ids         = [220, 16, 11, 220, 17]
finish_reason     = stop
stop_reason       = 17
completion_tokens = 5

Primary EOS was independently verified:

Case Result
ignore_eos = false Stopped at token 151645; finish_reason = stop; stop_reason = null
ignore_eos = true Continued past token 151645 and later finished with finish_reason = length

Known upstream limitation

The repository currently pins vLLM revision 8e61b646e2d157f9b93451fa048f9c8530c8a67b.

In that revision, when the first generated token is itself a stopping token, the detokenizer may incorrectly flush prompt text into the completion. This is a separate upstream issue fixed by vLLM PR #47707, so this PR does not add a local workaround.

Signed-off-by: RicardoMin <17879681016@163.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57270613ef

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread pegainfer-frontend/src/vllm/wire.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant