@@ -35,6 +35,7 @@ def _make_scheduler_with_dynamic_sd(
3535 max_num_seqs : int = 16 ,
3636 max_num_batched_tokens : int = 8192 ,
3737 runtime_num_speculative_tokens : int = 3 ,
38+ adaptive_verification : bool = False ,
3839) -> Scheduler :
3940 base_scheduler = create_scheduler (
4041 max_num_seqs = max_num_seqs ,
@@ -45,6 +46,7 @@ def _make_scheduler_with_dynamic_sd(
4546 speculative_config = base_scheduler .vllm_config .speculative_config
4647 assert speculative_config is not None
4748 speculative_config .num_speculative_tokens_per_batch_size = schedule
49+ speculative_config .adaptive_verification = adaptive_verification
4850
4951 return Scheduler (
5052 vllm_config = base_scheduler .vllm_config ,
@@ -183,9 +185,104 @@ def test_v2_scheduler_selects_aggregate_dynamic_sd_budget():
183185 assert output .num_spec_tokens_to_schedule == 4
184186
185187
186- @pytest .mark .parametrize ("adaptive_verification" , [False , True ])
187- def test_scheduler_preserves_actual_grammar_mask_layout (
188+ @pytest .mark .parametrize ("optimal_k" , [0 , 2 ])
189+ def test_adaptive_scheduler_caps_fallback_decodes_at_dynamic_k (optimal_k : int ):
190+ scheduler = _make_scheduler_with_dynamic_sd (
191+ [(1 , 2 , optimal_k )],
192+ max_num_seqs = 2 ,
193+ max_num_batched_tokens = 32 ,
194+ runtime_num_speculative_tokens = 4 ,
195+ adaptive_verification = True ,
196+ )
197+ decode_req , prefill_req = create_requests (num_requests = 2 , num_tokens = 1 )
198+ scheduler .add_request (decode_req )
199+ prefill_output = scheduler .schedule ()
200+ scheduler .update_from_output (
201+ prefill_output ,
202+ ModelRunnerOutput (
203+ req_ids = [decode_req .request_id ],
204+ req_id_to_index = {decode_req .request_id : 0 },
205+ sampled_token_ids = [[0 ]],
206+ logprobs = None ,
207+ prompt_logprobs_dict = {},
208+ pooler_output = [],
209+ ),
210+ )
211+ scheduler .update_draft_token_ids (
212+ DraftTokenIds ([decode_req .request_id ], [[10 , 11 , 12 , 13 ]])
213+ )
214+
215+ scheduler .add_request (prefill_req )
216+ output = scheduler .schedule ()
217+
218+ expected_draft_tokens = [10 , 11 , 12 , 13 ][:optimal_k ]
219+ assert output .scheduled_spec_decode_tokens == (
220+ {decode_req .request_id : expected_draft_tokens } if expected_draft_tokens else {}
221+ )
222+ assert output .num_scheduled_tokens == {
223+ decode_req .request_id : 1 + optimal_k ,
224+ prefill_req .request_id : 1 ,
225+ }
226+ assert output .total_num_scheduled_tokens == 2 + optimal_k
227+ assert output .num_spec_tokens_to_schedule == 2 * optimal_k
228+
229+
230+ def test_adaptive_scheduler_preserves_full_windows_for_worker_allocation ():
231+ scheduler = _make_scheduler_with_dynamic_sd (
232+ [(1 , 2 , 2 )],
233+ max_num_seqs = 2 ,
234+ max_num_batched_tokens = 32 ,
235+ runtime_num_speculative_tokens = 4 ,
236+ adaptive_verification = True ,
237+ )
238+ requests = create_requests (num_requests = 2 , num_tokens = 1 )
239+ for request in requests :
240+ scheduler .add_request (request )
241+ prefill_output = scheduler .schedule ()
242+ scheduler .update_from_output (
243+ prefill_output ,
244+ ModelRunnerOutput (
245+ req_ids = [request .request_id for request in requests ],
246+ req_id_to_index = {
247+ request .request_id : i for i , request in enumerate (requests )
248+ },
249+ sampled_token_ids = [[0 ], [0 ]],
250+ logprobs = None ,
251+ prompt_logprobs_dict = {},
252+ pooler_output = [],
253+ ),
254+ )
255+ scheduler .update_draft_token_ids (
256+ DraftTokenIds (
257+ [request .request_id for request in requests ],
258+ [[10 , 11 , 12 , 13 ], [20 , 21 , 22 , 23 ]],
259+ )
260+ )
261+
262+ output = scheduler .schedule ()
263+
264+ assert output .scheduled_spec_decode_tokens == {
265+ requests [0 ].request_id : [10 , 11 , 12 , 13 ],
266+ requests [1 ].request_id : [20 , 21 , 22 , 23 ],
267+ }
268+ assert output .num_scheduled_tokens == {
269+ requests [0 ].request_id : 5 ,
270+ requests [1 ].request_id : 5 ,
271+ }
272+ assert output .total_num_scheduled_tokens == 10
273+ assert output .num_spec_tokens_to_schedule == 4
274+
275+
276+ @pytest .mark .parametrize (
277+ ("adaptive_verification" , "expected_spec_token_ids" ),
278+ [
279+ (False , [10 , 11 , 12 ]),
280+ (True , [10 , 11 , 12 , - 1 ]),
281+ ],
282+ )
283+ def test_scheduler_preserves_grammar_mask_window (
188284 adaptive_verification : bool ,
285+ expected_spec_token_ids : list [int ],
189286):
190287 scheduler = create_scheduler (
191288 num_speculative_tokens = 4 ,
@@ -194,6 +291,7 @@ def test_scheduler_preserves_actual_grammar_mask_layout(
194291 speculative_config = scheduler .vllm_config .speculative_config
195292 assert speculative_config is not None
196293 speculative_config .adaptive_verification = adaptive_verification
294+ scheduler .adaptive_verification = adaptive_verification
197295
198296 request = create_requests (num_requests = 1 , num_tokens = 1 )[0 ]
199297 scheduler .add_request (request )
@@ -220,21 +318,24 @@ def test_scheduler_preserves_actual_grammar_mask_layout(
220318 )
221319 )
222320
223- assert request .spec_token_ids == [ 10 , 11 , 12 ]
321+ assert request .spec_token_ids == expected_spec_token_ids
224322 grammar .validate_tokens .assert_called_once_with ([10 , 11 , 12 , 13 ])
225323
226324 output = scheduler .schedule ()
227- assert output .scheduled_spec_decode_tokens [request .request_id ] == [10 , 11 , 12 ]
325+ assert (
326+ output .scheduled_spec_decode_tokens [request .request_id ]
327+ == expected_spec_token_ids
328+ )
228329
229- bitmask = np .zeros ((4 , 1 ), dtype = np .int32 )
330+ bitmask = np .zeros ((len ( expected_spec_token_ids ) + 1 , 1 ), dtype = np .int32 )
230331 scheduler .structured_output_manager .grammar_bitmask = Mock (return_value = bitmask )
231332 grammar_output = scheduler .get_grammar_bitmask (output )
232333
233334 assert grammar_output is not None
234335 assert grammar_output .grammar_mask_stride == 5
235336 assert grammar_output .grammar_bitmask is bitmask
236337 assert scheduler .structured_output_manager .grammar_bitmask .call_args .args [2 ] == {
237- request .request_id : [ 10 , 11 , 12 ]
338+ request .request_id : expected_spec_token_ids
238339 }
239340
240341
0 commit comments