fix: auto-cap max_tokens to the model window per prompt - #145
Merged
Merged
Conversation
vLLM rejects a request when prompt_tokens + max_tokens > max_model_len.
A workflow that hard-codes a large max_tokens therefore fails EVERY
invocation regardless of prompt size: a dsm_clause_entailment_audit op
requesting max_tokens=65536 against QuantTrio/Qwen3.6-27B-AWQ (native
window 40960) could never succeed. The op only emits a short verdict,
so the 65536 is a caller mistake — but nothing in the worker defended
against it, and the error ("maximum context length … you requested 0
output tokens") names neither the parameter nor the op, so it was
misdiagnosed as an auth/config problem more than once.
Auto-cap max_tokens per prompt to window - prompt_tokens - margin,
bounded by a floor and never above what was requested. Because vLLM's
LLM.generate accepts either one SamplingParams or a per-prompt list, we
build the list only when a clamp actually bites; the common case keeps
sharing one object.
Deliberately conservative:
- Window unknown (engine shape unrecognised) → no clamp; a missing
window must never silently shrink a caller's budget.
- Prompt already at/over the window → floor, leaving vLLM to raise the
real "input too long" error rather than hiding it behind a 1-token
generation.
- Multimodal (TextPrompt) entries keep the requested budget: the raw
text undercounts image tokens, so clamping on text alone could permit
MORE than fits — preserving current behaviour is the safe choice.
The window is read off the live engine (llm_engine.model_config
.max_model_len) with getattr fallbacks, so a vLLM version skew degrades
to "no clamp" instead of crashing.
Tests: the pure clamp is builtins-only (no vllm/torch); the wiring runs
on a bare instance with fakes. Both added in
tests/worker/test_vllm_auto_cap_max_tokens.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Follow-up on the auto-cap change from review.
Surface the clamp to callers: InferenceItem gains a `diagnostics` field
(engine-owned, keyed by producer), and a clamped prompt records
`{auto_cap: {max_tokens, requested}}`. The grouped-output remap
aggregates it per member, mirroring finish_reason. A single summary log
replaces the per-prompt one, and tokenizer failures log at debug.
Simplify the clamp to `min(requested, max(budget, floor))`, which also
stops a below-floor request from being raised up to the floor. Read the
window and max_tokens through typed attribute access instead of getattr,
guarded by try/except so a vLLM version skew still degrades to no clamp.
Thread the per-index clamp record through the return value rather than
instance attributes, removing a cross-run stale-state hazard.
Signed-off-by: Noppanat Wadlom <noppanat.wad@gmail.com>
The SDK's InferenceItem is a field-for-field mirror of the shared schema, enforced by tests/sdk/test_schema_compat.py. Add the diagnostics field so the mirror matches after the server-side addition. Signed-off-by: Noppanat Wadlom <noppanat.wad@gmail.com>
Trim the docstrings and comments added for the max_tokens auto-cap to the level of detail they need, and give the helper docstrings a consistent one-line summary plus description. Signed-off-by: Noppanat Wadlom <noppanat.wad@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The vLLM executor passes a request's
max_tokensstraight through toSamplingParams. vLLM rejects a request whenprompt_tokens + max_tokens > max_model_len, so a workflow that hard-codes a largemax_tokensfails every invocation regardless of prompt size.Concretely: a
dsm_clause_entailment_auditop requestingmax_tokens: 65536againstQuantTrio/Qwen3.6-27B-AWQ(native window 40960) can never succeed — even though the op only emits a short verdict. The surfaced error names neither the parameter nor the op:(The "0 output tokens" is vLLM clamping the output budget to zero once the prompt alone overflows; the real 65536 only becomes visible once the window is large enough to admit the prompt.) This was misdiagnosed as an auth/config problem more than once.
Fix
Auto-cap
max_tokensper prompt towindow − prompt_tokens − margin, bounded by a floor and never above what was requested.LLM.generateaccepts either oneSamplingParamsor a per-prompt list, so the list is built only when a clamp actually bites — the common case keeps sharing one object.Deliberately conservative:
TextPrompt) entries → keep the requested budget. Raw text undercounts image tokens, so clamping on text alone could permit more than fits; preserving current behaviour is the safe choice.The window and
max_tokensare read through typed attribute access on the live engine (llm_engine.model_config.max_model_len), guarded bytry/except, so a vLLM version skew degrades to "no clamp" rather than crashing.When a clamp bites it is surfaced to the caller on the result item's
diagnosticsfield (diagnostics.auto_cap = {max_tokens, requested}), so a shorter-than-requested completion is self-explaining instead of a silent truncation. The grouped/table output path aggregates it per member.diagnosticsis engine-owned and kept distinct from the user-ownedmetadata.Tests
tests/worker/test_vllm_auto_cap_max_tokens.pycovers the pure clamp (fit, window-unknown, already-fits, floor, never-above-requested) and the wiring on a bare instance with fakes (per-prompt list, mixed batch, multimodal passthrough, tokenizer-failure fallback) plus the grouped-remap aggregation. The pure clamp is builtins-only; the wiring needs no GPU deps. The suite passes underuv run pytest.Note
Pairs with the deploy-side change that stopped Lumilake capping
max_model_lenat 8192 fleet-wide (deploy_infra#51). That fix lets the prompt fit the 40960 window; this fix stops an oversizedmax_tokensfrom overflowing it from the other side.