diff --git a/tests/v1/core/test_scheduler.py b/tests/v1/core/test_scheduler.py index 44fdb9d5f2..1ef63cbb04 100644 --- a/tests/v1/core/test_scheduler.py +++ b/tests/v1/core/test_scheduler.py @@ -1828,6 +1828,22 @@ def test_spec_decode_padding_first_decode_step(): assert out.scheduled_spec_decode_tokens[r2.request_id] == [-1] * num_spec +def test_spec_decode_padding_resumed_request_without_running_requests(): + """Pad a synchronously resumed request even without local running work.""" + num_spec = 3 + scheduler = create_scheduler( + num_speculative_tokens=num_spec, + use_kv_connector=mock_kv(matched_tokens=32, is_async=False), + ) + (resumed,) = create_requests(num_requests=1, num_tokens=33, max_tokens=16) + + scheduler.add_request(resumed) + out = scheduler.schedule() + + assert out.num_scheduled_tokens[resumed.request_id] == 1 + num_spec + assert out.scheduled_spec_decode_tokens[resumed.request_id] == [-1] * num_spec + + def test_spec_decode_padding_skipped_for_diffusion(): """Diffusion spec tokens are the fixed-size denoising canvas, not rejectable drafts: a first-decode-step request must keep its 1-token span diff --git a/vllm/v1/core/sched/scheduler.py b/vllm/v1/core/sched/scheduler.py index cbef879b29..d9d86668a6 100644 --- a/vllm/v1/core/sched/scheduler.py +++ b/vllm/v1/core/sched/scheduler.py @@ -973,7 +973,8 @@ class Scheduler(SchedulerInterface): (self.num_spec_tokens > 0 and self.dynamic_sd_lookup is None) and self.num_sampled_tokens_per_step > 0 and num_new_tokens == 1 - and (scheduled_running_reqs and not prefill_scheduled) + and not prefill_scheduled + and (scheduled_running_reqs or num_computed_tokens > 0) ): padded_num_tokens = 1 + self.num_spec_tokens # Pad only when there is room for the sampled token(s).