[Config Refactor] sentinel default precedence (#3078)

Signed-off-by: lishunyang <lishunyang12@163.com>
Signed-off-by: xiaohajiayou <923390377@qq.com>
Co-authored-by: lishunyang <lishunyang12@163.com>
This commit is contained in:
Hacoandlishunyang authored and GitHub committed 2026-04-24 20:07:24 +08:00
1 parent b33d536f63
commit 22ffb85063
29 files changed
+458 -188

No files matched your search

+61 -70
View File
@@ -4,6 +4,7 @@
Unit tests for StageConfigFactory and related classes.
"""
import warnings
from dataclasses import dataclass
from pathlib import Path
@@ -1196,99 +1197,89 @@ class TestCLIOverrideFlow:
assert s.runtime_overrides["enforce_eager"] is True
class TestCLIExplicitPrecedence:
"""Verify YAML > argparse defaults; explicit CLI args > YAML."""
class TestSentinelDefaultPrecedence:
"""Caller-typed (non-None) values win over YAML; None values fall through
to YAML / dataclass defaults (#3035)."""
def _stages(self, cli_overrides, cli_explicit_keys):
def _stages(self, cli_overrides):
import vllm_omni.model_executor.models.qwen3_omni.pipeline # noqa: F401
return StageConfigFactory._create_from_registry(
"qwen3_omni_moe",
cli_overrides=cli_overrides,
cli_explicit_keys=cli_explicit_keys,
)
def test_explicit_cli_overrides_yaml(self):
"""User-typed --max-num-seqs wins over the deploy YAML value."""
stages = self._stages(
cli_overrides={"max_num_seqs": 999},
cli_explicit_keys={"max_num_seqs"},
)
# Stage 2 yaml has max_num_seqs=1; explicit CLI must beat it.
def test_typed_kwarg_overrides_yaml(self):
stages = self._stages({"max_num_seqs": 999})
assert stages[2].runtime_overrides.get("max_num_seqs") == 999
def test_default_cli_does_not_override_yaml(self):
"""Argparse defaults must NOT clobber values that are present in YAML."""
stages = self._stages(
cli_overrides={"max_num_seqs": 256},
cli_explicit_keys=set(), # user typed nothing
)
# Stage 2's YAML value (1) should win because the user didn't type --max-num-seqs.
assert stages[2].runtime_overrides.get("max_num_seqs") != 256
def test_none_value_skipped_yaml_wins(self):
stages = self._stages({"max_num_seqs": None})
assert stages[2].runtime_overrides.get("max_num_seqs") is None
assert stages[2].yaml_engine_args.get("max_num_seqs") == 1
def test_default_cli_fills_missing_yaml_field(self):
"""Argparse defaults still fill fields the YAML doesn't set."""
stages = self._stages(
cli_overrides={"some_unrelated_knob": "fallback"},
cli_explicit_keys=set(),
)
# Field absent from YAML → CLI default flows through as a fallback.
assert stages[0].runtime_overrides.get("some_unrelated_knob") == "fallback"
def test_empty_kwargs_yaml_only(self):
stages = self._stages({})
for stage in stages:
assert stage.runtime_overrides == {}
def test_per_stage_overrides_always_explicit(self):
"""``stage_<id>_*`` keys are always treated as explicit."""
stages = self._stages(
cli_overrides={"stage_0_gpu_memory_utilization": 0.42},
cli_explicit_keys=set(), # not in the explicit set, but per-stage
)
def test_typed_kwarg_equal_to_dataclass_default_still_overrides(self):
# Caller intent honored regardless of value coincidence (no heuristic).
stages = self._stages({"gpu_memory_utilization": 0.9})
assert stages[2].runtime_overrides.get("gpu_memory_utilization") == 0.9
def test_per_stage_kwarg_routed_to_correct_stage(self):
stages = self._stages({"stage_0_gpu_memory_utilization": 0.42})
assert stages[0].runtime_overrides.get("gpu_memory_utilization") == 0.42
assert stages[2].runtime_overrides.get("gpu_memory_utilization") is None
def test_none_explicit_set_treats_all_as_explicit(self):
"""Programmatic Omni() callers (cli_explicit_keys=None) keep current behavior."""
stages = self._stages(
cli_overrides={"max_num_seqs": 999},
cli_explicit_keys=None,
)
assert stages[2].runtime_overrides.get("max_num_seqs") == 999
def test_explicit_async_chunk_false_overrides_yaml(self):
"""``--no-async-chunk`` flips the deploy-level async_chunk to False even
when the YAML sets it to True. Verifies that the per-stage
``async_chunk: True`` injection in ``merge_pipeline_deploy`` is skipped
and that ``async_chunk`` does not leak through ``_merge_cli_overrides``.
"""
stages = self._stages(
cli_overrides={"async_chunk": False},
cli_explicit_keys={"async_chunk"},
)
# qwen3_omni_moe.yaml has `async_chunk: true`, so by default every
# stage's engine_args would carry it. With the explicit override, it
# must NOT show up.
def test_async_chunk_false_overrides_yaml_true(self):
stages = self._stages({"async_chunk": False})
for stage in stages:
assert stage.yaml_engine_args.get("async_chunk") is not True
assert stage.runtime_overrides.get("async_chunk") is None
def test_default_async_chunk_leaves_yaml_alone(self):
"""An unset ``--async-chunk`` (default None) must leave the YAML's True
in force on every stage."""
stages = self._stages(
cli_overrides={"async_chunk": None},
cli_explicit_keys=set(),
)
# qwen3_omni_moe.yaml: `async_chunk: true` → injected on every stage.
def test_async_chunk_none_keeps_yaml_true(self):
stages = self._stages({"async_chunk": None})
for stage in stages:
assert stage.yaml_engine_args.get("async_chunk") is True
def test_explicit_enable_prefix_caching_overrides_yaml(self):
"""``--enable-prefix-caching`` (global) flips every stage's
``enable_prefix_caching`` to True regardless of the YAML default."""
stages = self._stages(
cli_overrides={"enable_prefix_caching": True},
cli_explicit_keys={"enable_prefix_caching"},
)
def test_enable_prefix_caching_typed_overrides_yaml(self):
stages = self._stages({"enable_prefix_caching": True})
for stage in stages:
assert stage.runtime_overrides.get("enable_prefix_caching") is True
def test_omni_with_vars_args_anti_pattern_is_safe(self):
# Omni(**vars(args)) with mostly-None namespace must not clobber YAML.
simulated_vars_args = {
"gpu_memory_utilization": None,
"max_num_seqs": None,
"async_chunk": None,
"enable_prefix_caching": None,
"dtype": None,
}
stages = self._stages(simulated_vars_args)
for stage in stages:
assert stage.runtime_overrides == {}
def test_create_from_registry_no_cli_explicit_keys_param(self):
import inspect
sig = inspect.signature(StageConfigFactory._create_from_registry)
named = [p for p in sig.parameters.values() if p.kind != p.VAR_KEYWORD]
assert "cli_explicit_keys" not in {p.name for p in named}
def test_cli_explicit_keys_kwarg_emits_deprecation(self):
import vllm_omni.model_executor.models.qwen3_omni.pipeline # noqa: F401
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
StageConfigFactory._create_from_registry(
"qwen3_omni_moe",
cli_overrides={},
cli_explicit_keys={"max_num_seqs"},
)
assert any(issubclass(x.category, DeprecationWarning) for x in w)
def test_async_chunk_dispatches_processors(self):
"""A single ``qwen3_tts`` pipeline picks per-chunk vs end-to-end
processors based on ``deploy.async_chunk``, without needing a