Skip to content

[Bugfix][Model Runner V2][Spec Decode] Fix off-by-one in bad_words draft-prefix matching - #52311

Merged
njhill merged 3 commits into
vllm-project:mainfrom
jyan-R:claude/workspacec-agent
Aug 16, 2026
Merged

njhill merged 3 commits into
vllm-project:mainfrom
jyan-R:claude/workspacec-agent

Conversation

@jyan-R

@jyan-R jyan-R commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Purpose

Fix an off-by-one in the spec-decode branch of _bad_words_kernel (vllm/v1/worker/gpu/sample/bad_words.py), present since the kernel was introduced in #33433.

The sampler passes input_ids gathered at logits_indices, so for a spec-decode request the per-request local layout is: local position 0 = last committed sampled token, local position j = draft token d_{j-1}. The sibling kernels index draft tokens accordingly (_penalties_kernel uses start_idx + prev_pos + 1; thinking budget's _load_effective_token uses + 1 with an explicit comment). _bad_words_kernel read input_ids_ptr + cur_req_first_pos + spec_offset without the + 1, so every draft-region read was shifted back by one token (the boundary token was read twice).

Consequences when bad_words and speculative decoding are combined and a multi-token bad word's prefix window reaches into the draft tokens:

  • prefix matches ending in the draft region are missed (the banned token can be sampled/accepted);
  • matches spanning the committed/draft boundary fire one verification row late;
  • the duplicated boundary read can spuriously match and ban an innocent token.

Single-token bad words and non-spec rows are unaffected. Model Runner V1 has a separate implementation and is unaffected.

Not a duplicate: searched open PRs/issues for bad_words + spec decode / off-by-one / _bad_words_kernel; no existing fix found (closest is #34213, which only touches penalties).

Test Plan

New unit test (first coverage for this kernel), following the pattern of tests/v1/worker/test_gpu_thinking_budget.py:

pytest -v tests/v1/worker/test_gpu_bad_words.py

Scenario: committed output [10, 11], draft tokens [12, 13]. The core regression case is the boundary-spanning prefix ([11, 12, 30] must mask at the row where the prefix completes, not one row later); the other three cases pin the remaining coverage axes: a positive match at draft offset >= 1 ([12, 13, 40]), a no-double-count guard for the boundary token ([11, 11, 50]), and a committed-only baseline ([10, 11, 60]). Assertions compare the full logits tensor.

Test Result

Ran on an A100 SXM4 80GB (torch 2.13.0+cu130, triton 3.7.1, Python 3.11), with the branch installed editable via VLLM_USE_PRECOMPILED (nightly x86_64 wheel 66728feb1):

  • With the fix (HEAD): 4 passed.
  • With the pre-fix kernel (parent commit's bad_words.py swapped in): 3 failed, 1 passed — exactly the three spec-branch cases fail; the committed-only baseline passes.
  • ruff check / ruff format --check pass on both changed files.
  • Cross-validation: the kernel's scalar loop was transcribed to pure Python and checked against an independent reference matcher over 20,000 randomized spec-decode scenarios; with the + 1 the kernel matches the reference in all cases, without it ~24% of scenarios diverge.

AI assistance was used for this change (Claude); I have reviewed every changed line.

@mergify mergify Bot added mrv2 Model Runner V2 specific bug Something isn't working labels Aug 14, 2026
…aft-prefix matching

The spec-decode branch of _bad_words_kernel read draft tokens from the
gathered input_ids at local position spec_offset, but local position 0
holds the last committed sampled token; draft token d_j lives at local
position j + 1, as _penalties_kernel and thinking_budget's
_load_effective_token already index. The shifted reads made prefix
matches reaching into the draft tokens miss, fire one row late, or
spuriously match by re-reading the boundary token.

Signed-off-by: jyan <r_02213@sjtu.edu.cn>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jyan-R
jyan-R force-pushed the claude/workspacec-agent branch from 42c8dab to e5a45e4 Compare August 14, 2026 09:56
@github-actions

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment /ci run whenever CI signals are needed.

Once the PR is approved or has the ready label, the PR author can also use /ci run, /ci retry, or /ci cancel. New commits do not start CI automatically.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban.

🚀

@jyan-R
jyan-R marked this pull request as ready for review August 14, 2026 10:21
Copilot AI lite review requested due to automatic review settings August 14, 2026 10:21

@claude claude 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.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes a speculative-decoding-only off-by-one in the Model Runner V2 Triton _bad_words_kernel so draft-prefix matching reads the correct draft tokens (avoiding missed masks and spurious boundary matches), and adds initial GPU unit coverage for this kernel.

Changes:

  • Adjust speculative branch indexing in _bad_words_kernel by + 1 to correctly align input_ids local layout for drafts.
  • Add a new CUDA/Triton-backed unit test suite covering committed-only, draft-only, and committed/draft-boundary bad-words prefix cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
vllm/v1/worker/gpu/sample/bad_words.py Fix speculative-draft token indexing in bad-words prefix matching and document the local input_ids layout.
tests/v1/worker/test_gpu_bad_words.py New GPU unit tests validating masking behavior across committed vs draft token boundaries for speculative decoding.
Suppressed comments (1)

tests/v1/worker/test_gpu_bad_words.py:62

  • idx_mapping_np / expanded_idx_mapping also hard-code the request index as 3, which couples this test to RequestState's internal slot allocation. Compute the request index from the created state and use it consistently for the mappings.
    idx_mapping_np = np.array([3], dtype=np.intp)
    expanded_idx_mapping = torch.tensor(
        [3] * num_logits, dtype=torch.int32, device=DEVICE
    )

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/v1/worker/test_gpu_bad_words.py
Signed-off-by: jyan <r_02213@sjtu.edu.cn>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@njhill njhill left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @jyan-R

@njhill njhill added the ready ONLY add when PR is ready to merge/full CI is needed label Aug 14, 2026
@github-actions

Copy link
Copy Markdown

@jyan-R, CI is now available for this PR.

  • /ci run starts a CI build.
  • /ci retry retries failed jobs in the CI build for the current PR head. If the current head has no CI build, it starts a new CI build for the current head containing only jobs that failed in the latest earlier CI build for this PR.
  • /ci cancel cancels scheduled or running CI builds for this PR branch.

@njhill

njhill commented Aug 14, 2026

Copy link
Copy Markdown
Member

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #83984 for commit 04777f0c269c.

@njhill
njhill enabled auto-merge (squash) August 14, 2026 22:28
@jyan-R

jyan-R commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Queued 2 failed job(s) for retry in Buildkite CI #83984.

@jyan-R

jyan-R commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

CI status note: the remaining red job Multi-Modal Models (Standard 4, other + whisper) failed twice (initial + retry) with an identical, PR-unrelated signature — gemma4 engine-core warmup crashing on a PyTorch-internal NVML assert (CUDACachingAllocator.cpp:1407), preceded by stale torch_compile_cache cubin misses on the node. The AMD mirror of the same suite passed in this build, and the jobs covering this PR's area (v1-sample-plus-logits, v1-others-cpu) are green. Filed #52403 to track the infra failure; not retrying further to avoid burning CI.

@jyan-R

jyan-R commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Build #83984 finished. Final tally: everything green except (a) cpu-language-generation-and-pooling-model-testsExpired, never picked up by an agent, so it has not actually run; and (b) the whisper job tracked in #52403. Issuing one /ci retry to requeue the expired job; the whisper job is expected to stay red unless the node's stale compile cache cleared (see #52403).
🤖 Posted by Claude Code on behalf of @jyan-R

@jyan-R

jyan-R commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Queued 2 failed job(s) for retry in Buildkite CI #83984.

@jyan-R

jyan-R commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Queued 1 failed job(s) for retry in Buildkite CI #83984.

@njhill
njhill merged commit 1b079c4 into vllm-project:main Aug 16, 2026
92 checks passed
zzzzwwjj pushed a commit to vllm-project/vllm-ascend that referenced this pull request Aug 19, 2026
#14433)

### What this PR does / why we need it?
同步上游更改
vllm-project/vllm#52311
### Does this PR introduce _any_ user-facing change?

### How was this patch tested?

- vLLM version: v0.27.1
- vLLM main:
vllm-project/vllm@58d3918

Signed-off-by: liyishi <1252651434@qq.com>
MmMmaru pushed a commit to jiaqi-lee/vllm-ascend that referenced this pull request Aug 19, 2026
vllm-project#14433)

### What this PR does / why we need it?
同步上游更改
vllm-project/vllm#52311
### Does this PR introduce _any_ user-facing change?

### How was this patch tested?

- vLLM version: v0.27.1
- vLLM main:
vllm-project/vllm@58d3918

Signed-off-by: liyishi <1252651434@qq.com>
zzzzwwjj pushed a commit to vllm-project/vllm-ascend that referenced this pull request Aug 20, 2026
…t token offset in bad words kernel (from #14433) (#14526)

Cherry-pick of PR #14433 onto `releases/v0.26.0rc`.

Original PR: #14433
Original author: @SOMEONEUNSEEN

---
### What this PR does / why we need it?
同步上游更改
vllm-project/vllm#52311
### Does this PR introduce _any_ user-facing change?

### How was this patch tested?


- vLLM version: v0.26.0
- vLLM main:
vllm-project/vllm@d02df74

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
zufangzhu pushed a commit to zufangzhu/vllm that referenced this pull request Aug 24, 2026
…aft-prefix matching (vllm-project#52311)

Signed-off-by: jyan <r_02213@sjtu.edu.cn>
Co-authored-by: jyan <r_02213@sjtu.edu.cn>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Signed-off-by: Zhu, Zufang <zufang.zhu@intel.com>
frankie-ys pushed a commit to Csrayz/vllm-ascend that referenced this pull request Aug 26, 2026
vllm-project#14433)

### What this PR does / why we need it?
同步上游更改
vllm-project/vllm#52311
### Does this PR introduce _any_ user-facing change?

### How was this patch tested?

- vLLM version: v0.27.1
- vLLM main:
vllm-project/vllm@58d3918

Signed-off-by: liyishi <1252651434@qq.com>
Leetrytry pushed a commit to Leetrytry/vllm-ascend that referenced this pull request Sep 11, 2026
…t token offset in bad words kernel (from vllm-project#14433) (vllm-project#14526)

Cherry-pick of PR vllm-project#14433 onto `releases/v0.26.0rc`.

Original PR: vllm-project#14433
Original author: @SOMEONEUNSEEN

---
### What this PR does / why we need it?
同步上游更改
vllm-project/vllm#52311
### Does this PR introduce _any_ user-facing change?

### How was this patch tested?


- vLLM version: v0.26.0
- vLLM main:
vllm-project/vllm@d02df74

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working mrv2 Model Runner V2 specific ready ONLY add when PR is ready to merge/full CI is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants