Skip to content

[None][fix] clamp very small non-zero sampling temperature#16222

Open
lonexreb wants to merge 2 commits into
NVIDIA:mainfrom
lonexreb:fix/clamp-min-temperature
Open

[None][fix] clamp very small non-zero sampling temperature#16222
lonexreb wants to merge 2 commits into
NVIDIA:mainfrom
lonexreb:fix/clamp-min-temperature

Conversation

@lonexreb

@lonexreb lonexreb commented Jul 10, 2026

Copy link
Copy Markdown

Description

Fixes #15715.

SamplingParams only validated temperature >= 0, so tiny positive values (e.g. 1e-12) passed through unchanged. In the sampling backend, logits / temperature with such values overflows to inf/nan in fp16/bf16 (typical logits ~10 divided by 1e-6 already exceed fp16's max ~65504), producing undefined sampling behavior.

Change

  • Add MIN_SAMPLING_TEMPERATURE = 1e-2 and clamp any positive temperature below it in SamplingParams._validate().
  • temperature == 0.0 (greedy decoding) is explicitly left unchanged.
  • Mirrors vLLM's numerical-stability guard.

Test

New CPU-only unit tests in tests/unittest/llmapi/test_sampling_params.py covering: None passthrough, greedy 0.0 preserved, tiny values (1e-12, 1e-6, 5e-3) clamped to the floor, normal temperatures unchanged, and negative rejected.

Summary by CodeRabbit

  • Bug Fixes

    • Improved sampling stability by clamping extremely small positive temperature values to a safe minimum.
    • Preserved greedy decoding when temperature is set to zero.
    • Continued rejecting negative temperature values.
  • Tests

    • Added coverage for default, boundary, valid, and invalid temperature settings.

Positive temperatures below a numerically safe floor (1e-2) overflow
logits / temperature to inf/nan in fp16/bf16 and produce undefined
sampling behavior. Clamp them up to MIN_SAMPLING_TEMPERATURE while
leaving temperature == 0 (greedy decoding) unchanged.

Fixes NVIDIA#15715

Signed-off-by: lonexreb <reach2shubhankar@gmail.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a 1e-2 minimum for non-zero sampling temperatures, applies it during validation while preserving 0.0, and adds unit tests for supported and rejected temperature values.

Changes

Sampling temperature safety

Layer / File(s) Summary
Temperature floor and validation
tensorrt_llm/sampling_params.py
Defines MIN_SAMPLING_TEMPERATURE and clamps positive temperatures below it during SamplingParams._validate(), leaving zero unchanged.
Temperature validation tests
tests/unittest/llmapi/test_sampling_params.py
Tests default, greedy, clamped, normal, and negative temperature behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately describes the main fix for clamping tiny non-zero sampling temperatures.
Description check ✅ Passed The description covers the bug, the fix, and test coverage, though it omits the checklist section from the template.
Linked Issues check ✅ Passed The PR implements #15715 by clamping tiny positive temperatures, preserving 0.0, and adding tests.
Out of Scope Changes check ✅ Passed The changes are limited to the temperature clamp and matching unit tests, with no obvious unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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: 2

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

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

Add type annotations to the new test functions.

The Python guidelines require function annotations; use -> None and annotate parametrized temperature arguments as float.

Proposed fix
-def test_temperature_none_unchanged():
+def test_temperature_none_unchanged() -> None:

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

-def test_tiny_positive_temperature_clamped(tiny):
+def test_tiny_positive_temperature_clamped(tiny: float) -> None:

-def test_normal_temperature_unchanged(temp):
+def test_normal_temperature_unchanged(temp: float) -> None:

-def test_negative_temperature_rejected():
+def test_negative_temperature_rejected() -> None:
🤖 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 20 - 41, Add type
annotations to all new test functions in the sampling parameter tests: use ->
None for each test function and annotate parametrized arguments such as tiny and
temp as float.

Source: Coding guidelines


1-41: 📐 Maintainability & Code Quality | 🔵 Trivial

No QA test-list update is needed.

This is a narrow CPU-only unit-test change under tests/unittest/llmapi; integration and performance QA lists do not need an entry.

🤖 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 1 - 41, Keep the
change limited to the CPU-only unit tests in test_temperature_none_unchanged,
test_temperature_zero_kept_for_greedy, test_tiny_positive_temperature_clamped,
test_normal_temperature_unchanged, and test_negative_temperature_rejected; do
not modify integration or performance QA test lists.

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.

Inline comments:
In `@tensorrt_llm/sampling_params.py`:
- Around line 327-332: Validate that temperature is finite in the SamplingParams
temperature validation logic before the negative-value and minimum-floor checks.
Reject NaN and positive or negative infinity with a ValueError, and add tests
covering these non-finite values alongside the existing sign and clamping tests.

In `@tests/unittest/llmapi/test_sampling_params.py`:
- Around line 29-31: Update test_tiny_positive_temperature_clamped to assert the
contractual floor directly as 1e-2 instead of using MIN_SAMPLING_TEMPERATURE,
while retaining the existing tiny positive temperature parameterization.

---

Nitpick comments:
In `@tests/unittest/llmapi/test_sampling_params.py`:
- Around line 20-41: Add type annotations to all new test functions in the
sampling parameter tests: use -> None for each test function and annotate
parametrized arguments such as tiny and temp as float.
- Around line 1-41: Keep the change limited to the CPU-only unit tests in
test_temperature_none_unchanged, test_temperature_zero_kept_for_greedy,
test_tiny_positive_temperature_clamped, test_normal_temperature_unchanged, and
test_negative_temperature_rejected; do not modify integration or performance QA
test lists.
🪄 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: c57bc6e4-0cd6-40b4-ac35-91f3ff4094c0

📥 Commits

Reviewing files that changed from the base of the PR and between e2ce7a6 and 5d87910.

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

Comment on lines 327 to +332
if self.temperature is not None and self.temperature < 0:
raise ValueError(f"require temperature >= 0, got temperature={self.temperature}")
# Clamp very small non-zero temperatures up to a numerically safe floor;
# keep 0.0 as-is so greedy decoding is unaffected.
if self.temperature is not None and 0 < self.temperature < MIN_SAMPLING_TEMPERATURE:
self.temperature = MIN_SAMPLING_TEMPERATURE

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject non-finite temperatures before applying the floor.

float("nan") makes both comparisons false, so it is forwarded to the backend; positive infinity also passes through. Validate finiteness and add NaN/Infinity tests before the sign/clamp checks.

Proposed fix
+import math
+
...
+        if self.temperature is not None and not math.isfinite(self.temperature):
+            raise ValueError(f"temperature must be finite, got temperature={self.temperature}")
         if self.temperature is not None and self.temperature < 0:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if self.temperature is not None and self.temperature < 0:
raise ValueError(f"require temperature >= 0, got temperature={self.temperature}")
# Clamp very small non-zero temperatures up to a numerically safe floor;
# keep 0.0 as-is so greedy decoding is unaffected.
if self.temperature is not None and 0 < self.temperature < MIN_SAMPLING_TEMPERATURE:
self.temperature = MIN_SAMPLING_TEMPERATURE
if self.temperature is not None and not math.isfinite(self.temperature):
raise ValueError(f"temperature must be finite, got temperature={self.temperature}")
if self.temperature is not None and self.temperature < 0:
raise ValueError(f"require temperature >= 0, got temperature={self.temperature}")
# Clamp very small non-zero temperatures up to a numerically safe floor;
# keep 0.0 as-is so greedy decoding is unaffected.
if self.temperature is not None and 0 < self.temperature < MIN_SAMPLING_TEMPERATURE:
self.temperature = MIN_SAMPLING_TEMPERATURE
🤖 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/sampling_params.py` around lines 327 - 332, Validate that
temperature is finite in the SamplingParams temperature validation logic before
the negative-value and minimum-floor checks. Reject NaN and positive or negative
infinity with a ValueError, and add tests covering these non-finite values
alongside the existing sign and clamping tests.

Comment on lines +29 to +31
@pytest.mark.parametrize("tiny", [1e-12, 1e-6, MIN_SAMPLING_TEMPERATURE / 2])
def test_tiny_positive_temperature_clamped(tiny):
assert SamplingParams(temperature=tiny).temperature == MIN_SAMPLING_TEMPERATURE

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the contractual floor independently of the implementation constant.

Using MIN_SAMPLING_TEMPERATURE as the expected value means an incorrect constant could still pass the tests. The requirement is specifically 1e-2.

Proposed fix
-    assert SamplingParams(temperature=tiny).temperature == MIN_SAMPLING_TEMPERATURE
+    assert SamplingParams(temperature=tiny).temperature == 1e-2
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.parametrize("tiny", [1e-12, 1e-6, MIN_SAMPLING_TEMPERATURE / 2])
def test_tiny_positive_temperature_clamped(tiny):
assert SamplingParams(temperature=tiny).temperature == MIN_SAMPLING_TEMPERATURE
`@pytest.mark.parametrize`("tiny", [1e-12, 1e-6, MIN_SAMPLING_TEMPERATURE / 2])
def test_tiny_positive_temperature_clamped(tiny):
assert SamplingParams(temperature=tiny).temperature == 1e-2
🤖 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 29 - 31, Update
test_tiny_positive_temperature_clamped to assert the contractual floor directly
as 1e-2 instead of using MIN_SAMPLING_TEMPERATURE, while retaining the existing
tiny positive temperature parameterization.

- Reject NaN/+-Inf temperatures before the sign/clamp checks (they
  bypassed both comparisons and reached the backend).
- Add -> None / float type annotations on the new tests.
- Assert the contractual floor 1e-2 directly, plus NaN/Inf rejection tests.

Signed-off-by: lonexreb <reach2shubhankar@gmail.com>
@lonexreb

Copy link
Copy Markdown
Author

Thanks for the review. Addressed in ef42922:

  • Non-finite temperatures: now reject NaN/±Inf with a ValueError before the sign/clamp checks (they previously bypassed both comparisons).
  • Type annotations: added -> None and float param annotations to the new tests.
  • Test assertions: assert the contractual floor 1e-2 directly (not the constant), and added NaN/Inf rejection tests.

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.

[Bug]: Clamp very small non-zero temperature values to avoid numerical instability

1 participant