Skip to content

[TRTLLM-13212][refactor] Unify sampler ops and clean up dead code in TorchSampler#16365

Merged
zhaoyangwang-nvidia merged 5 commits into
NVIDIA:mainfrom
zhaoyangwang-nvidia:unify-sampler-ops
Jul 15, 2026
Merged

[TRTLLM-13212][refactor] Unify sampler ops and clean up dead code in TorchSampler#16365
zhaoyangwang-nvidia merged 5 commits into
NVIDIA:mainfrom
zhaoyangwang-nvidia:unify-sampler-ops

Conversation

@zhaoyangwang-nvidia

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

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Unified sampling APIs now support top-k, top-p, temperature, generators, and seed/offset-based randomness.
    • Added sampling options for top-k-only and top-p-only selection from probabilities.
    • Improved CUDA graph compatibility with stateless random seeds and offsets.
  • Bug Fixes

    • Improved output allocation metadata handling.
    • Corrected edge cases for sampling temperatures, log probabilities, finish reasons, and minimum-length penalties.
    • Updated speculative decoding sampling behavior for consistent top-k/top-p handling.
  • Refactor

    • Removed the standalone probability-computation operation in favor of the unified sampling flow.

Description

Follow-ups promised in the #15542 review (sampler ops centralization), plus dead-code
removal and small correctness/readability fixes found while auditing the sampler module.
All changes are behavior-preserving unless noted.

Dedup thin wrappers (ops/vanilla.py) — #15542 review follow-up

  • Fold the top_k_/top_p_/temperature_sampling_batch pass-through wrappers into
    top_k_top_p_sampling_batch via default arguments (top_k=None disables top-k,
    top_p=1 disables top-p).
  • Drop the pass-through greedy() wrapper; the greedy re-export now aliases
    greedy_search_sampling_batch directly.

Merge flashinfer op variants (ops/flashinfer.py) — #15542 review follow-up

  • Merge the seed/offset and generator variants of each sampling op wrapper into a
    single signature with keyword-only randomness arguments (6 wrappers -> 4).
    flashinfer's own API (>= 0.6.4) already accepts both in one signature, with explicit
    seed/offset taking precedence over generator; the split wrappers predated that.
  • Both randomness modes are preserved: seed/offset (device tensors) remain required
    for CUDA-graph capture and cross-rank determinism; generator remains for eager paths.
    The module docstring now documents this distinction.

Remove dead code

  • trtllm::compute_probs_from_logits_op chain, unused end to end (no Python or C++
    callers): the register_fake stub, the thop wrapper + TORCH_LIBRARY registration,
    and the exclusive kernel chain in dynamicTreeKernels.cu (topKProbStage1/2,
    invokeTopKTopPMaskingForProbs, applyTopKTopPForProbOp, computeSoftmaxForProbOp,
    computeProbsFromLogits) plus the torch/ATen includes only they used.
  • Four TorchSampler methods with zero callers: _handle_finish_reasons,
    _longest_stop_word_len, _requests_with_stop_words, _request_indices_with_stop_words.

Promote de-facto public internals

sampling_utils and sampler.py were reaching into underscore-private members of
ops.vanilla; promote the actually-shared surface and remove a duplicated constant:

  • _GREEDY_TEMPERATURE_THRESHOLD -> GREEDY_TEMPERATURE_THRESHOLD; DISABLE_TEMP_VAL
    in speculative/interface.py now derives from it (/ 10, value unchanged at 1e-5)
    so the sentinel and threshold cannot drift apart silently.
  • _safely_apply_temperature_inplace -> safely_apply_temperature_inplace,
    _Fusions -> Fusions.
  • Single definition for BEAM_SEARCH_PAD_TOKEN (in ops.vanilla, re-exported by the
    facade); previously defined independently in two files.
  • sampling_utils is now the only module importing the ops.* backend modules.

