Skip to content

[TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler#16594

Open
zhaoyangwang-nvidia wants to merge 6 commits into
NVIDIA:mainfrom
zhaoyangwang-nvidia:torch-sampler-no-repeat-ngram
Open

[TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler#16594
zhaoyangwang-nvidia wants to merge 6 commits into
NVIDIA:mainfrom
zhaoyangwang-nvidia:torch-sampler-no-repeat-ngram

Conversation

@zhaoyangwang-nvidia

@zhaoyangwang-nvidia zhaoyangwang-nvidia commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Dev Engineer Review

  • Added no_repeat_ngram_size support to TorchSampler, aligning with C++ banRepeatNgram and Hugging Face behavior:
    • Prevents sampling tokens that would recreate an existing n-gram in the generated sequence including prompt tokens.
    • Enforced per beam.
    • When n == 1, bans all previously seen tokens (unigram repeat prevention).
    • Feature is disabled for None or 0.
  • Refactored bad-word and no-repeat-ngram logic to use a shared suffix-rule ban framework:
    • Unified suffix-rule ban collection with a single shared flush via _apply_token_bans.
    • Added memoized token/context access to reduce host overhead.
    • Introduced _TokenBans to accumulate ban targets across unconditional, overlap-conditioned, and device-token-column cases.
    • Implemented incremental per-request n-gram state indexing via _NgramIndexCache for efficient updates.
    • Reused the existing neg_inf tensor for overlap conditional penalties to avoid extra allocations.
    • Updated _process_requests to compute per-request ngram_sizes and apply both features via shared collection/flush when overlap scheduler tracking is enabled.
    • Improved cache lifecycle by storing _NgramIndexCache in WeakKeyDictionary[LlmRequest, _NgramIndexCache] so state is reclaimed when requests are freed (including rollback scenarios).
  • Updated request plumbing:
    • executor_request_to_llm_request now reads executor_request.sampling_config.no_repeat_ngram_size, normalizes falsy/disabled values to None, and sets llm_request.py_no_repeat_ngram_size.
  • Documentation/API clarity:
    • Updated docs/source/features/sampling.md to document prompt-inclusive n-gram banning and that None/0 disables the restriction.
    • Updated SamplingParams docstring to explicitly describe the prompt-inclusive behavior and the default (None).

QA Engineer Review

  • Test code changes (CUDA unit tests) in tests/unittest/_torch/sampler/test_torch_sampler.py:
    • Modified TestApplyBadWords to use a new sampler helper for both fresh and stale-host paths.
    • Added CUDA-only TestApplyNoRepeatNgram with a MockLlmRequest and helpers to validate banned-logit columns and correct index/cache behavior.
    • Added 18 test cases covering: matching behavior, disabled requests, speculative decoding, multi-beam layouts, overlap handling, duplicate bans (no NaNs), incremental cache updates, cache growth/rollback invalidation, and stale-host execution paths (device hit/miss, mixed stale/fresh batches, window ending at device token).
  • Integration/manual coverage in tests/integration/test_lists/, test-db/, or qa/: not verifiable from the provided context (no test-list changes available).
    • Verdict: needs follow-up.

Description

SamplingParams.no_repeat_ngram_size was previously only honored by the C++ executor path
(banRepeatNgram kernel via banWordsLayer); on the PyTorch backend it was silently ignored.
This PR implements it in TorchSampler.

Semantics

Same as the C++ banRepeatNgram kernel and HF's NoRepeatNGramLogitsProcessor: with
no_repeat_ngram_size = n, the last n - 1 tokens of the sequence (prompt included) form the
current prefix; the final token of every n-gram already present in the sequence whose first
n - 1 tokens match that prefix is masked to -inf, so no n-gram is ever generated twice.
n == 1 bans every token already present. The restriction is per-beam. None / 0 disables
the restriction (docs updated accordingly).

Implementation

Unified suffix-rule framework. Bad words and no-repeat ngram are both instances of one
abstraction — a rule (prefix, col) that bans token col when the sequence ends with prefix
(a bad word [t0..tk-1] is the static rule (word[:-1], word[-1]); n-grams are rules derived
from the sequence itself). A shared driver _collect_suffix_rule_bans now owns:

  • packed-row bookkeeping and suffix matching (feature collectors reduce to small rule generators);
  • the overlap-scheduler stale-host split: the host token list lags the device state by exactly one
    token d (in new_tokens_cuda[0, seq_slot, 0]), so a rule's match is split into a host-side
    prefix check plus a single device-side comparison of d against a host-known expected token,
    resolved at flush time with no device-to-host sync. Even the n-gram window whose banned token
    is d itself reduces to a host-known column (the match condition forces d == context[-1]).

Single flush. Bans from all features accumulate into one _TokenBans and flush with one
H2D transfer + one index_put_ per ban category (previously one set per feature).

Host cost: incremental per-request index. A naive per-step window scan is
O(batch × history) on the host. Two measured bottlenecks are removed:

  1. An (n-1)-gram → next tokens index (plus an (n-2)-gram pair index for the stale path) is
    cached on the request and extended only with newly covered windows: steady-state cost is
    O(new tokens) + an O(1) lookup. Rebuilt automatically on history shrinkage (speculative
    rollback) or ngram-size change; multi-beam requests keep the direct scan (diverging beam
    histories cannot share a per-request cache).
  2. get_tokens() copies the whole history through the bindings (~40 µs at 2048 tokens, measured).
    The index keeps a host-side token mirror grown via get_num_tokens / get_last_tokens scalar
    accessors, and the driver hands rule generators a memoized context thunk fetched only when a
    rule actually needs the full history — bad-words single-token rules and the single-beam n-gram
    path never pay the copy.

Performance

Isolated sampler benchmark (H200, bs=32, vocab 152k, 2048-token context per request, n=3,
p50 sampler-beyond-forward wall time, 1024 iters × 3 runs):

Variant p50 Overhead vs baseline
Baseline (feature off) 127 µs
Naive window scan 15,377 µs +15.2 ms
+ incremental n-gram index 1,414 µs +1.29 ms
+ token mirror & lazy context (this PR) 129 µs ≈ +2 µs

Limitations

  • Overlap + speculative decoding / beam search: as with bad words, the missing host tokens cannot
    be reconstructed by the single-token device comparison, so bans may be enforced one step late
    (existing warning_once extended to cover both features).
  • With speculation, later draft steps reuse the banned set computed from the host context
    (same approximation as bad words).

Test Coverage

  • tests/unittest/_torch/sampler/test_torch_sampler.py::TestApplyNoRepeatNgram (18 cases):
    bigram/trigram hit & miss, multiple matches, n == 1, prompt-internal n-grams, sequences
    shorter than n, disabled requests, multi-step (speculation) and multi-beam row layouts,
    the full stale-host overlap matrix (device hit/miss, window ending at the device token,
    mixed stale+fresh batches, duplicate bans producing no NaN), incremental cache growth,
    and cache rebuild on history rollback. All pass on H200.
  • Additional out-of-tree validation performed during development: 400 randomized cases
    cross-checked against a brute-force reference implementation (fresh path) and against
    "fresh path on the completed sequence" (stale path), plus 300 randomized bad-words
    equivalence cases guarding the shared-driver refactor.

PR Checklist

  • PR title follows the [JIRA/NVBUG/None][type] description format
  • Documentation updated (docs/source/features/sampling.md, SamplingParams docstring)
  • Unit tests added and passing
  • pre-commit hooks pass

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@zhaoyangwang-nvidia zhaoyangwang-nvidia changed the title [TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler [None][feat] Support beam search length_penalty/diversity_rate and validate early_stopping in TorchSampler Jul 21, 2026
@zhaoyangwang-nvidia zhaoyangwang-nvidia changed the title [None][feat] Support beam search length_penalty/diversity_rate and validate early_stopping in TorchSampler [13233][feat] Support no_repeat_ngram_size in TorchSampler Jul 21, 2026
@zhaoyangwang-nvidia
zhaoyangwang-nvidia marked this pull request as ready for review July 21, 2026 08:03
@zhaoyangwang-nvidia
zhaoyangwang-nvidia requested review from a team as code owners July 21, 2026 08:03
@zhaoyangwang-nvidia zhaoyangwang-nvidia changed the title [13233][feat] Support no_repeat_ngram_size in TorchSampler [TRTLLM-13233][feat] Support no_repeat_ngram_size in TorchSampler Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The Torch executor now propagates no_repeat_ngram_size into LLM requests. The Torch sampler combines no-repeat n-gram and bad-word bans, handles stale overlap-scheduler context, and adds CUDA coverage for fresh and stale token histories.

Changes

No-repeat n-gram sampling

Layer / File(s) Summary
Sampling parameter propagation
tensorrt_llm/_torch/pyexecutor/llm_request.py, tensorrt_llm/sampling_params.py, docs/source/features/sampling.md
Request conversion stores disabled values as None; documentation describes prompt-inclusive n-gram matching and None/0 disabling.
Unified token-ban collection
tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
Memoized context and _TokenBans support combined bad-word and no-repeat n-gram collection, including overlap-scheduler stale-context handling and weakly keyed n-gram caches.
Fresh and stale-context validation
tests/unittest/_torch/sampler/test_torch_sampler.py
CUDA tests cover n-gram sizes, cache changes, disabled requests, multi-step rows, stale device tokens, mixed batches, and duplicate bans.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: arysef

Sequence Diagram(s)

sequenceDiagram
  participant ExecutorRequest
  participant LlmRequest
  participant TorchSampler
  participant Logits
  ExecutorRequest->>LlmRequest: propagate no_repeat_ngram_size
  LlmRequest->>TorchSampler: provide request context and ngram size
  TorchSampler->>TorchSampler: collect bad-word and no-repeat n-gram bans
  TorchSampler->>Logits: apply accumulated token bans
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change and follows the required ticket/type format.
Description check ✅ Passed The description covers the issue, semantics, implementation, tests, and checklist, with only minor template duplication.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
tests/unittest/_torch/sampler/test_torch_sampler.py (1)

2789-2797: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider adding a multi-beam coverage case.

All _run/_run_stale invocations use num_beams == 1, so the multi-beam branch of _collect_no_repeat_ngram_bans.fresh_rules (the num_beams[index] != 1 path that scans each beam's context directly rather than the incremental index) is never exercised. Since beam search is a supported production path, a follow-up test that passes num_beams > 1 with divergent per-beam histories would close this gap.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 2789 -
2797, Add a focused multi-beam test using the existing _run or _run_stale
helpers, passing num_beams greater than one with distinct histories for each
beam. Assert the no-repeat n-gram bans for every beam, exercising
_collect_no_repeat_ngram_bans.fresh_rules through its num_beams[index] != 1 path
while preserving existing single-beam coverage.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 2789-2797: Add a focused multi-beam test using the existing _run
or _run_stale helpers, passing num_beams greater than one with distinct
histories for each beam. Assert the no-repeat n-gram bans for every beam,
exercising _collect_no_repeat_ngram_bans.fresh_rules through its
num_beams[index] != 1 path while preserving existing single-beam coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8beb9dee-254c-4001-aea5-a5e5f79b7bfe

📥 Commits

Reviewing files that changed from the base of the PR and between d1eda80 and baefd08.

📒 Files selected for processing (5)
  • docs/source/features/sampling.md
  • tensorrt_llm/_torch/pyexecutor/llm_request.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/sampling_params.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py

@QiJune
QiJune requested a review from lori-ren July 23, 2026 01:59
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch 2 times, most recently from fd73666 to 7bf7eb4 Compare July 23, 2026 07:02
Bans tokens that would recreate an n-gram already present in the
sequence (prompt included), matching the C++ banRepeatNgram kernel and
HF NoRepeatNGramLogitsProcessor semantics.

Both bad words and no-repeat ngram reduce to suffix rules
(prefix -> banned token); a shared driver owns packed-row bookkeeping,
suffix matching, and the overlap-scheduler stale-host split (host prefix
check + device token comparison). Bans from all features accumulate into
one _TokenBans and flush with a single H2D transfer + index_put_ per
category.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
The per-step window scan over the full token history was O(batch x
history) on the host (~15ms/step at bs=32 with 2048-token contexts on
H200, vs a ~128us sampler baseline). Two host-cost sources are removed:

- Cache an (n-1)-gram -> next-tokens index (plus an (n-2)-gram pair
  index for the stale overlap path) on the request and extend it only
  with newly covered windows: steady-state O(new tokens) + O(1) lookup.
- Avoid the full-history get_tokens() copy through the bindings (~40us
  at 2048 tokens per request per step): the index keeps a host-side
  token mirror grown via get_num_tokens/get_last_tokens scalar calls,
  and the suffix-rule driver hands generators a memoized context thunk
  fetched only when a rule actually needs the history.

Multi-beam requests keep the direct scan: diverging beam histories
cannot share the per-request cache. The cache is rebuilt on history
shrinkage (speculative rollback) or ngram-size change.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
Collector docstrings no longer repeat the shared driver's argument
documentation (the row-layout description now lives once, on
_collect_suffix_rule_bans), and inline comments that re-narrated the
docstrings are condensed. No functional change (AST-identical modulo
docstrings).

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from 7bf7eb4 to 940c68e Compare July 23, 2026 07:13
@zhaoyangwang-nvidia

Copy link
Copy Markdown
Collaborator Author

Hi @lori-ren all comments were addressed, please take a look.

@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from 940c68e to 646e258 Compare July 23, 2026 07:14

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
tests/unittest/_torch/sampler/test_torch_sampler.py (2)

2954-3024: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fresh-path assertions verified correct.

Manually traced the index construction against all bigram/trigram/unigram/incremental/rollback/multi-step assertions in this block — all match the expected _extend_no_repeat_ngram_index semantics.

One gap: test_disabled_request_untouched (Line 2989-2994) only covers ngram_size=None. The SamplingParams.no_repeat_ngram_size contract states both None and 0 disable the restriction — worth adding a 0 case alongside None to lock in that branch, since 0 exercises a different code path (an explicit falsy check) than None.

✅ Suggested additional case
     def test_disabled_request_untouched(self):
         active = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
-        disabled = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
-        logits = self._run([active, disabled], [2, None])
+        disabled_none = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
+        disabled_zero = self.MockLlmRequest(tokens=[1, 2, 3, 1, 2])
+        logits = self._run([active, disabled_none, disabled_zero], [2, None, 0])
         assert self._banned_cols(logits[0]) == {3}
-        assert self._banned_cols(logits[1]) == set()
+        assert self._banned_cols(logits[1]) == set()
+        assert self._banned_cols(logits[2]) == set()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 2954 -
3024, Add coverage to test_disabled_request_untouched for an explicitly
configured no_repeat_ngram_size of 0, alongside the existing None case. Verify
the request remains unchanged and no tokens are banned, while preserving the
current enabled-request assertion.

3025-3105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stale-host assertions verified correct; multi-beam divergence untested.

Traced the stale/device-boundary math for all 8 stale-path tests (including the subtle "virtual self-transition" case in test_stale_no_nan_on_duplicate_bans) — all check out.

_make_sampler (Line 2926-2937) fixes max_beam_width=1, and no test here passes num_beams > 1 with per-beam-divergent histories. Per _extend_no_repeat_ngram_index's own docstring, the incremental cache is single-beam only "as beam histories diverge," implying a separate/fallback code path handles beam_width > 1. That path isn't exercised in this file, despite the PR description listing "multi-beam layouts" among the 18 covered scenarios.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 3025 -
3105, Add coverage for divergent multi-beam histories in the stale-host sampler
tests; current _make_sampler fixes max_beam_width=1 and never exercises the
fallback path. Create a sampler/request setup with num_beams greater than one
and distinct per-beam token histories, then invoke _run_stale or the relevant
sampling flow and assert each beam receives the correct no-repeat-ngram bans.
Ensure the test validates the path used when _extend_no_repeat_ngram_index
cannot use its single-beam incremental cache.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 2954-3024: Add coverage to test_disabled_request_untouched for an
explicitly configured no_repeat_ngram_size of 0, alongside the existing None
case. Verify the request remains unchanged and no tokens are banned, while
preserving the current enabled-request assertion.
- Around line 3025-3105: Add coverage for divergent multi-beam histories in the
stale-host sampler tests; current _make_sampler fixes max_beam_width=1 and never
exercises the fallback path. Create a sampler/request setup with num_beams
greater than one and distinct per-beam token histories, then invoke _run_stale
or the relevant sampling flow and assert each beam receives the correct
no-repeat-ngram bans. Ensure the test validates the path used when
_extend_no_repeat_ngram_index cannot use its single-beam incremental cache.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: dcf99299-557a-45d6-b3fa-5a910e2cba56

📥 Commits

Reviewing files that changed from the base of the PR and between 7bf7eb4 and 646e258.

📒 Files selected for processing (5)
  • docs/source/features/sampling.md
  • tensorrt_llm/_torch/pyexecutor/llm_request.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/sampling_params.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tensorrt_llm/sampling_params.py
  • docs/source/features/sampling.md
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py

Replace the untyped 5-tuple ngram index cache with a typed _NgramIndexCache
dataclass, and store caches in a TorchSampler-owned WeakKeyDictionary keyed on
the request instead of injecting a _py_ngram_index_cache attribute into
LlmRequest. Weak keys let entries be reclaimed automatically when a request is
freed, so no manual cleanup is needed.

The no-repeat-ngram and bad-words token-ban helpers become instance methods and
call each other and the shared suffix-rule driver via self, dropping the
hardcoded TorchSampler.<method> references flagged in review.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from dcead0f to 58f6d9d Compare July 23, 2026 08:45

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unittest/_torch/sampler/test_torch_sampler.py (1)

3002-3007: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover the 0 disabled value.

The contract requires both None and 0 to disable the restriction, but this test only exercises None. Add a second disabled request using ngram_size=0 and assert that its logits remain unchanged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/sampler/test_torch_sampler.py` around lines 3002 -
3007, Extend test_disabled_request_untouched to include a request configured
with ngram_size=0, run it alongside the existing active and None-disabled
requests, and assert its banned columns are empty. Preserve the existing
assertions for the active request and the None-disabled request.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 2952-2960: Extend the tests around _run and _apply_no_repeat_ngram
with a num_beams=[2] case using distinct token histories for each beam.
Configure the mock request so get_tokens(beam_idx) returns beam-specific
sequences, then assert each logits row is banned only according to its
corresponding beam history.

---

Outside diff comments:
In `@tests/unittest/_torch/sampler/test_torch_sampler.py`:
- Around line 3002-3007: Extend test_disabled_request_untouched to include a
request configured with ngram_size=0, run it alongside the existing active and
None-disabled requests, and assert its banned columns are empty. Preserve the
existing assertions for the active request and the None-disabled request.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a326faf9-9f4a-43d5-a483-72fee52fadd5

📥 Commits

Reviewing files that changed from the base of the PR and between 646e258 and 58f6d9d.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py

Comment thread tests/unittest/_torch/sampler/test_torch_sampler.py
@zhaoyangwang-nvidia
zhaoyangwang-nvidia force-pushed the torch-sampler-no-repeat-ngram branch from 646e258 to dcead0f Compare July 23, 2026 08:52
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated


@dataclass(kw_only=True)
class _TokenBans:

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.

The sampler.sampler submodule is already very big. Perhaps the token ban stuff could have its own submodule?

Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
Comment thread tensorrt_llm/_torch/pyexecutor/sampler/sampler.py Outdated
if not active(index, r):
continue

if stale_by_one is not None and stale_by_one[index]:

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.

Where is fresh_rules processed? Somewhere, we should probably check if overlap scheduling is enabled or not and then route accordingly in here. After encapsulating the token-ban-handling, this might be as concise as choosing between two polymorphic variants of the ban handler (one variant corresponds to the "fresh" and the other to the "stale" paths in the current feature branch).

neg_inf = torch.full((), float("-inf"), dtype=logits.dtype, device=logits.device)

if bans.rows:
row_idx = torch.tensor(bans.rows, dtype=torch.long, pin_memory=prefer_pinned()).to(

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.

Could do row_idx, col_idx = torch.tensor([bans.rows, bans.cols], ...)...

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.

  • dev_row_idx and dev_slot_idx can be combined in the same manner
  • same for cond_row_idx, cond_col_idx, slot_idx

neg_inf,
torch.zeros((), dtype=logits.dtype, device=device),
)
logits.index_put_((cond_row_idx, cond_col_idx), penalty, accumulate=True)

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.

Consider cond_row_idx_sel, cond_col_idx_sel = cond_rowcol_idx[:, prev_tokens == expected], logits.index_put_((cond_row_idx_sel, cond_col_idx_sel), neginf)

new_tokens_cuda=new_tokens_cuda,
stale_by_one=stale_by_one,
)
req_num_steps_list = sampling_requests_metadata.req_num_steps.tolist()

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.

Only call .tolist() if needed (cf. following ifs)

Comment thread tests/unittest/_torch/sampler/test_torch_sampler.py
Rename _memoized_context to _memoizing_tokens_getter (it returns a token
getter, not a context manager). Give _NgramIndexCache per-field doc-strings
instead of a long class doc-string, and rename its indexed_upto field to
indexed_up_to. Annotate the DEFAULT_MAX_STOP_WORD_* class constants as
Final[int]. Drop the confusing "as beam histories diverge" clause from the
n-gram index doc-string and explain what "flush" means on _TokenBans.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
setdefault(key, set()) builds a throwaway empty set on every call, even for
keys already present. Switch the incrementally maintained fresh_idx / stale_idx
to defaultdict(set) so a set is created only on first insertion of a key. All
reads go through .get()/.items(), so the "read creates the key" behaviour of
defaultdict is not triggered.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
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.

3 participants