Skip MinLength logits processor construction when eos_token_id is negative#29604
Conversation
Only construct the MinLength logits processor when eos_token_id is a valid (non-negative) token id. A negative eos_token_id is the documented "no eos" sentinel used by greedy/sampling generation; in that case there is no token to demote, so the processor would be a no-op. Skipping its construction makes the call-site contract explicit and mirrors the conditional-add style of the neighboring processors, while the existing SetScore bounds check remains the runtime backstop. Add unit tests covering the negative-sentinel no-op path and the existing min-length enforcement behavior to guard against regressions. Signed-off-by: Tita Wang <titaiwang@microsoft.com>
The existing tests construct MinLengthLogitsProcessor directly and thus exercise the SetScore runtime backstop rather than the LogitsProcessorList::Init call site that decides whether to add the processor. Add two list-level tests that drive Init with a GreedySearchParameters configured to activate only the MinLength path: one asserts a negative eos_token_id skips the processor (scores unchanged), and a positive-control test asserts a valid eos_token_id adds it (eos demoted). Together they cover the Init construction condition in both directions. Signed-off-by: Tita Wang <titaiwang@microsoft.com>
There was a problem hiding this comment.
Pull request overview
This PR improves robustness and clarity in the CPU generation logits-processor initialization by avoiding construction of a no-op MinLengthLogitsProcessor when eos_token_id is the negative “no EOS” sentinel (e.g., -1). It also adds focused unit tests to lock in both the processor behavior and the list-level construction guard.
Changes:
- Guard
MinLengthLogitsProcessorconstruction inLogitsProcessorList::LogitsProcessorInitImplbehindeos_token_id >= 0. - Add unit tests covering: negative EOS no-op, valid EOS demotion behavior, and list-level init guard behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
onnxruntime/contrib_ops/cpu/transformers/logits_processor.h |
Adds an eos_token_id >= 0 guard to skip constructing a no-op MinLength processor for the negative sentinel. |
onnxruntime/test/contrib_ops/min_length_logits_processor_test.cc |
Adds unit tests for MinLengthLogitsProcessor behavior and for the new list-level construction guard. |
The earlier tests constructed MinLengthLogitsProcessor<float> directly, which references a template member defined in logits_processor.cc. That symbol is not exported from the shared library, so onnxruntime_provider_test failed to link in the shared-library build configurations (undefined reference to the constructor), even though it linked in a static build. Rework the tests to depend only on symbols available in both static and shared builds: drive the processor through the public LogitsProcessorList::Init and Process path (which is exactly the construction guard under test), and cover the negative-token-id backstop through the header-inline NextTokenScores::SetScore. Coverage is preserved: negative-sentinel skip, below-min-length demotion, min-length boundary no-op, and the SetScore negative-id guard. Signed-off-by: Tita Wang <titaiwang@microsoft.com>
Review — multi-agent pass (readability, correctness, adversarial, spec/deep, integration)Verdict: LGTM. The guard change is correct, safe, and behavior-neutral. Reviewers confirmed the "negative-eos MinLength is a no-op" premise directly: Worth addressing
Minor / nits (optional)
Follow-ups (out of scope)
Reviewed by a 5-agent team (Claude / GPT / Gemini). Findings deduplicated and prioritized by the lead; the Major was verified against |
Reword the production and test comments to describe the guard honestly as a defensive/performance skip of a MinLength processor that would be a guaranteed no-op for a negative (no-EOS) eos_token_id, rather than a correctness fix. The eos >= 0 positive-control test is the discriminating case for the enforcement path. Value-initialize GreedySearchParameters in the test helper so inherited scalar fields are not left indeterminate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Tita Wang <titaiwang@microsoft.com>
What
Skip constructing the
MinLengthLogitsProcessorwheneos_token_idis negative.A negative
eos_token_idis the "no-EOS" sentinel used by greedy/samplinggeneration (it defaults to
-1). With no EOS token there is nothing to demote,so a MinLength processor built with a negative eos can only ever be a guaranteed
no-op. This change guards its construction at the list level in
LogitsProcessorInitImpl(logits_processor.h), so we do not build a processorthat would do nothing.
Why
This is a small defense-in-depth / code-clarity improvement, not a behavior
change and not a correctness or security fix:
eos_token_id >= 0, behavior is unchanged — the processor isstill constructed and enforces the minimum length exactly as before.
anyway (
SetScorealready ignores negative token ids), so this is a minorperformance and clarity optimization.
RepetitionPenalty), keeping the construction logic consistent.CPU-only: the CUDA path is already inherently a no-op for a negative eos, so no
CUDA code is touched.
Tests
Adds 4 unit tests in
min_length_logits_processor_test.cc(run viaonnxruntime_provider_test --gtest_filter='*MinLengthLogitsProcessorTest*'):SetScoreIgnoresNegativeTokenId— documents the pre-existing inline backstopthat ignores negative token ids.
ListInitSkipsProcessorForNegativeEosTokenId— drivesLogitsProcessorList::Initwith a negative eos and confirms a below-min-length run leaves scores unchanged
(the processor is skipped as a guaranteed no-op).
ListInitDemotesEosBelowMinLength— positive control / enforcement path: with avalid eos the processor is constructed and demotes the eos score below min length.
ListInitLeavesScoresUnchangedAtMinLength— at the min length, no demotion.The tests only reference exported (
LogitsProcessorList::Init/Process) andheader-inline symbols so they link in both static and shared-library builds.