Correctness / readability fixes in TorchSampler

  • _get_logprobs_from_request: use forward slicing for the history view;
    [:, :-preallocate_extra_steps, :] silently yields an empty view when
    preallocate_extra_steps == 0 (the default), skipping history filling.
  • _apply_min_length_penalty: return None instead of echoing the in-place-mutated
    logits; defer the host tensor->list conversions until after the early-out (the common
    no-min-length path now skips them); flatten double-nested guard ifs into early
    continues, hoisting the per-request offset accumulation above the guards.
  • _flashinfer_check_nans: restore the explanatory comments lost in the [TRTLLM-13212][refactor] Centralize sampling logic, split backends into isolated modules #15542 move —
    the constant False deliberately keeps flashinfer's syncing check_nan path
    disabled while the async device-side assert provides NaN protection without stalling
    the pipeline (flashinfer issue Error occured when running medusa inference. #1575).
  • Fix seed/offset type hints on sampling_batch_spec_dec_one_model
    (Optional[int] -> Optional[torch.Tensor]; callers pass device tensors).

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.

…shinfer op variants, drop dead trtllm op

Follow-ups promised in PR NVIDIA#15542 review:

- vanilla: fold top_k_/top_p_/temperature_sampling_batch thin wrappers into
  top_k_top_p_sampling_batch via default args (top_k=None disables top-k,
  top_p=1 disables top-p); drop the pass-through greedy() wrapper
  (greedy re-export now aliases greedy_search_sampling_batch directly).
- flashinfer ops: merge the seed/offset and generator variants of each
  sampling op into a single signature with keyword-only randomness args,
  matching flashinfer's own unified API (>= 0.6.4 accepts both; explicit
  seed/offset takes precedence). 6 wrappers -> 4.
- Fix seed/offset type hints on sampling_batch_spec_dec_one_model
  (Optional[int] -> Optional[torch.Tensor]; callers pass device tensors
  for CUDA graph compatibility).
- Remove dead trtllm::compute_probs_from_logits_op chain (no callers
  anywhere): Python register_fake, thop wrapper + TORCH_LIBRARY
  registration, and the exclusive kernel chain in dynamicTreeKernels.cu
  (topKProbStage1/2, invokeTopKTopPMaskingForProbs, applyTopKTopPForProbOp,
  computeSoftmaxForProbOp, computeProbsFromLogits) plus now-unused
  torch/ATen includes.
- Refresh stale docstring references in sanitize_top_k.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
… public API

The sampling facade (sampling_utils) and sampler.py were reaching into
underscore-private members of ops.vanilla. Promote the de-facto public
surface and remove a duplicated constant:

- _GREEDY_TEMPERATURE_THRESHOLD -> GREEDY_TEMPERATURE_THRESHOLD; document
  the contract with the spec-decoding metadata layer, and derive
  DISABLE_TEMP_VAL in speculative/interface.py from it
  (GREEDY_TEMPERATURE_THRESHOLD / 10 == 1e-5, value unchanged) so the
  sentinel and the threshold can no longer drift apart silently.
- _safely_apply_temperature_inplace -> safely_apply_temperature_inplace.
- _Fusions -> Fusions (compiled logprobs fusion helpers used by
  sampler.py); per-method _impl internals stay private.
- Drop the duplicated BEAM_SEARCH_PAD_TOKEN definition in sampling_utils;
  ops.vanilla now holds the single definition and the facade re-exports it.

speculative/interface.py imports the threshold via the sampling_utils
facade, keeping sampling_utils the only importer of the ops backend
modules.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
Repo-wide audit found four methods with zero callers (no dynamic
dispatch, no test usage):

- _handle_finish_reasons: superseded by _handle_first_finish_reasons /
  direct _handle_finish_reasons_impl calls.
- _longest_stop_word_len: logic re-implemented in the live
  _check_stop_words_length.
- _requests_with_stop_words / _request_indices_with_stop_words: unused.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
…th penalty path, document flashinfer op contracts

- _get_logprobs_from_request: use forward slicing for the history view;
  [:, :-preallocate_extra_steps, :] silently yields an empty view when
  preallocate_extra_steps == 0 (the default), skipping history filling.
- _apply_min_length_penalty: return None instead of echoing the
  in-place-mutated logits; defer the num_steps/num_beams host
  tensor->list conversion until after the early-out; flatten the
  double-nested guard ifs into early continues, hoisting the per-request
  offset accumulation above the guards.
