[Misc] Separate adaptive verification config validation (#54108)

Signed-off-by: Nick Hill <nickhill123@gmail.com>

Nick Hill committed Aug 27, 2026 at 19:03 UTC 8cfba696eef712edbdc4b02aafd5ac73a818736d
1 file changed +36 -30
vllm/config/vllm.py
+36 -30
@@ -1607,6 +1607,7 @@ class VllmConfig:
1607 self._validate_v1_model_runner()
1608
1609 self._validate_batch_sharded_sampling()
1610 + self._validate_adaptive_verification()
1611
1612 # Re-compute compile ranges after platform-specific config updates
1613 # (e.g., XPU may lower max_num_batched_tokens when MLA is enabled)
@@ -2444,34 +2445,6 @@ class VllmConfig:
2445 ):
2446 unsupported.append("EAGLE3 with pipeline parallelism")
2447
2447 - if (
2448 - speculative_config.enable_adaptive_verification
2449 - and self.lora_config is not None
2450 - ):
2451 - # The per-token LoRA mapping is built from CPU placeholder boundaries,
2452 - # while the trimmed batch's true boundaries are decided on the GPU.
2453 - unsupported.append("adaptive verification with LoRA")
2454 -
2455 - if (
2456 - speculative_config.enable_adaptive_verification
2457 - and self.compilation_config.cudagraph_mode == CUDAGraphMode.NONE
2458 - ):
2459 - # The draft budget divides by step costs profiled from captured
2460 - # cudagraphs; eager execution captures none.
2461 - unsupported.append(
2462 - "adaptive verification with enforce_eager/cudagraph_mode=none"
2463 - )
2464 -
2465 - if (
2466 - speculative_config.enable_adaptive_verification
2467 - and self.parallel_config.pipeline_parallel_size > 1
2468 - ):
2469 - # Cost curves and confidences currently only exist on the last PP rank;
2470 - # earlier ranks would diverge on the trimmed batch shape.
2471 - # TODO: we should be able to support adaptive verification with PP by
2472 - # broadcasting the cost curves and confidences to all ranks.
2473 - unsupported.append("adaptive verification with pipeline parallelism")
2474 -
2448 if self.parallel_config.enable_dbo:
2449 unsupported.append("dual batch overlap")
2450
@@ -2503,8 +2476,11 @@ class VllmConfig:
2476 unsupported.append("prefill context parallel")
2477
2478 # DSpark is implemented only by the V2 GPU model runner.
2506 - if self.speculative_config and self.speculative_config.method == "dspark":
2507 - unsupported.append("dspark speculative decoding")
2479 + if self.speculative_config:
2480 + if self.speculative_config.method == "dspark":
2481 + unsupported.append("dspark speculative decoding")
2482 + if self.speculative_config.enable_adaptive_verification:
2483 + unsupported.append("adaptive draft verification")
2484
2485 # Mixed sliding/full DFlash drafts need multiple KV groups (V2 only).
2486 if self._dflash_needs_multi_kv_group():
@@ -2524,6 +2500,36 @@ class VllmConfig:
2500
2501 return unsupported
2502
2503 + def _validate_adaptive_verification(self) -> None:
2504 + spec_config = self.speculative_config
2505 + if not spec_config or not spec_config.enable_adaptive_verification:
2506 + return
2507 +
2508 + if self.lora_config is not None:
2509 + # The per-token LoRA mapping is built from CPU placeholder boundaries,
2510 + # while the trimmed batch's true boundaries are decided on the GPU.
2511 + raise ValueError(
2512 + "Adaptive verification is not currently compatible with LoRA"
2513 + )
2514 +
2515 + if self.compilation_config.cudagraph_mode == CUDAGraphMode.NONE:
2516 + # The draft budget divides by step costs profiled from captured
2517 + # cudagraphs; eager execution captures none.
2518 + raise ValueError(
2519 + "Adaptive verification is not currently compatible with "
2520 + "enforce_eager/cudagraph_mode=none"
2521 + )
2522 +
2523 + if self.parallel_config.pipeline_parallel_size > 1:
2524 + # Cost curves and confidences currently only exist on the last PP rank;
2525 + # earlier ranks would diverge on the trimmed batch shape.
2526 + # TODO: we should be able to support adaptive verification with PP by
2527 + # broadcasting the cost curves and confidences to all ranks.
2528 + raise ValueError(
2529 + "Adaptive verification is not currently compatible "
2530 + "with pipeline parallelism"
2531 + )
2532 +
2533 def _validate_batch_sharded_sampling(self) -> None:
2534 """Validate `enable_batch_sharded_sampling` against the rest of the config."""
2535 if not self.parallel_config.enable_batch_sharded_sampling: