Skip to content

[https://nvbugs/6485885][fix] Stop thinking-budget processor re-forcing the reasoning end tag#16785

Open
Wanli-Jiang wants to merge 1 commit into
NVIDIA:mainfrom
Wanli-Jiang:user/fix-thinking-budget-stale-force
Open

[https://nvbugs/6485885][fix] Stop thinking-budget processor re-forcing the reasoning end tag#16785
Wanli-Jiang wants to merge 1 commit into
NVIDIA:mainfrom
Wanli-Jiang:user/fix-thinking-budget-stale-force

Conversation

@Wanli-Jiang

@Wanli-Jiang Wanli-Jiang commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

With the overlap scheduler, logits processors run one step behind the sampled tokens (request.get_tokens() does not yet include the previous iteration's token). ThinkingBudgetLogitsProcessor was stateless and re-derived its decision from that stale view, so the step right after forcing the reasoning end tag could not see it and forced it again. The raw stream then contained a doubled end tag (e.g. ''); the reasoning parser consumes the first and the second leaks into message.content (reproduced 3/3 with nano-v3 + thinking_token_budget on nemotron-nano-3.5; clean with the overlap scheduler disabled).

Track per-(request, beam) progress through the end sequence: continue from recorded progress instead of re-deriving from token_ids, stop once the sequence is complete, and reset when the view shows the block closed so later reasoning blocks are budgeted again. Also fixes multi-token end sequences restarting at the first token under the same lag.

Dev Engineer Review

  • Updated ThinkingBudgetLogitsProcessor to track per-request, per-beam reasoning-end progress, preventing duplicated end tags when the overlap scheduler exposes stale token views.
  • Supports multi-token reasoning-end sequences and resets tracking after the reasoning block closes.
  • Preserves stream handling while explicitly ignoring client_id.
  • No public API or configuration changes.

QA Engineer Review

  • Added regression coverage for stale token views and multi-token reasoning-end progress in tests/unittest/llmapi/test_sampling_params.py.
  • No test-list changes were identified; coverage is not listed in tests/integration/test_lists/.
  • Verdict: needs follow-up for CI/manual test-list coverage.

Description

Test Coverage

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.

…ng the reasoning end tag

With the overlap scheduler, logits processors run one step behind the
sampled tokens (request.get_tokens() does not yet include the previous
iteration's token). ThinkingBudgetLogitsProcessor was stateless and
re-derived its decision from that stale view, so the step right after
forcing the reasoning end tag could not see it and forced it again. The
raw stream then contained a doubled end tag (e.g. '</think></think>');
the reasoning parser consumes the first and the second leaks into
message.content (reproduced 3/3 with nano-v3 + thinking_token_budget on
nemotron-nano-3.5; clean with the overlap scheduler disabled).

Track per-(request, beam) progress through the end sequence: continue
from recorded progress instead of re-deriving from token_ids, stop once
the sequence is complete, and reset when the view shows the block
closed so later reasoning blocks are budgeted again. Also fixes
multi-token end sequences restarting at the first token under the same
lag.

Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
@Wanli-Jiang

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

ThinkingBudgetLogitsProcessor now preserves reasoning-end forcing progress per request and beam, handles CUDA stream execution explicitly, and avoids duplicate or restarted end-token emissions when token views are stale. Tests cover single- and multi-token end sequences.

Changes

Reasoning-end progress handling

Layer / File(s) Summary
Stateful reasoning-end forcing
tensorrt_llm/llmapi/thinking_budget.py
The processor tracks forced end-token progress by request and beam, handles direct and external CUDA streams, resets progress after closure, and stops forcing completed sequences.
Stale token view regression coverage
tests/unittest/llmapi/test_sampling_params.py
Regression tests verify that stale views do not duplicate single-token forcing or restart multi-token end sequences.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: asfiyab-nvidia

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.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 PR’s main fix and uses the required ticket/type format.
Description check ✅ Passed The description clearly explains the bug and fix, but the Test Coverage section is left empty.
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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/unittest/llmapi/test_sampling_params.py (1)

211-261: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover request and beam isolation.

Both regressions use req_id=0 and a single beam, so they do not exercise the new (req_id, beam_idx) state boundary. Add cases where stale progress on one request and one beam cannot advance, suppress, or reset forcing for another.

🤖 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/llmapi/test_sampling_params.py` around lines 211 - 261, Extend
the thinking-budget logits processor regression tests to cover state isolation
by both req_id and beam_idx, using multiple requests and beams with stale token
views. Verify progress from one (req_id, beam_idx) pair cannot advance,
suppress, or reset end-sequence forcing for another pair, while preserving the
existing stale-view behavior in both single- and multi-token end sequences.
tensorrt_llm/llmapi/thinking_budget.py (1)

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

Add concrete annotations to the new Python interfaces.

  • tensorrt_llm/llmapi/thinking_budget.py#L48-L48: declare _end_progress as dict[tuple[int, int], int].
  • tensorrt_llm/llmapi/thinking_budget.py#L65-L71: use built-in generic annotations and type key as tuple[int, int].
  • tests/unittest/llmapi/test_sampling_params.py#L204-L261: annotate _run() parameters/return type and test return types.
Proposed fix
-        self._end_progress: dict = {}
+        self._end_progress: dict[tuple[int, int], int] = {}

-    def _apply(self, req_id: int, token_ids: List[List[int]], logits: torch.Tensor) -> None:
+    def _apply(self, req_id: int, token_ids: list[list[int]], logits: torch.Tensor) -> None:

-    def _forced_token(self, key, token_ids: List[int]) -> Optional[int]:
+    def _forced_token(
+        self, key: tuple[int, int], token_ids: list[int]
+    ) -> Optional[int:

- def _run(processor, token_ids):
+ def _run(
+     processor: ThinkingBudgetLogitsProcessor,
+     token_ids: list[list[int]],
+ ) -> torch.Tensor:

- def test_thinking_budget_logits_processor_does_not_force_end_twice_on_stale_view():
+ def test_thinking_budget_logits_processor_does_not_force_end_twice_on_stale_view() -> None:

- def test_thinking_budget_logits_processor_continues_end_sequence_on_stale_view():
+ def test_thinking_budget_logits_processor_continues_end_sequence_on_stale_view() -> None:

As per coding guidelines, “Annotate every function” and “avoid Any ... prefer built-in generic types.” Based on learnings, Python 3.10+ syntax is supported.

🤖 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 `@tensorrt_llm/llmapi/thinking_budget.py` at line 48, The thinking-budget
interfaces need concrete Python 3.10+ annotations. In
tensorrt_llm/llmapi/thinking_budget.py lines 48-48, annotate _end_progress as
dict[tuple[int, int], int]; in lines 65-71, use built-in generic annotations and
type key as tuple[int, int]. In tests/unittest/llmapi/test_sampling_params.py
lines 204-261, annotate _run() parameters and return type, along with the test
functions’ return types, without using Any.

Sources: Coding guidelines, Learnings

🤖 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 `@tensorrt_llm/llmapi/thinking_budget.py`:
- Around line 45-48: Update the request-finalization and cancellation flow
associated with _end_progress so all entries for the completed request’s
(req_id, beam_idx) keys are removed even when the reasoning end sequence is
never observed. Ensure state is cleared when a request finishes or is cancelled,
or make the processor request-scoped, while preserving normal end-sequence
cleanup.

---

Nitpick comments:
In `@tensorrt_llm/llmapi/thinking_budget.py`:
- Line 48: The thinking-budget interfaces need concrete Python 3.10+
annotations. In tensorrt_llm/llmapi/thinking_budget.py lines 48-48, annotate
_end_progress as dict[tuple[int, int], int]; in lines 65-71, use built-in
generic annotations and type key as tuple[int, int]. In
tests/unittest/llmapi/test_sampling_params.py lines 204-261, annotate _run()
parameters and return type, along with the test functions’ return types, without
using Any.

In `@tests/unittest/llmapi/test_sampling_params.py`:
- Around line 211-261: Extend the thinking-budget logits processor regression
tests to cover state isolation by both req_id and beam_idx, using multiple
requests and beams with stale token views. Verify progress from one (req_id,
beam_idx) pair cannot advance, suppress, or reset end-sequence forcing for
another pair, while preserving the existing stale-view behavior in both single-
and multi-token end sequences.
🪄 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: 74f86545-0917-46af-9a50-86a8a5d80245

📥 Commits

Reviewing files that changed from the base of the PR and between c6e0c98 and 55862ce.

📒 Files selected for processing (2)
  • tensorrt_llm/llmapi/thinking_budget.py
  • tests/unittest/llmapi/test_sampling_params.py

Comment thread tensorrt_llm/llmapi/thinking_budget.py
@Wanli-Jiang
Wanli-Jiang requested a review from JunyiXu-nv July 23, 2026 07:16
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61231 [ run ] triggered by Bot. Commit: 55862ce Link to invocation

@JunyiXu-nv JunyiXu-nv 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!

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61231 [ run ] completed with state FAILURE. Commit: 55862ce
/LLM/main/L0_MergeRequest_PR pipeline #49470 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

@Wanli-Jiang

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61453 [ run ] triggered by Bot. Commit: 55862ce Link to invocation

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