- _flashinfer_check_nans: restore the explanatory comments lost in the
  ops-module split (NVIDIA#15542): the constant False deliberately keeps
  FlashInfer's syncing check_nan path disabled, while the async
  device-side assert provides NaN protection without stalling the
  pipeline (see flashinfer issue NVIDIA#1575).
- ops/flashinfer: per-op docstrings pointing at the module-level
  generator-vs-seed/offset contract; note why the 1:1 pipeline-stage
  wrappers (mask/softmax/renorm) exist (guarded import, PDL decision).

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>

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

LGTM

@zhaoyangwang-nvidia

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59173 [ run ] triggered by Bot. Commit: 5cf0c6a Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59173 [ run ] completed with state FAILURE. Commit: 5cf0c6a
/LLM/main/L0_MergeRequest_PR pipeline #47677 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

The unified flashinfer ops now forward seed/offset (alongside generator) to
the underlying flashinfer.sampling.* calls, but the test mocks that patch
those functions only accepted generator, so the eager batched-sampling tests
failed with 'unexpected keyword argument seed'. Add seed/offset (defaulting to
None) to the four affected mock signatures.

Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
@zhaoyangwang-nvidia

Copy link
Copy Markdown
Collaborator Author

/bot run

@xxi-nv

xxi-nv commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59359 [ run ] triggered by Bot. Commit: 003a19f Link to invocation

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Failed to post review comments.

GitHub was unavailable or timed out while CodeRabbit was posting the review. Please request a new review later if the pull request still needs one. Use @coderabbitai full review to retry the review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a3abcdc6-025c-4b46-b1a3-f7fad2aae54a

📥 Commits

Reviewing files that changed from the base of the PR and between 771d385 and 003a19f.

📒 Files selected for processing (10)
  • cpp/tensorrt_llm/kernels/speculativeDecoding/dynamicTreeKernels.cu
  • cpp/tensorrt_llm/thop/dynamicTreeOp.cpp
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tensorrt_llm/_torch/custom_ops/cpp_custom_ops.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/speculative/interface.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
💤 Files with no reviewable changes (3)
  • tensorrt_llm/_torch/custom_ops/cpp_custom_ops.py
  • cpp/tensorrt_llm/thop/dynamicTreeOp.cpp
  • cpp/tensorrt_llm/kernels/speculativeDecoding/dynamicTreeKernels.cu
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Pre-commit Check
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (CODING_GUIDELINES.md)

**/*.py: Write Python 3.10+ code according to PEP 8, using four spaces and no tabs; follow the applicable formatter without unnecessary overrides.
Order imports according to the configured linter, never use wildcard imports, and keep __all__ updated for public interfaces.
Use snake_case for files, functions, methods, and variables; PascalCase for classes; and UPPER_SNAKE_CASE for constants. Prefix non-public names with _.
Avoid shadowing outer variables, initialize externally visible class members in the constructor, and use _host, _device, or _cuda suffixes when tensor location would otherwise be ambiguous.
Prefer docstrings for external interfaces, use Google-style docstrings, document public function arguments, and include tensor dimensions and constrained dtypes where applicable.
User-facing configuration classes must inherit from StrictBaseModel; use discriminated unions, Pydantic validators, model_post_init(), Field descriptions, typed fields, default_factory, and constrained or Literal types as appropriate.
Do not define Pydantic model __init__, to_dict, from_dict, or from_kwargs methods; use Pydantic validation and model_dump() instead.
Catch the narrowest possible exceptions, keep duck-typing try blocks minimal, prefer isinstance(), use built-in exception types, and use exceptions rather than return values for errors.
Annotate every function, use None for non-returning functions, avoid Any and unnecessary type ignores, prefer built-in generic types and |, use precise Callable types, and use Literal, overload, TypeVar, or Protocol when appropriate.

Avoid broad exception handling; catch specific exceptions instead of using bare except:.

Files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
**/*.{cpp,cc,cxx,h,hpp,py}

📄 CodeRabbit inference engine (AGENTS.md)

Read and follow CODING_GUIDELINES.md for all C++ and Python code changes.

Files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Add the NVIDIA copyright header to all new files and update the copyright year on modified files.
Keep each pull request focused on one concern and avoid scope creep; split unrelated changes into separate pull requests.
Treat TensorRT backend components as legacy: bug fixes are acceptable, but new features should target PyTorch or AutoDeploy.
Protected LLM API signatures require code-owner review because API stability tests enforce committed signatures.
Use git commit -s for DCO sign-off; do not attribute AI tools, add co-authors, or manually insert a sign-off line unless explicitly instructed.
If pre-commit hooks modify files, re-stage the modified files and commit again.
Target new features at the PyTorch or AutoDeploy execution paths rather than the legacy TensorRT backend.

Files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
tensorrt_llm/**/*.{py,cpp,cc,cxx,h,hpp}

📄 CodeRabbit inference engine (AGENTS.md)

Follow the Pydantic guidelines in CODING_GUIDELINES.md when editing or defining user-facing configuration classes, especially BaseLlmArgs or classes used in its fields.

Files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
tests/unittest/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Run unit tests with pytest tests/unittest/ after changes, using targeted test paths or -k filters when appropriate.

Files:

  • tests/unittest/_torch/sampler/test_torch_sampler.py
tests/**

⚙️ CodeRabbit configuration file

tests/**: Act as a QA engineer reviewing test changes and coverage for TensorRT-LLM.
Keep feedback actionable: suggest concrete list file names and whether
coverage is sufficient, insufficient, or needs follow-up outside the PR.

Files:

  • tests/unittest/_torch/sampler/test_torch_sampler.py
🧠 Learnings (14)
📚 Learning: 2026-02-13T10:15:37.120Z
Learnt from: ixlmar
Repo: NVIDIA/TensorRT-LLM PR: 11508
File: tests/unittest/_torch/sampler/test_beam_search_util.py:71-71
Timestamp: 2026-02-13T10:15:37.120Z
Learning: In TensorRT-LLM (Python requires >=3.10 and <4 as per setup.py), you can use Python 3.10+ features (e.g., PEP 585 generics like dict[str, int], list[str], etc.) throughout the codebase, and you do not need to add from __future__ import annotations. This applies to all Python files, including tests (e.g., tests/unittest/...); ensure tests and code consistently rely on Python 3.10+ features where applicable.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-04-24T23:54:27.755Z
Learnt from: hnover-nv
Repo: NVIDIA/TensorRT-LLM PR: 13453
File: tensorrt_llm/_torch/modules/mamba/causal_conv1d_triton.py:1016-1018
Timestamp: 2026-04-24T23:54:27.755Z
Learning: In this TensorRT-LLM codebase, treat `get_sm_version()` as an intentional implementation detail: it always queries device 0 to determine the SM version. Since the project assumes homogeneous GPU deployments (all GPUs in a node share the same SM capability), code review should not flag `get_sm_version()` calls for not using the current device, a tensor’s `.device`, or an explicit device argument.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-05-29T08:18:25.109Z
Learnt from: zhenhuaw-me
Repo: NVIDIA/TensorRT-LLM PR: 14733
File: tensorrt_llm/visual_gen/output.py:289-319
Timestamp: 2026-05-29T08:18:25.109Z
Learning: In this repository, Ruff rule A002 ("Function argument shadows a Python builtin") is not enabled in the configured Ruff rules. During code reviews, do not flag potential builtin-shadowing argument names (e.g., parameters like `format`) for A002, and do not recommend adding `# noqa: A002` suppressions—since the rule would not trigger here, such comments would be unnecessary noise, especially on public API surfaces.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-05-29T08:18:27.934Z
Learnt from: zhenhuaw-me
Repo: NVIDIA/TensorRT-LLM PR: 14733
File: examples/visual_gen/serve/async_video_gen.py:36-37
Timestamp: 2026-05-29T08:18:27.934Z
Learning: In NVIDIA/TensorRT-LLM, Ruff’s `[tool.ruff.lint].select` enables only `D, E, F, I, PLE, W` and does NOT include `A` (flake8-builtins). Therefore, builtin-name shadowing issues like `A002` (e.g., using `format` as a function parameter) should not be treated as lint failures for this repo during code review.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-06-25T13:04:55.284Z
Learnt from: taianz-nv
Repo: NVIDIA/TensorRT-LLM PR: 15555
File: tensorrt_llm/_torch/visual_gen/models/wan/wan_vae.py:23-24
Timestamp: 2026-06-25T13:04:55.284Z
Learning: For NVIDIA/TensorRT-LLM Python code, Ruff is configured in pyproject.toml to enable only {D, E, F, I, PLE, W}. In code review, do not request iterator/list-style cleanups tied to disabled Ruff rules B905, RUF005, or RUF007 (e.g., suggesting zip(..., strict=True), itertools.pairwise(), or rewriting `[x] + list_var` into unpacking) unless there is a separate, concrete correctness/performance/security reason. Prefer the repository’s existing plain forms for consistency.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tests/unittest/_torch/sampler/test_torch_sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-03-06T11:45:02.068Z
Learnt from: ixlmar
Repo: NVIDIA/TensorRT-LLM PR: 11983
File: tensorrt_llm/_torch/pyexecutor/llm_request.py:237-240
Timestamp: 2026-03-06T11:45:02.068Z
Learning: In tensorrt_llm/_torch/pyexecutor/llm_request.py, LogProbStorage.append() relies on next(iter(prob.values())).logprob to accumulate cum_log_probs when cum_log_probs is None. This path assumes prob is a non-empty dict because TorchSampler places the sampled logprob first when num_logprobs >= 0. Therefore, no guard for empty dicts is needed here. If future changes may yield empty prob, consider adding a guard or a clearer invariant.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-03-09T12:34:56.631Z
Learnt from: yibinl-nvidia
Repo: NVIDIA/TensorRT-LLM PR: 12009
File: tensorrt_llm/_torch/visual_gen/models/ltx2/transformer_ltx2.py:296-299
Timestamp: 2026-03-09T12:34:56.631Z
Learning: In tensorrt_llm/_torch/visual_gen/models/ltx2/transformer_ltx2.py, BasicAVTransformerBlock should not be flagged for a config mismatch when config.parallel.dit_ulysses_size > 1. The function setup_sequence_parallelism() returns use_ulysses=True for dit_ulysses_size > 1, or raises a RuntimeError/ValueError/NotImplementedError; it never returns use_ulysses=False in that case. Treat this as intentional and correct; do not flag as a mismatch between raw config checks and setup_sequence_parallelism()'s result.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-04-16T00:07:18.998Z
Learnt from: venkywonka
Repo: NVIDIA/TensorRT-LLM PR: 12944
File: tensorrt_llm/_torch/auto_deploy/shim/ad_executor.py:654-679
Timestamp: 2026-04-16T00:07:18.998Z
Learning: When reviewing TensorRT-LLM multimodal handling, do not assume a bug/inconsistency just because `special_token_offsets` appears written in different locations for different backends. In the AutoDeploy path, `special_token_offsets` is written under `multimodal_data['layout_metadata']['special_token_offsets']` by the AD-specific input processor and is read by `ad_executor.py` via the nested `layout_metadata` lookup. In the PyTorch path, it’s written at the top level as `mm_data['special_token_offsets']` (e.g., via `tensorrt_llm/inputs/registry.py`) and is read by `model_engine.py` from the top level. These backends should not share requests, so the reviewer should verify reads/writes match the intended backend’s request structure rather than flaging cross-backend mismatches.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-04-24T03:28:03.123Z
Learnt from: yihwang-nv
Repo: NVIDIA/TensorRT-LLM PR: 13373
File: tests/integration/defs/accuracy/test_llm_api_pytorch.py:51-61
Timestamp: 2026-04-24T03:28:03.123Z
Learning: When reviewing TensorRT-LLM attention backend code, treat `_TRTLLM_ENABLE_TRTLLM_GEN_ATTENTION` (in `tensorrt_llm._torch.attention_backend.trtllm`) as a *gate* that enables the `trtllm_gen` path, not as an identifier of the executed backend. The same `TrtllmAttention` class may handle both legacy and `trtllm_gen`, and the actual per-forward branch is also governed by checks like `trtllm_gen.is_supported(...)`. Do not expect public LLM/attention object attributes to reveal which path executed; if test assertions require confirming GEN usage, reviewers should require production-side instrumentation/counters (or other observable signals) rather than relying on existing public fields.

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-05-16T01:43:01.298Z
Learnt from: yibinl-nvidia
Repo: NVIDIA/TensorRT-LLM PR: 14161
File: tensorrt_llm/serve/cluster_storage.py:5-5
Timestamp: 2026-05-16T01:43:01.298Z
Learning: In the NVIDIA/TensorRT-LLM codebase, do not raise a code review issue for missing NVIDIA Apache 2.0 license headers in Python files (they are intentionally not required/enforced in this repository).

Applied to files:

  • tensorrt_llm/_torch/speculative/interface.py
  • tensorrt_llm/_torch/auto_deploy/shim/demollm.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📚 Learning: 2026-02-25T01:48:05.078Z
Learnt from: yibinl-nvidia
Repo: NVIDIA/TensorRT-LLM PR: 11683
File: tests/unittest/llmapi/_run_multi_mpi_comm_tasks.py:6-8
Timestamp: 2026-02-25T01:48:05.078Z
Learning: When reviewing cherry-pick PRs that backport commits (PRs that move fixes from one branch to another), do not suggest or require style/formatting changes in the backport. Cherry-picks should preserve the exact commit history and focus on functional correctness; style improvements are lower priority than preserving the integrity of the backported fixes. This guidance applies broadly to Python test files under tests/, ensuring reviews favor minimal, non-formatting changes that maintain correctness.

Applied to files:

  • tests/unittest/_torch/sampler/test_torch_sampler.py
📚 Learning: 2026-05-29T08:18:31.547Z
Learnt from: zhenhuaw-me
Repo: NVIDIA/TensorRT-LLM PR: 14733
File: tests/unittest/_torch/visual_gen/test_trtllm_serve_endpoints.py:1838-1840
Timestamp: 2026-05-29T08:18:31.547Z
Learning: In NVIDIA/TensorRT-LLM, Ruff’s `ruff select` does not enable the flake8-bandit (“S*”) rules, so Bandit checks like S108 are not enforced by the lint gate. During review of test Python code, do not flag `/tmp/...` string literals (or other situations) as Ruff S108 violations unless the repo’s Ruff configuration is updated to select flake8-bandit rules.

Applied to files:

  • tests/unittest/_torch/sampler/test_torch_sampler.py
📚 Learning: 2026-02-27T21:32:25.857Z
Learnt from: lucaslie
Repo: NVIDIA/TensorRT-LLM PR: 11796
File: tests/unittest/_torch/auto_deploy/unit/singlegpu/custom_ops/utils/test_block_table_ragged_conversion.py:713-747
Timestamp: 2026-02-27T21:32:25.857Z
Learning: When reviewing tests in this repository, lightweight benchmark tests (sub-second duration, ~100 iterations) that detect performance regressions should remain part of the standard test suite and should not be gated by environment variables or pytest markers. Only expensive/long-running benchmarks should be gated (e.g., via a pytest marker like pytest.mark.slow or a dedicated marker/flag) so they can be run selectively. This guidance applies to Python tests under tests/ and related subdirectories.

Applied to files:

  • tests/unittest/_torch/sampler/test_torch_sampler.py
📚 Learning: 2025-12-12T03:27:08.565Z
Learnt from: tongyuantongyu
Repo: NVIDIA/TensorRT-LLM PR: 9655
File: tensorrt_llm/_torch/pyexecutor/sampler.py:3031-3031
Timestamp: 2025-12-12T03:27:08.565Z
Learning: In files under tensorrt_llm/_torch/pyexecutor, avoid accessing torch.Tensor objects inside for-loops when iterating over requests. Convert batched tensors to Python lists beforehand using tensor.tolist(), and then iterate over those lists. This improves performance by reducing tensor-bound operations inside hot loops. Apply this pattern to similar code paths that process batches to access simple Python data structures (lists) inside loops.

Applied to files:

  • tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py
  • tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
  • tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
📝 Walkthrough

Walkthrough

Changes

Unified sampling interfaces

Layer / File(s) Summary
Sampling operation contracts
tensorrt_llm/_torch/pyexecutor/sampler/ops/flashinfer.py, tensorrt_llm/_torch/pyexecutor/sampler/ops/vanilla.py
FlashInfer operations unify generator and seed/offset inputs; vanilla sampling consolidates top-k, top-p, and temperature APIs.
Sampler dispatch and speculative routing
tensorrt_llm/_torch/pyexecutor/sampler/sampling_utils.py, tensorrt_llm/_torch/auto_deploy/shim/demollm.py, tensorrt_llm/_torch/speculative/interface.py, tests/unittest/_torch/sampler/test_torch_sampler.py
Sampling strategies and speculative decoding use the consolidated operations, shared greedy-temperature handling, and compatible instrumentation mocks.
Sampler runtime maintenance
tensorrt_llm/_torch/pyexecutor/sampler/sampler.py
Fusion references, finish-reason handling, logprob slicing, minimum-length penalties, and stop-word helpers are updated.

Dynamic tree probability operation removal

Layer / File(s) Summary
Remove probability computation path
cpp/tensorrt_llm/kernels/speculativeDecoding/dynamicTreeKernels.cu, cpp/tensorrt_llm/thop/dynamicTreeOp.cpp
Top-k/top-p probability computation and its Torch operation declaration, implementation, and registrations are removed. Dynamic-tree build and verification code remains.

Fake allocate-output contract

Layer / File(s) Summary
Update fake output metadata
tensorrt_llm/_torch/custom_ops/cpp_custom_ops.py
The fake operation derives shape and dtype from overrides or the reference tensor and returns the output buffer kind alongside the tensor.

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

Sequence Diagram(s)

sequenceDiagram
  participant Sampler
  participant SamplingUtils
  participant FlashInfer
  Sampler->>SamplingUtils: select sampling strategy
  SamplingUtils->>FlashInfer: pass logits or probabilities with randomness inputs
  FlashInfer-->>SamplingUtils: return sampled token
  SamplingUtils-->>Sampler: return token and sampling results
Loading

Suggested reviewers: tongyuantongyu, schetlur-nv, tfogal, yizhang-nv, ziyixiong-nv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 is concise and accurately summarizes the main TorchSampler refactor and dead-code cleanup.
Description check ✅ Passed The description is detailed and mostly follows the template, but the Test Coverage section is left unfilled.
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.

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59359 [ run ] completed with state SUCCESS. Commit: 003a19f
/LLM/main/L0_MergeRequest_PR pipeline #47836 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@zhaoyangwang-nvidia

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59408 [ run ] triggered by Bot. Commit: 003a19f Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59408 [ run ] completed with state SUCCESS. Commit: 003a19f
/LLM/main/L0_MergeRequest_PR pipeline #47879 completed with status: 'SUCCESS'
Pipeline passed with automatic retried tests. Check the rerun report for details.

CI Report

Link to invocation

@zhaoyangwang-nvidia
zhaoyangwang-nvidia merged commit 46bf21b into NVIDIA:main Jul 15, 2026
10 checks passed
return tokens


# The three ops below wrap the mask -> softmax -> renorm pipeline stages 1:1.

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.

For instance, softmax_op will fail if FlashInfer is not available. Importing something that certainly fails if it ever gets invoked may result in late failures (imagine a service being "healthy" and crashing only after a user request selects some unsupported behavior). It might be preferable if that service failed at startup time already.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the review, @ixlmar. Since this PR has already been merged, I'll address your suggestions in #16052

# only called on the flashinfer sampler / speculative-worker paths.
from tensorrt_llm._torch.pyexecutor.sampler.ops.flashinfer import (
sampling_from_probs_generator_op as sampling_from_probs_generator_op,
sampling_from_probs_op as sampling_from_probs_op,

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.

Replace from a import b as b with from a import b (multiple occurrences).


# Re-export the torch greedy op (used by drafting_loops and speculative/interface).
greedy = _torch_greedy
greedy = greedy_search_sampling_batch

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.

greedy_search_sampling_batch is a better name. Should we perhaps change the dependent modules to use this name instead?

``vocab_size`` (== keep all tokens), leaving genuine top_k values
untouched.
"""
return torch.where(top_k > 0, top_k, torch.full_like(top_k, vocab_size)).clamp(max=vocab_size)

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 using .masked_fill_.

zhaoyangwang-nvidia added a commit to zhaoyangwang-nvidia/TensorRT-LLM that referenced this pull request Jul 16, 2026
Apply ixlmar's post-merge review suggestions from NVIDIA#16365:

- ops/flashinfer.py: without flashinfer installed, calling a sampling op
  now raises an ImportError with installation guidance instead of a bare
  NameError. The module intentionally stays importable (sampling_utils
  imports it unconditionally and non-flashinfer sampler paths must keep
  working); startup-time enforcement remains in TorchSampler.__init__.
  Document that contract in the module docstring.
- sampling_utils.py: collapse the "from x import y as y" re-export lines
  into plain grouped imports plus an explicit __all__ (mypy runs strict,
  so implicit re-export is disabled).
- Drop the "greedy" alias of greedy_search_sampling_batch; use the
  descriptive name directly in drafting_loops.py and
  speculative/interface.py.
- sanitize_top_k: replace torch.where(...).clamp(...) with
  clamp(...).masked_fill_(...), avoiding the full_like intermediate.

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.

5 participants