[ModelRunner V2] Enable by default for all dense models (#44443)

Signed-off-by: yewentao256 <zhyanwentao@126.com> Signed-off-by: Nick Hill <nickhill123@gmail.com> Co-authored-by: Nick Hill <nickhill123@gmail.com>

Wentao Ye committed Jul 2, 2026 at 06:48 UTC a2f713002df9fd08c0fe13272c76547421721f2d
6 files changed +55 -17
.buildkite/test_areas/model_runner_v2.yaml
+1 -3
@@ -18,9 +18,7 @@ steps:
18 - set -x
19 - export VLLM_USE_V2_MODEL_RUNNER=1
20 - pytest -v -s v1/engine/test_llm_engine.py -k "not test_engine_metrics"
21 - # This requires eager until we sort out CG correctness issues.
22 - # TODO: remove ENFORCE_EAGER here after https://github.com/vllm-project/vllm/pull/32936 is merged.
23 - - ENFORCE_EAGER=1 pytest -v -s v1/e2e/general/test_async_scheduling.py -k "not ngram"
21 + - pytest -v -s v1/e2e/general/test_async_scheduling.py -k "not ngram"
22 - pytest -v -s v1/e2e/general/test_context_length.py
23 - pytest -v -s v1/e2e/general/test_min_tokens.py
24 # Temporary hack filter to exclude ngram spec decoding based tests.
tests/distributed/test_multiproc_executor.py
+8 -3
@@ -283,9 +283,14 @@ def test_multiproc_executor_pipeline_parallel():
283 output_rank = executor._get_output_rank()
284 assert output_rank == 2, "Output rank should be 2 (first rank of last PP stage)"
285
286 - # Verify max_concurrent_batches for pipeline parallel
287 - assert vllm_config.max_concurrent_batches == 2, (
288 - "Max concurrent batches should equal PP size"
286 + # V2 model runner uses one extra batch to overlap async scheduling.
287 + expected_concurrent_batches = 2 + int(
288 + vllm_config.scheduler_config.async_scheduling
289 + and vllm_config.use_v2_model_runner
290 + )
291 + assert vllm_config.max_concurrent_batches == expected_concurrent_batches, (
292 + "Max concurrent batches should follow the configured PP/async "
293 + "scheduling policy"
294 )
295
296 finally:
tests/distributed/test_ray_v2_executor.py
+7 -1
@@ -84,7 +84,13 @@ def assert_executor(executor, tp_size, pp_size):
84 assert executor._get_output_rank() == expected_output_rank
85
86 if pp_size > 1:
87 - assert executor.vllm_config.max_concurrent_batches == pp_size
87 + expected_concurrent_batches = pp_size + int(
88 + executor.vllm_config.scheduler_config.async_scheduling
89 + and executor.vllm_config.use_v2_model_runner
90 + )
91 + assert (
92 + executor.vllm_config.max_concurrent_batches == expected_concurrent_batches
93 + )
94
95 executor.check_health()
96 assert not executor.is_failed
tests/test_config.py
+23 -1
@@ -118,7 +118,17 @@ def test_v2_model_runner_env_tri_state(monkeypatch, env_value, expected):
118 is_moe=False,
119 is_quantized=False,
120 ),
121 - False,
121 + True,
122 + ),
123 + (
124 + SimpleNamespace(
125 + model="google/gemma-2-2b",
126 + architectures=["Gemma2ForCausalLM"],
127 + runner_type="generate",
128 + is_moe=False,
129 + is_quantized=False,
130 + ),
131 + True,
132 ),
133 (
134 SimpleNamespace(
@@ -197,6 +207,18 @@ def test_v2_model_runner_env_tri_state(monkeypatch, env_value, expected):
207 runner_type="generate",
208 is_moe=False,
209 is_quantized=False,
210 + is_hybrid=True,
211 + ),
212 + False,
213 + ),
214 + (
215 + SimpleNamespace(
216 + model="state-spaces/mamba-130m-hf",
217 + architectures=["MambaForCausalLM"],
218 + runner_type="generate",
219 + is_moe=False,
220 + is_quantized=False,
221 + is_attention_free=True,
222 ),
223 False,
224 ),
tests/v1/kv_connector/unit/test_remote_prefill_lifecycle.py
+8 -4
@@ -478,10 +478,14 @@ def test_cannot_schedule_after_recv():
478 # request is retrieved from preempted list.
479 scheduler_output = scheduler.schedule()
480 model_runner_output = create_model_runner_output(reqs=[request_remote])
481 - assert (
482 - scheduler_output.scheduled_cached_reqs.num_computed_tokens[0]
483 - == NUM_PROMPT_BLOCKS * BLOCK_SIZE
484 - )
481 + # V2 emits a resumed (previously preempted) request as a NewRequestData
482 + # rather than a cached request.
483 + if scheduler.use_v2_model_runner:
484 + num_computed = scheduler_output.scheduled_new_reqs[0].num_computed_tokens
485 + else:
486 + cached = scheduler_output.scheduled_cached_reqs
487 + num_computed = cached.num_computed_tokens[0]
488 + assert num_computed == NUM_PROMPT_BLOCKS * BLOCK_SIZE
489 scheduler.update_from_output(scheduler_output, model_runner_output)
490 assert len(scheduler.running) == 1
491 assert _num_waiting_requests(scheduler) == 0
vllm/config/vllm.py
+8 -5
@@ -67,12 +67,9 @@ logger = init_logger(__name__)
67
68 DEFAULT_V2_MODEL_RUNNER_ARCHITECTURES = frozenset(
69 {
70 - "Qwen3ForCausalLM",
70 "DeepseekV2ForCausalLM",
71 "Qwen2MoeForCausalLM",
72 "GraniteMoeForCausalLM",
74 - "LlamaForCausalLM",
75 - "MistralForCausalLM",
73 }
74 )
75
@@ -565,9 +562,15 @@ class VllmConfig:
562 if model_config.runner_type != "generate":
563 return False
564
565 + if getattr(model_config, "is_hybrid", False):
566 + return False
567 +
568 + if getattr(model_config, "is_attention_free", False):
569 + return False
570 architectures = getattr(model_config, "architectures", [])
569 - return any(
570 - arch in DEFAULT_V2_MODEL_RUNNER_ARCHITECTURES for arch in architectures
571 + return (
572 + any(arch in DEFAULT_V2_MODEL_RUNNER_ARCHITECTURES for arch in architectures)
573 + or not model_config.is_moe
574 )
575
576 @property