3232from vllm .v1 .worker .gpu .spec_decode .confidence import ( # noqa: E402
3333 MaskedConfidenceManager ,
3434 VarlenConfidenceManager ,
35- build_sps_table_from_costs ,
35+ build_cost_tables_from_curves ,
3636 compute_prefix_survival ,
3737 make_confidence_manager ,
3838 select_draft_token_budget ,
4545
4646def _spec_config (** overrides : Any ) -> SimpleNamespace :
4747 config = SimpleNamespace (
48- dspark_confidence_threshold = 0.0 ,
4948 dspark_budget_frac = 1.0 ,
5049 dspark_sps_curve = None ,
5150 dspark_online_sts = False ,
@@ -62,6 +61,7 @@ def _logit(probs: np.ndarray) -> np.ndarray:
6261@pytest .fixture (autouse = True )
6362def _single_rank_tp_group (monkeypatch ):
6463 group = SimpleNamespace (
64+ rank_in_group = 0 ,
6565 broadcast = lambda tensor , src = 0 : tensor ,
6666 broadcast_object = lambda value , src = 0 : value ,
6767 )
@@ -95,9 +95,7 @@ def test_auto_verification_selects_supported_manager(cg_support, expected_type):
9595 manager = make_confidence_manager (
9696 "auto" ,
9797 attn_cg_support ,
98- max_num_tokens = 8 ,
9998 req_states = req_states ,
100- device = device ,
10199 speculative_config = _spec_config (),
102100 )
103101
@@ -107,7 +105,6 @@ def test_auto_verification_selects_supported_manager(cg_support, expected_type):
107105def test_masked_verification_does_not_auto_profile_sps ():
108106 device = torch .device ("cuda" )
109107 manager = MaskedConfidenceManager (
110- max_num_tokens = 8 ,
111108 req_states = RequestState (
112109 max_num_reqs = 2 ,
113110 max_model_len = 4 ,
@@ -116,12 +113,12 @@ def test_masked_verification_does_not_auto_profile_sps():
116113 vocab_size = 16 ,
117114 device = device ,
118115 ),
119- device = device ,
120116 speculative_config = _spec_config (dspark_sps_curve = "auto" ),
121117 )
122118
123119 assert not manager .wants_auto_sps_curve
124- assert manager .sps_table_np is None
120+ assert manager .draft_cost_ms is None
121+ assert manager .verify_and_sample_cost_ms is None
125122
126123
127124def test_prepare_pos_seq_lens_clears_active_padding ():
@@ -196,32 +193,16 @@ def test_varlen_indexer_indices_use_device_lengths():
196193 assert indices .cpu ().tolist () == [0 , 1 , 1 , 2 , 3 ]
197194
198195
199- def test_select_budget_uses_global_prefix_order ():
200- survival = compute_prefix_survival (
201- _logit (
202- np .array (
203- [
204- [0.90 , 0.90 , 0.90 ],
205- [0.95 , 0.10 , 0.99 ],
206- [0.70 , 0.70 , 0.70 ],
207- ]
208- )
209- )
210- )
211- budget = select_draft_token_budget (survival , 3 , 3 , 3 , min_survival = 0.75 )
212- assert budget == 3
213-
214-
215196def test_select_budget_applies_fraction_globally ():
216197 survival = compute_prefix_survival (_logit (np .array ([[0.90 , 0.80 ], [0.80 , 0.80 ]])))
217198 budget = select_draft_token_budget (survival , 2 , 2 , 2 , budget_frac = 0.5 )
218- assert budget == 3
199+ assert budget == 2
219200
220201
221202def test_select_budget_is_hard_cap_under_ties ():
222203 survival = compute_prefix_survival (_logit (np .array ([[0.90 , 0.80 ], [0.90 , 0.80 ]])))
223204 budget = select_draft_token_budget (survival , 2 , 2 , 2 , budget_frac = 0.25 )
224- assert budget == 2
205+ assert budget == 1
225206
226207
227208def test_select_budget_is_hard_cap_for_saturated_scores ():
@@ -232,7 +213,7 @@ def test_select_budget_is_hard_cap_for_saturated_scores():
232213 """
233214 survival = compute_prefix_survival (np .full ((4 , 7 ), 40.0 ))
234215 budget = select_draft_token_budget (survival , 4 , 4 , 4 , budget_frac = 0.5 )
235- assert budget == int (4 * 7 * 0.5 ) + 1
216+ assert budget == int (4 * 7 * 0.5 )
236217
237218
238219def test_select_budget_never_admits_zero_survival ():
@@ -256,46 +237,45 @@ def test_select_budget_uses_sps_argmax():
256237 # SPS drops sharply after B=4 so theta peaks at k=2:
257238 # k=0: 2.00*1.00, k=1: 2.90*0.95=2.755, k=2: 3.62*0.90=3.258,
258239 # k=3: 4.22*0.20=0.844, k=4: 4.52*0.10=0.452.
259- sps_table = np .broadcast_to (
260- np .array ([1.0 , 1.0 , 1.0 , 0.95 , 0.90 , 0.20 , 0.10 ]),
261- (3 , 7 ),
262- )
240+ rates = np .array ([1.0 , 1.0 , 1.0 , 0.95 , 0.90 , 0.20 , 0.10 ])
263241 budget = select_draft_token_budget (
264242 survival ,
265243 num_batch_requests = 2 ,
266244 num_sampling_requests = 2 ,
267245 num_required_target_tokens = 2 ,
268- sps_table = sps_table ,
246+ draft_cost_ms = np .zeros (3 ),
247+ verify_and_sample_cost_ms = 1000.0 / rates ,
269248 )
270249 assert budget == 2
271250
272251
273252def test_select_budget_counts_only_requests_that_sample ():
274253 survival = np .array ([[0.9 , 0.8 ], [0.7 , 0.6 ]])
275- sps_table = np .ones (( 3 , 9 ) )
276- sps_table [:, 5 :] = 0.1
254+ rates = np .ones (9 )
255+ rates [ 5 :] = 0.1
277256
278257 budget = select_draft_token_budget (
279258 survival ,
280259 num_batch_requests = 2 ,
281260 num_sampling_requests = 1 ,
282261 num_required_target_tokens = 4 ,
283- sps_table = sps_table ,
262+ draft_cost_ms = np .zeros (3 ),
263+ verify_and_sample_cost_ms = 1000.0 / rates ,
284264 )
285265
286266 assert budget == 0
287267
288268
289- def test_sps_table_combines_direct_draft_and_verification_costs ():
269+ def test_cost_tables_keep_draft_and_verification_costs_separate ():
290270 draft_curve = [(1 , 0.2 ), (4 , 0.8 ), (8 , 1.6 )]
291271 verify_curve = [(1 , 2.0 ), (8 , 3.0 ), (64 , 5.0 )]
292272
293- table = build_sps_table_from_costs (
273+ draft_cost_ms , verify_and_sample_cost_ms = build_cost_tables_from_curves (
294274 draft_curve , verify_curve , max_num_reqs = 8 , max_batch_tokens = 64
295275 )
296276
297- assert 1000.0 / table [ 1 , 8 ] == pytest .approx (3.2 )
298- assert 1000.0 / table [ 8 , 8 ] == pytest .approx (4.6 )
277+ assert draft_cost_ms [ 1 ] + verify_and_sample_cost_ms [ 8 ] == pytest .approx (3.2 )
278+ assert draft_cost_ms [ 8 ] + verify_and_sample_cost_ms [ 8 ] == pytest .approx (4.6 )
299279
300280
301281def test_select_budget_temperature_desaturates_zeros ():
@@ -312,9 +292,8 @@ def test_select_budget_temperature_desaturates_zeros():
312292 )
313293 # T=1: exact-zero survival past position 2 truncates both requests.
314294 assert budget_t1 == 4
315- # T=80: sigmoid(-10) > 0, so the budget (int(10*0.9)+1 = 10 admissions,
316- # capped at 5 per request) is fully spent.
317- assert budget_t80 == 10
295+ # T=80: sigmoid(-10) > 0, so 90% of the candidates are admitted.
296+ assert budget_t80 == 9
318297
319298
320299def test_online_sts_fits_order_preserving_temperatures ():
@@ -362,6 +341,7 @@ def test_capacity_manager_assigns_scalar_budget_from_gpu_scores(monkeypatch):
362341 monkeypatch .setattr (
363342 "vllm.v1.worker.gpu.spec_decode.confidence.get_tp_group" ,
364343 lambda : SimpleNamespace (
344+ rank_in_group = 0 ,
365345 broadcast = lambda tensor , src = 0 : broadcasts .append (tensor ),
366346 broadcast_object = lambda value , src = 0 : value ,
367347 ),
@@ -377,9 +357,7 @@ def test_capacity_manager_assigns_scalar_budget_from_gpu_scores(monkeypatch):
377357 )
378358 req_states .req_id_to_index = {"req0" : 0 , "req1" : 1 }
379359 handler = VarlenConfidenceManager (
380- max_num_tokens = 16 ,
381360 req_states = req_states ,
382- device = device ,
383361 speculative_config = _spec_config (),
384362 )
385363 input_batch : Any = SimpleNamespace (
@@ -433,9 +411,7 @@ def test_varlen_capacity_manager_compacts_verifier_batch():
433411 )
434412 req_states .req_id_to_index = {"req0" : 0 , "req1" : 1 }
435413 handler = VarlenConfidenceManager (
436- max_num_tokens = 16 ,
437414 req_states = req_states ,
438- device = device ,
439415 speculative_config = _spec_config (dspark_budget_frac = 0.34 ),
440416 )
441417 handler ._confidence_logits [:2 ].copy_ (
@@ -541,9 +517,7 @@ def test_masked_capacity_manager_marks_pruned_tokens_for_forward_and_sampler():
541517 )
542518 req_states .req_id_to_index = {"req0" : 0 , "req1" : 1 }
543519 handler = MaskedConfidenceManager (
544- max_num_tokens = 16 ,
545520 req_states = req_states ,
546- device = device ,
547521 speculative_config = _spec_config (dspark_budget_frac = 0.34 ),
548522 )
549523 handler ._confidence_logits [:2 ].copy_ (
@@ -679,7 +653,6 @@ def test_varlen_cudagraph_capture_adds_full_desc():
679653
680654 manager ._init_candidates ()
681655
682- assert any (
683- desc .max_req_tokens == manager .decode_query_len
684- for desc in manager ._capture_descs [CUDAGraphMode .FULL ]
685- )
656+ descs = manager ._capture_descs [CUDAGraphMode .FULL ]
657+ assert [desc .num_reqs for desc in descs ] == [3 , 5 ]
658+ assert all (desc .max_req_tokens == manager .decode_query_len for desc in descs )
0 commit comments