[Config] Read engine args from VllmOmniConfig (#5678)
Signed-off-by: Acerak01-fy <wfy2003324@163.com> Signed-off-by: Wang fuyin <wfy2003324@163.com>
This commit is contained in:
1 parent
6ec9033065
commit
3d7fc3b9ba
6 files changed
+1090
-72
No files matched your search
@@ -39,7 +39,7 @@ from vllm_omni.config.stage_config import (
|
||||
load_deploy_config,
|
||||
merge_pipeline_deploy,
|
||||
)
|
||||
from vllm_omni.engine.stage_init_utils import build_engine_args_dict
|
||||
from vllm_omni.engine.stage_init_utils import build_legacy_engine_args_dict
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
@@ -124,10 +124,16 @@ def test_vllm_omni_config_from_pipeline_config_matches_merge_pipeline_deploy(mod
|
||||
assert omni_stage.model_config.enforce_eager == engine_args.get("enforce_eager", False)
|
||||
assert omni_stage.load_config.load_format == engine_args.get("load_format", "auto")
|
||||
assert omni_stage.load_config.tokenizer_mode == engine_args.get("tokenizer_mode", "auto")
|
||||
assert omni_stage.cache_config.gpu_memory_utilization == engine_args.get("gpu_memory_utilization", 0.90)
|
||||
assert omni_stage.scheduler_config.max_num_seqs == engine_args.get("max_num_seqs", 128)
|
||||
assert omni_stage.cache_config.gpu_memory_utilization == engine_args.get("gpu_memory_utilization")
|
||||
assert omni_stage.cache_config.enable_prefix_caching == engine_args.get("enable_prefix_caching")
|
||||
expected_disable_hybrid = engine_args.get("disable_hybrid_kv_cache_manager")
|
||||
if omni_stage.stage_pipeline_config.execution_type == StageExecutionType.LLM_GENERATION:
|
||||
expected_disable_hybrid = True if expected_disable_hybrid is None else expected_disable_hybrid
|
||||
assert omni_stage.cache_config.disable_hybrid_kv_cache_manager == expected_disable_hybrid
|
||||
assert omni_stage.scheduler_config.max_num_seqs == engine_args.get("max_num_seqs")
|
||||
assert omni_stage.scheduler_config.max_num_batched_tokens == engine_args.get("max_num_batched_tokens")
|
||||
assert omni_stage.scheduler_config.async_scheduling == engine_args.get("async_scheduling", True)
|
||||
assert omni_stage.scheduler_config.enable_chunked_prefill == engine_args.get("enable_chunked_prefill")
|
||||
assert omni_stage.scheduler_config.async_scheduling == engine_args.get("async_scheduling")
|
||||
legacy_parallel_config = engine_args.get("parallel_config") or {}
|
||||
assert omni_stage.parallel_config.tensor_parallel_size == legacy_parallel_config.get(
|
||||
"tensor_parallel_size",
|
||||
@@ -198,6 +204,64 @@ def test_from_pipeline_config_applies_cli_overrides_without_stage_config_runtime
|
||||
assert stage1.runtime_config.num_gpus == stage1.parallel_config.world_size
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cli_overrides",
|
||||
[
|
||||
{"enable_lora": True},
|
||||
{"stage_0_enable_lora": True},
|
||||
],
|
||||
ids=["global", "stage-scoped"],
|
||||
)
|
||||
def test_from_pipeline_config_rejects_explicit_unowned_engine_cli_fields(cli_overrides):
|
||||
with pytest.raises(ValueError, match=r"no structured config owner: enable_lora"):
|
||||
_from_pipeline_key("qwen3_tts", cli_overrides=cli_overrides)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("engine_extras", "unowned_field"),
|
||||
[
|
||||
({"enable_lora": True}, "enable_lora"),
|
||||
({"parallel_config": {"cfg_parallel_size": 2}}, "parallel_config.cfg_parallel_size"),
|
||||
],
|
||||
ids=["top-level", "nested-parallel-config"],
|
||||
)
|
||||
def test_from_pipeline_config_rejects_unowned_deploy_engine_extras(engine_extras, unowned_field):
|
||||
pipeline = _resolve_pipeline_or_skip("qwen3_tts")
|
||||
deploy = DeployConfig(stages=[StageDeployConfig(stage_id=0, engine_extras=engine_extras)])
|
||||
|
||||
with pytest.raises(ValueError, match=rf"no structured config owner: {unowned_field}"):
|
||||
VllmOmniConfig.from_pipeline_config(pipeline, user_deploy_config=deploy)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("cli_overrides", "stage_id"),
|
||||
[
|
||||
({"kv_cache_dtype": "fp8"}, 0),
|
||||
({"stage_0_kv_cache_dtype": "fp8"}, 0),
|
||||
({"stage_1_kv_cache_dtype": "fp8"}, 1),
|
||||
],
|
||||
ids=["global", "ar-stage", "generation-stage"],
|
||||
)
|
||||
def test_from_pipeline_config_rejects_diffusion_only_cli_fields_for_llm_stages(cli_overrides, stage_id):
|
||||
with pytest.raises(ValueError, match=rf"Stage {stage_id} .*no structured config owner: kv_cache_dtype"):
|
||||
_from_pipeline_key("qwen3_tts", cli_overrides=cli_overrides)
|
||||
|
||||
|
||||
def test_from_pipeline_config_accepts_diffusion_only_cli_fields_for_diffusion_stage():
|
||||
stage = _from_pipeline_key(
|
||||
"dreamzero",
|
||||
deploy_config_path="dreamzero_tp1_cfg2",
|
||||
cli_overrides={"kv_cache_dtype": "fp8"},
|
||||
).stage_by_id(0)
|
||||
|
||||
assert isinstance(stage, VllmOmniDiffusionStageConfig)
|
||||
assert stage.diffusion_config.diffusion_kv_cache_dtype == "fp8"
|
||||
|
||||
|
||||
def test_stage_cli_field_selection_defers_ownership_validation_until_sources_are_merged():
|
||||
assert omni_config_module._stage_cli_overrides(0, {"enable_lora": True}) == {"enable_lora": True}
|
||||
|
||||
|
||||
def test_runtime_num_gpus_is_derived_from_parallel_world_size():
|
||||
omni_config = _from_pipeline_key("hunyuan_image3_dit")
|
||||
stage = omni_config.stage_by_id(0)
|
||||
@@ -380,8 +444,10 @@ def test_vllm_omni_stage_config_public_fields_use_typed_stage_realizations():
|
||||
assert {f.name for f in fields(VllmOmniGenerationStageConfig)} == public_fields
|
||||
|
||||
|
||||
def test_runtime_config_fields_match_rfc_runtime_scope():
|
||||
def test_runtime_config_fields_match_structured_runtime_scope():
|
||||
assert {f.name for f in fields(OmniStageRuntimeConfig)} == {
|
||||
"distributed_executor_backend",
|
||||
"worker_cls",
|
||||
"devices",
|
||||
"num_replicas",
|
||||
"env",
|
||||
@@ -392,8 +458,21 @@ def test_runtime_config_fields_match_rfc_runtime_scope():
|
||||
}
|
||||
|
||||
|
||||
def test_sub_config_fields_match_rfc_scopes():
|
||||
def test_sub_config_fields_match_structured_scopes():
|
||||
assert {f.name for f in fields(OmniStageModelConfig)} == {
|
||||
"model",
|
||||
"model_arch",
|
||||
"revision",
|
||||
"tokenizer_revision",
|
||||
"code_revision",
|
||||
"seed",
|
||||
"logits_processors",
|
||||
"trust_remote_code",
|
||||
"dtype",
|
||||
"attention_backend",
|
||||
"moe_backend",
|
||||
"hf_overrides",
|
||||
"limit_mm_per_prompt",
|
||||
"active_stream_window",
|
||||
"duplex_max_sessions",
|
||||
"enable_sleep_mode",
|
||||
@@ -404,6 +483,7 @@ def test_sub_config_fields_match_rfc_scopes():
|
||||
"task_type",
|
||||
"codec_frame_rate_hz",
|
||||
"enforce_eager",
|
||||
"max_cudagraph_capture_size",
|
||||
"enable_flashinfer_autotune",
|
||||
"compilation_config",
|
||||
"enable_multithread_weight_load",
|
||||
@@ -416,12 +496,16 @@ def test_sub_config_fields_match_rfc_scopes():
|
||||
"tokenizer_subdir",
|
||||
}
|
||||
assert {f.name for f in fields(OmniStageLoadConfig)} == {
|
||||
"tokenizer",
|
||||
"download_dir",
|
||||
"skip_tokenizer_init",
|
||||
"load_format",
|
||||
"tokenizer_mode",
|
||||
"config_format",
|
||||
"skip_mm_profiling",
|
||||
}
|
||||
assert {f.name for f in fields(OmniStageCacheConfig)} == {
|
||||
"kv_cache_memory_bytes",
|
||||
"gpu_memory_utilization",
|
||||
"enable_prefix_caching",
|
||||
"disable_hybrid_kv_cache_manager",
|
||||
@@ -435,6 +519,8 @@ def test_sub_config_fields_match_rfc_scopes():
|
||||
"async_scheduling",
|
||||
}
|
||||
assert {f.name for f in fields(OmniStageConnectorConfig)} == {
|
||||
"async_chunk",
|
||||
"omni_kv_config",
|
||||
"stage_connector",
|
||||
"output_connectors",
|
||||
"input_connectors",
|
||||
@@ -827,7 +913,7 @@ def test_from_pipeline_config_matches_build_engine_args_dict_behavior_for_repres
|
||||
pipeline = _resolve_pipeline_or_skip("qwen3_tts")
|
||||
legacy_stage = merge_pipeline_deploy(pipeline, _load_default_deploy(pipeline))[0]
|
||||
omega_stage = legacy_stage.to_omegaconf()
|
||||
legacy_engine_args = build_engine_args_dict(
|
||||
legacy_engine_args = build_legacy_engine_args_dict(
|
||||
omega_stage,
|
||||
model="/tmp/qwen3-tts",
|
||||
stage_connector_spec={"name": "SharedMemoryConnector", "extra": {}},
|
||||
@@ -922,6 +1008,8 @@ def test_from_pipeline_config_normalizes_diffusion_config_aliases_from_engine_ar
|
||||
|
||||
|
||||
def test_diffusion_config_field_classification_covers_current_fields():
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
|
||||
classified_fields = (
|
||||
omni_config_module._DIFFUSION_SHARED_CONFIG_FIELDS
|
||||
| omni_config_module._DIFFUSION_RUNTIME_CONFIG_FIELDS
|
||||
@@ -929,8 +1017,12 @@ def test_diffusion_config_field_classification_covers_current_fields():
|
||||
)
|
||||
|
||||
assert classified_fields == {f.name for f in fields(omni_config_module._DiffusionConfigProjection)}
|
||||
assert {f.name for f in fields(OmniDiffusionConfig)} <= (
|
||||
classified_fields | omni_config_module._DIFFUSION_MOVED_SHARED_FIELDS
|
||||
)
|
||||
assert {
|
||||
"enable_prompt_embed_cache",
|
||||
"enable_session_state_manager",
|
||||
"prompt_embed_cache_size",
|
||||
"diffusion_kv_cache_dtype",
|
||||
} <= omni_config_module._DIFFUSION_ONLY_CONFIG_FIELDS
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import sys
|
||||
import types
|
||||
from dataclasses import fields, replace
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from pydantic.fields import FieldInfo
|
||||
|
||||
from vllm_omni.config.omni_config import VllmOmniConfig
|
||||
from vllm_omni.config.pipeline_registry import OMNI_PIPELINES, resolve_pipeline_config
|
||||
from vllm_omni.config.stage_config import (
|
||||
DeployConfig,
|
||||
PipelineConfig,
|
||||
StageDeployConfig,
|
||||
StageExecutionType,
|
||||
StagePipelineConfig,
|
||||
StageType,
|
||||
build_stage_runtime_overrides,
|
||||
load_deploy_config,
|
||||
merge_pipeline_deploy,
|
||||
)
|
||||
from vllm_omni.diffusion.data import AttentionConfig, OmniDiffusionConfig
|
||||
from vllm_omni.engine import stage_init_utils
|
||||
from vllm_omni.engine.arg_utils import OmniEngineArgs
|
||||
from vllm_omni.engine.stage_init_utils import (
|
||||
build_engine_args_dict,
|
||||
build_engine_args_dict_from_omni_stage_config,
|
||||
build_legacy_engine_args_dict,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
_DEPLOY_DIR = Path(__file__).parents[2] / "vllm_omni" / "deploy"
|
||||
|
||||
|
||||
def _effective_backend_values(config_cls: type, engine_args: dict) -> dict[str, object]:
|
||||
backend_fields = fields(config_cls)
|
||||
backend_field_names = {backend_field.name for backend_field in backend_fields}
|
||||
backend_config = config_cls(
|
||||
**{name: copy.deepcopy(value) for name, value in engine_args.items() if name in backend_field_names}
|
||||
)
|
||||
effective_values: dict[str, object] = {}
|
||||
for backend_field in backend_fields:
|
||||
value = getattr(backend_config, backend_field.name)
|
||||
if isinstance(value, FieldInfo):
|
||||
value = value.get_default(call_default_factory=True)
|
||||
effective_values[backend_field.name] = value
|
||||
return effective_values
|
||||
|
||||
|
||||
_LLM_BACKEND_FIELDS = frozenset(field.name for field in fields(OmniEngineArgs))
|
||||
_DIFFUSION_BACKEND_FIELDS = frozenset(field.name for field in fields(OmniDiffusionConfig))
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _stable_engine_arg_environment(monkeypatch, tmp_path):
|
||||
from vllm_omni import platforms
|
||||
|
||||
monkeypatch.delenv("VLLM_USE_FLASHINFER_MOE_FP16", raising=False)
|
||||
xcodec_model = tmp_path / "xcodec-model"
|
||||
xcodec_model.mkdir()
|
||||
monkeypatch.setenv("XCODEC1_PATH", str(xcodec_model))
|
||||
platform = platforms.current_omni_platform
|
||||
monkeypatch.setattr(platform, "device_name", "cpu", raising=False)
|
||||
monkeypatch.setattr(platform, "device_type", "cpu", raising=False)
|
||||
monkeypatch.setattr(platform, "is_rocm", lambda: False)
|
||||
monkeypatch.setattr(
|
||||
OmniDiffusionConfig,
|
||||
"_resolve_master_port",
|
||||
lambda config: config.master_port or 29500,
|
||||
)
|
||||
|
||||
def _resolve_test_worker(engine_args):
|
||||
worker_type = engine_args.get("worker_type")
|
||||
if worker_type is not None and engine_args.get("worker_cls") in (None, "auto"):
|
||||
engine_args["worker_cls"] = f"test.{worker_type}.Worker"
|
||||
|
||||
monkeypatch.setattr(stage_init_utils, "resolve_worker_cls", _resolve_test_worker)
|
||||
|
||||
|
||||
def _engine_arg_inputs(tmp_path: Path) -> tuple[PipelineConfig, DeployConfig, str]:
|
||||
parent_model = tmp_path / "parent-model"
|
||||
stage_model = tmp_path / "stage-model"
|
||||
diffusion_model = tmp_path / "diffusion-model"
|
||||
for root in (parent_model, stage_model, diffusion_model):
|
||||
root.mkdir()
|
||||
(stage_model / "ar-model").mkdir()
|
||||
(stage_model / "ar-tokenizer").mkdir()
|
||||
|
||||
pipeline = PipelineConfig(
|
||||
model_type="typed-engine-args",
|
||||
model_arch="PipelineModel",
|
||||
stages=(
|
||||
StagePipelineConfig(
|
||||
stage_id=0,
|
||||
model_stage="thinker",
|
||||
execution_type=StageExecutionType.LLM_AR,
|
||||
requires_multimodal_data=True,
|
||||
hf_config_name="thinker_config",
|
||||
engine_output_type="hidden_states",
|
||||
model_arch="Qwen3OmniMoeForConditionalGeneration",
|
||||
model_subdir="ar-model",
|
||||
tokenizer_subdir="ar-tokenizer",
|
||||
retains_state_across_chunks=True,
|
||||
async_chunk_process_next_stage_input_func="operator.add",
|
||||
),
|
||||
StagePipelineConfig(
|
||||
stage_id=1,
|
||||
model_stage="talker",
|
||||
execution_type=StageExecutionType.LLM_GENERATION,
|
||||
input_sources=(0,),
|
||||
engine_output_type="audio_tokens",
|
||||
),
|
||||
StagePipelineConfig(
|
||||
stage_id=2,
|
||||
model_stage="dit",
|
||||
execution_type=StageExecutionType.DIFFUSION,
|
||||
input_sources=(1,),
|
||||
final_output=True,
|
||||
final_output_type="image",
|
||||
model_arch="TypedDiffusionPipeline",
|
||||
),
|
||||
),
|
||||
)
|
||||
deploy = DeployConfig(
|
||||
async_chunk=True,
|
||||
trust_remote_code=True,
|
||||
dtype="float16",
|
||||
distributed_executor_backend="mp",
|
||||
stages=[
|
||||
StageDeployConfig(
|
||||
stage_id=0,
|
||||
tensor_parallel_size=2,
|
||||
max_num_seqs=8,
|
||||
default_sampling_params={"extra_args": {"voice": "test"}},
|
||||
engine_extras={
|
||||
"model": str(stage_model),
|
||||
"attention_backend": "FLASHINFER",
|
||||
"hf_overrides": {"rope_scaling": {"factor": 2}},
|
||||
"limit_mm_per_prompt": {"audio": 1},
|
||||
"logits_processors": ["test.custom.LogitsProcessor"],
|
||||
"kv_cache_memory_bytes": 1024,
|
||||
"omni_kv_config": {"need_send_cache": True},
|
||||
"max_cudagraph_capture_size": 0,
|
||||
"worker_cls": "test.custom.Worker",
|
||||
},
|
||||
),
|
||||
StageDeployConfig(
|
||||
stage_id=1,
|
||||
max_num_seqs=4,
|
||||
),
|
||||
StageDeployConfig(
|
||||
stage_id=2,
|
||||
tensor_parallel_size=2,
|
||||
vae_parallel_mode="spatial_shard_height",
|
||||
diffusion_attention_config={
|
||||
"default": {"backend": "FLASH_ATTN"},
|
||||
"per_role": {"cross": {"backend": "TORCH_SDPA"}},
|
||||
},
|
||||
engine_extras={
|
||||
"model": str(diffusion_model),
|
||||
"engine_backend": "test.diffusion.Engine",
|
||||
"enable_session_state_manager": True,
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
return pipeline, deploy, str(parent_model)
|
||||
|
||||
|
||||
def _legacy_and_typed_stages(
|
||||
pipeline: PipelineConfig,
|
||||
deploy: DeployConfig,
|
||||
model: str,
|
||||
cli_overrides: dict[str, object] | None = None,
|
||||
):
|
||||
resolved_cli_overrides = {"model": model, **(cli_overrides or {})}
|
||||
legacy_stage_configs = merge_pipeline_deploy(
|
||||
pipeline,
|
||||
copy.deepcopy(deploy),
|
||||
)
|
||||
for stage in legacy_stage_configs:
|
||||
stage.runtime_overrides = build_stage_runtime_overrides(
|
||||
stage.stage_id,
|
||||
resolved_cli_overrides,
|
||||
)
|
||||
legacy_stages = [stage.to_omegaconf() for stage in legacy_stage_configs]
|
||||
omni_config = VllmOmniConfig.from_pipeline_config(
|
||||
pipeline,
|
||||
user_deploy_config=copy.deepcopy(deploy),
|
||||
cli_overrides=resolved_cli_overrides,
|
||||
)
|
||||
return legacy_stages, omni_config
|
||||
|
||||
|
||||
def test_typed_llm_engine_args_preserve_legacy_adapter_behavior(tmp_path):
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(pipeline, deploy, model)
|
||||
connector_spec = {"name": "SharedMemoryConnector", "extra": {"mode": "test"}}
|
||||
cli_tokenizer = "/external/tokenizer"
|
||||
|
||||
typed_args_by_stage = {}
|
||||
for stage_id in (0, 1):
|
||||
legacy_args = build_legacy_engine_args_dict(
|
||||
legacy_stages[stage_id],
|
||||
model,
|
||||
stage_connector_spec=connector_spec,
|
||||
cli_tokenizer=cli_tokenizer,
|
||||
)
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(
|
||||
omni_config.stage_by_id(stage_id),
|
||||
model,
|
||||
stage_connector_spec=connector_spec,
|
||||
cli_tokenizer=cli_tokenizer,
|
||||
)
|
||||
typed_args_by_stage[stage_id] = typed_args
|
||||
|
||||
assert {name: typed_args[name] for name in legacy_args} == legacy_args
|
||||
|
||||
thinker_args = typed_args_by_stage[0]
|
||||
assert thinker_args["model"] == str(tmp_path / "stage-model" / "ar-model")
|
||||
assert thinker_args["tokenizer"] == str(tmp_path / "stage-model" / "ar-tokenizer")
|
||||
assert thinker_args["stage_id"] == 0
|
||||
assert thinker_args["stage_connector_spec"] == connector_spec
|
||||
assert thinker_args["has_sampling_extra_args"] is True
|
||||
assert thinker_args["omni_kv_config"] == {"need_send_cache": True}
|
||||
assert thinker_args["worker_cls"] == "test.custom.Worker"
|
||||
assert thinker_args["attention_backend"] == "FLASHINFER"
|
||||
assert thinker_args["trust_remote_code"] is True
|
||||
assert thinker_args["dtype"] == "float16"
|
||||
assert thinker_args["distributed_executor_backend"] == "mp"
|
||||
assert thinker_args["tensor_parallel_size"] == 2
|
||||
assert thinker_args["hf_overrides"] == {"rope_scaling": {"factor": 2}}
|
||||
assert thinker_args["limit_mm_per_prompt"] == {"audio": 1}
|
||||
assert thinker_args["logits_processors"] == ["test.custom.LogitsProcessor"]
|
||||
assert thinker_args["retains_state_across_chunks"] is True
|
||||
assert thinker_args["kv_cache_memory_bytes"] == 1024
|
||||
assert thinker_args["max_cudagraph_capture_size"] == 0
|
||||
|
||||
talker_args = typed_args_by_stage[1]
|
||||
assert talker_args["model"] == model
|
||||
assert talker_args["tokenizer"] == cli_tokenizer
|
||||
assert talker_args["worker_cls"] == "test.generation.Worker"
|
||||
assert talker_args["disable_hybrid_kv_cache_manager"] is True
|
||||
assert talker_args["enable_prefix_caching"] is False
|
||||
|
||||
|
||||
def test_typed_diffusion_engine_args_use_structured_diffusion_config(tmp_path):
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(pipeline, deploy, model)
|
||||
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stages[2], model)
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(
|
||||
omni_config.stage_by_id(2),
|
||||
model,
|
||||
)
|
||||
|
||||
assert typed_args["stage_id"] == legacy_args["stage_id"] == 2
|
||||
assert typed_args["model"] == legacy_args["model"] == str(tmp_path / "diffusion-model")
|
||||
assert omni_config.stage_by_id(2).diffusion_config.model == str(tmp_path / "diffusion-model")
|
||||
assert typed_args["model_stage"] == legacy_args["model_stage"] == "dit"
|
||||
assert typed_args["model_arch"] == legacy_args["model_arch"] == "TypedDiffusionPipeline"
|
||||
assert typed_args["model_class_name"] == legacy_args["model_class_name"] == "TypedDiffusionPipeline"
|
||||
assert typed_args["engine_backend"] == legacy_args["engine_backend"] == "test.diffusion.Engine"
|
||||
assert typed_args["enable_session_state_manager"] is legacy_args["enable_session_state_manager"] is True
|
||||
assert typed_args["parallel_config"]["tensor_parallel_size"] == 2
|
||||
assert typed_args["parallel_config"]["vae_parallel_mode"] == "spatial_shard_height"
|
||||
assert isinstance(typed_args["diffusion_attention_config"], AttentionConfig)
|
||||
assert typed_args["diffusion_attention_config"].default.backend == "FLASH_ATTN"
|
||||
assert typed_args["diffusion_attention_config"].per_role["cross"].backend == "TORCH_SDPA"
|
||||
|
||||
|
||||
def test_typed_engine_args_preserve_explicit_backend_default_overrides(tmp_path):
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
deploy.enable_prefix_caching = False
|
||||
deploy.enable_chunked_prefill = False
|
||||
thinker_deploy = deploy.stages[0]
|
||||
thinker_deploy.gpu_memory_utilization = 0.75
|
||||
thinker_deploy.disable_hybrid_kv_cache_manager = False
|
||||
thinker_deploy.async_scheduling = False
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(pipeline, deploy, model)
|
||||
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stages[0], model)
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(
|
||||
omni_config.stage_by_id(0),
|
||||
model,
|
||||
)
|
||||
expected = {
|
||||
"gpu_memory_utilization": 0.75,
|
||||
"enable_prefix_caching": False,
|
||||
"disable_hybrid_kv_cache_manager": False,
|
||||
"max_num_seqs": 8,
|
||||
"enable_chunked_prefill": False,
|
||||
"async_scheduling": False,
|
||||
}
|
||||
|
||||
assert {name: legacy_args[name] for name in expected} == expected
|
||||
assert {name: typed_args[name] for name in expected} == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stage_scoped", [False, True], ids=["global", "stage-scoped"])
|
||||
def test_typed_engine_args_preserve_inherited_model_and_load_cli_fields(tmp_path, stage_scoped):
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
expected = {
|
||||
"revision": "model-rev",
|
||||
"tokenizer_revision": "tokenizer-rev",
|
||||
"code_revision": "code-rev",
|
||||
"seed": 123,
|
||||
"download_dir": str(tmp_path / "downloads"),
|
||||
}
|
||||
cli_overrides = {f"stage_0_{name}" if stage_scoped else name: value for name, value in expected.items()}
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(
|
||||
pipeline,
|
||||
deploy,
|
||||
model,
|
||||
cli_overrides,
|
||||
)
|
||||
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stages[0], model)
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(
|
||||
omni_config.stage_by_id(0),
|
||||
model,
|
||||
)
|
||||
|
||||
assert {name: legacy_args[name] for name in expected} == expected
|
||||
assert {name: typed_args[name] for name in expected} == expected
|
||||
|
||||
|
||||
def test_typed_engine_args_preserve_deploy_subdirectory_precedence(tmp_path):
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
stage_model = tmp_path / "stage-model"
|
||||
(stage_model / "override-model").mkdir()
|
||||
(stage_model / "override-tokenizer").mkdir()
|
||||
deploy.stages[0].engine_extras.update(
|
||||
{
|
||||
"model_subdir": "override-model",
|
||||
"tokenizer_subdir": "override-tokenizer",
|
||||
}
|
||||
)
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(pipeline, deploy, model)
|
||||
|
||||
typed_stage = omni_config.stage_by_id(0)
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stages[0], model)
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(typed_stage, model)
|
||||
|
||||
assert typed_stage.model_config.model_subdir == "override-model"
|
||||
assert typed_stage.model_config.tokenizer_subdir == "override-tokenizer"
|
||||
assert typed_args["model"] == legacy_args["model"] == str(stage_model / "override-model")
|
||||
assert typed_args["tokenizer"] == legacy_args["tokenizer"] == str(stage_model / "override-tokenizer")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("cli_overrides", "expected"),
|
||||
[
|
||||
({}, {"need_recv_cache": True}),
|
||||
(
|
||||
{"stage_0_omni_kv_config": {"need_send_cache": False}},
|
||||
{"need_send_cache": False},
|
||||
),
|
||||
],
|
||||
ids=["topology-over-deploy", "cli-over-topology"],
|
||||
)
|
||||
def test_typed_engine_args_preserve_legacy_omni_kv_precedence(tmp_path, cli_overrides, expected):
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
thinker = replace(
|
||||
pipeline.stages[0],
|
||||
omni_kv_config={"need_recv_cache": True},
|
||||
)
|
||||
pipeline = replace(pipeline, stages=(thinker, *pipeline.stages[1:]))
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(
|
||||
pipeline,
|
||||
deploy,
|
||||
model,
|
||||
cli_overrides,
|
||||
)
|
||||
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stages[0], model)
|
||||
typed_stage = omni_config.stage_by_id(0)
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(typed_stage, model)
|
||||
|
||||
assert typed_stage.connector_config.omni_kv_config == expected
|
||||
assert typed_args["omni_kv_config"] == legacy_args["omni_kv_config"] == expected
|
||||
|
||||
|
||||
def test_build_engine_args_dict_preserves_legacy_api(tmp_path):
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
legacy_stages, _ = _legacy_and_typed_stages(pipeline, deploy, model)
|
||||
connector_spec = {"name": "SharedMemoryConnector", "extra": {}}
|
||||
|
||||
compatibility_args = build_engine_args_dict(
|
||||
copy.deepcopy(legacy_stages[0]),
|
||||
model,
|
||||
stage_connector_spec=connector_spec,
|
||||
cli_tokenizer="/external/tokenizer",
|
||||
)
|
||||
explicitly_legacy_args = build_legacy_engine_args_dict(
|
||||
copy.deepcopy(legacy_stages[0]),
|
||||
model,
|
||||
stage_connector_spec=connector_spec,
|
||||
cli_tokenizer="/external/tokenizer",
|
||||
)
|
||||
|
||||
assert compatibility_args == explicitly_legacy_args
|
||||
|
||||
|
||||
def test_legacy_engine_args_drop_none_tp_without_mutating_stage_config():
|
||||
stage_config = types.SimpleNamespace(
|
||||
stage_id=0,
|
||||
stage_type="llm",
|
||||
engine_args={"tensor_parallel_size": None},
|
||||
default_sampling_params={},
|
||||
)
|
||||
|
||||
engine_args = build_legacy_engine_args_dict(
|
||||
stage_config,
|
||||
model="test-model",
|
||||
)
|
||||
|
||||
assert "tensor_parallel_size" not in engine_args
|
||||
assert stage_config.engine_args == {"tensor_parallel_size": None}
|
||||
|
||||
|
||||
def test_typed_engine_args_own_rocm_attention_default(monkeypatch, tmp_path):
|
||||
from vllm_omni import platforms
|
||||
|
||||
pipeline, deploy, model = _engine_arg_inputs(tmp_path)
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(pipeline, deploy, model)
|
||||
monkeypatch.setattr(platforms.current_omni_platform, "is_rocm", lambda: True)
|
||||
aiter_module = types.ModuleType("vllm._aiter_ops")
|
||||
setattr(
|
||||
aiter_module,
|
||||
"rocm_aiter_ops",
|
||||
types.SimpleNamespace(is_enabled=lambda: False),
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "vllm._aiter_ops", aiter_module)
|
||||
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stages[1], model)
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(
|
||||
omni_config.stage_by_id(1),
|
||||
model,
|
||||
)
|
||||
|
||||
assert "attention_backend" not in legacy_args
|
||||
assert typed_args["attention_backend"] == "TRITON_ATTN"
|
||||
|
||||
|
||||
def test_typed_ming_image_engine_args_defer_diffusion_batch_default():
|
||||
pipeline = resolve_pipeline_config("ming_flash_omni_image")
|
||||
assert pipeline is not None
|
||||
deploy = load_deploy_config(_DEPLOY_DIR / "ming_flash_omni_image.yaml")
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(
|
||||
pipeline,
|
||||
deploy,
|
||||
model="/tmp",
|
||||
)
|
||||
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stages[1], model="/tmp")
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(
|
||||
omni_config.stage_by_id(1),
|
||||
model="/tmp",
|
||||
)
|
||||
typed_backend_args = {name: value for name, value in typed_args.items() if name in _DIFFUSION_BACKEND_FIELDS}
|
||||
|
||||
assert "max_num_seqs" not in legacy_args
|
||||
assert "max_num_seqs" not in typed_args
|
||||
assert OmniDiffusionConfig(**typed_backend_args).max_num_seqs == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model_type", sorted(OMNI_PIPELINES))
|
||||
def test_typed_engine_args_match_current_registry_backend_semantics(model_type):
|
||||
pipeline = resolve_pipeline_config(model_type)
|
||||
if pipeline is None:
|
||||
pytest.skip(f"Pipeline {model_type!r} requires an HF config to resolve")
|
||||
|
||||
deploy = (
|
||||
load_deploy_config(_DEPLOY_DIR / pipeline.default_deploy_config_name)
|
||||
if pipeline.default_deploy_config_name is not None
|
||||
else DeployConfig()
|
||||
)
|
||||
legacy_stages, omni_config = _legacy_and_typed_stages(
|
||||
pipeline,
|
||||
deploy,
|
||||
model="/tmp",
|
||||
)
|
||||
|
||||
for legacy_stage in legacy_stages:
|
||||
stage_id = legacy_stage.stage_id
|
||||
legacy_args = build_legacy_engine_args_dict(legacy_stage, model="/tmp")
|
||||
typed_args = build_engine_args_dict_from_omni_stage_config(
|
||||
omni_config.stage_by_id(stage_id),
|
||||
model="/tmp",
|
||||
)
|
||||
if legacy_stage.stage_type == StageType.DIFFUSION:
|
||||
backend_fields = _DIFFUSION_BACKEND_FIELDS
|
||||
backend_config_cls = OmniDiffusionConfig
|
||||
else:
|
||||
backend_fields = _LLM_BACKEND_FIELDS
|
||||
backend_config_cls = OmniEngineArgs
|
||||
|
||||
missing_fields = (legacy_args.keys() & backend_fields) - typed_args.keys()
|
||||
assert not missing_fields, f"{model_type} stage {stage_id} lost backend fields: {sorted(missing_fields)}"
|
||||
|
||||
legacy_effective_args = _effective_backend_values(backend_config_cls, legacy_args)
|
||||
typed_effective_args = _effective_backend_values(backend_config_cls, typed_args)
|
||||
assert typed_effective_args == legacy_effective_args, (
|
||||
f"{model_type} stage {stage_id} changed effective backend arguments"
|
||||
)
|
||||
+266
-27
@@ -46,6 +46,29 @@ _EXECUTION_TYPE_TO_STAGE_WORKER: dict[StageExecutionType, tuple[StageType, str |
|
||||
|
||||
_PIPELINE_DEPLOY_CLI_FIELDS = PIPELINE_WIDE_ENGINE_FIELDS
|
||||
|
||||
_NON_STAGE_ENGINE_CLI_FIELDS = frozenset(
|
||||
{
|
||||
"async_chunk",
|
||||
"model",
|
||||
"omni",
|
||||
"output_modalities",
|
||||
"stage_configs_path",
|
||||
"stage_id",
|
||||
"tokenizer",
|
||||
}
|
||||
)
|
||||
|
||||
# Legacy deploy files can still carry StageConfig metadata in engine_extras.
|
||||
# Those values are not backend engine inputs, and the additive typed path keeps
|
||||
# sourcing their effective values from the immutable pipeline topology.
|
||||
_LEGACY_STAGE_METADATA_EXTRA_FIELDS = frozenset(
|
||||
{
|
||||
"final_output",
|
||||
"final_output_type",
|
||||
"is_comprehension",
|
||||
}
|
||||
)
|
||||
|
||||
_QuantizationConfigType: TypeAlias = QuantizationConfig | str | Mapping[str, Any] | None
|
||||
|
||||
|
||||
@@ -55,6 +78,21 @@ class _QuantizationEngineOverrides(TypedDict, total=False):
|
||||
|
||||
|
||||
class _ModelEngineOverrides(TypedDict, total=False):
|
||||
model: str
|
||||
model_arch: str
|
||||
model_subdir: str
|
||||
tokenizer_subdir: str
|
||||
revision: str
|
||||
tokenizer_revision: str
|
||||
code_revision: str
|
||||
seed: int
|
||||
logits_processors: list[str | type]
|
||||
trust_remote_code: bool
|
||||
dtype: Any
|
||||
attention_backend: Any
|
||||
moe_backend: str
|
||||
hf_overrides: Any
|
||||
limit_mm_per_prompt: dict[str, Any]
|
||||
active_stream_window: int
|
||||
enable_sleep_mode: bool
|
||||
subtalker_sampling_params: dict[str, Any]
|
||||
@@ -63,6 +101,7 @@ class _ModelEngineOverrides(TypedDict, total=False):
|
||||
task_type: str
|
||||
codec_frame_rate_hz: float
|
||||
enforce_eager: bool
|
||||
max_cudagraph_capture_size: int
|
||||
enable_flashinfer_autotune: bool
|
||||
compilation_config: dict[str, Any]
|
||||
enable_multithread_weight_load: bool
|
||||
@@ -71,6 +110,9 @@ class _ModelEngineOverrides(TypedDict, total=False):
|
||||
|
||||
|
||||
class _LoadEngineOverrides(TypedDict, total=False):
|
||||
tokenizer: str
|
||||
download_dir: str
|
||||
skip_tokenizer_init: bool
|
||||
load_format: str
|
||||
tokenizer_mode: str
|
||||
config_format: str
|
||||
@@ -78,6 +120,7 @@ class _LoadEngineOverrides(TypedDict, total=False):
|
||||
|
||||
|
||||
class _CacheEngineOverrides(TypedDict, total=False):
|
||||
kv_cache_memory_bytes: int
|
||||
gpu_memory_utilization: float
|
||||
enable_prefix_caching: bool
|
||||
disable_hybrid_kv_cache_manager: bool
|
||||
@@ -93,6 +136,8 @@ class _SchedulerEngineOverrides(TypedDict, total=False):
|
||||
|
||||
|
||||
class _RuntimeEngineOverrides(TypedDict, total=False):
|
||||
distributed_executor_backend: Any
|
||||
worker_cls: str
|
||||
devices: str
|
||||
num_replicas: int
|
||||
env: dict[str, Any]
|
||||
@@ -113,6 +158,7 @@ class _ParallelConfigEngineOverrides(TypedDict, total=False):
|
||||
ulysses_mode: str
|
||||
cfg_parallel_size: int
|
||||
vae_patch_parallel_size: int
|
||||
vae_parallel_mode: str
|
||||
text_encoder_tp_size: int
|
||||
use_hsdp: bool
|
||||
mask_sp_padding: bool
|
||||
@@ -125,6 +171,10 @@ class _ParallelEngineOverrides(_ParallelConfigEngineOverrides, total=False):
|
||||
parallel_config: _ParallelConfigEngineOverrides | Mapping[str, Any]
|
||||
|
||||
|
||||
class _ConnectorEngineOverrides(TypedDict, total=False):
|
||||
omni_kv_config: dict[str, Any]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _StageEngineValues:
|
||||
"""Typed projections of legacy flat per-stage ``yaml_engine_args``."""
|
||||
@@ -134,6 +184,7 @@ class _StageEngineValues:
|
||||
load: _LoadEngineOverrides
|
||||
cache: _CacheEngineOverrides
|
||||
scheduler: _SchedulerEngineOverrides
|
||||
connector: _ConnectorEngineOverrides
|
||||
runtime: _RuntimeEngineOverrides
|
||||
parallel: _ParallelEngineOverrides
|
||||
diffusion: _DiffusionEngineOverrides
|
||||
@@ -255,8 +306,21 @@ def _get_deploy_config(
|
||||
|
||||
@config
|
||||
class OmniStageModelConfig:
|
||||
"""Per-stage model behavior."""
|
||||
"""Per-stage model behavior and resolved model-engine inputs."""
|
||||
|
||||
model: str | None = None
|
||||
model_arch: str | None = None
|
||||
revision: str | None = None
|
||||
tokenizer_revision: str | None = None
|
||||
code_revision: str | None = None
|
||||
seed: int | None = None
|
||||
logits_processors: list[str | type] | None = None
|
||||
trust_remote_code: bool = False
|
||||
dtype: Any = "auto"
|
||||
attention_backend: Any = None
|
||||
moe_backend: str = "auto"
|
||||
hf_overrides: Any = None
|
||||
limit_mm_per_prompt: dict[str, Any] | None = None
|
||||
active_stream_window: int = Field(default=0, ge=0)
|
||||
duplex_max_sessions: int = Field(default=1, ge=1)
|
||||
enable_sleep_mode: bool = False
|
||||
@@ -267,6 +331,7 @@ class OmniStageModelConfig:
|
||||
task_type: str | None = None
|
||||
codec_frame_rate_hz: float | None = None
|
||||
enforce_eager: bool = False
|
||||
max_cudagraph_capture_size: int | None = Field(default=None, ge=0)
|
||||
enable_flashinfer_autotune: bool | None = None
|
||||
compilation_config: dict[str, Any] | None = None
|
||||
enable_multithread_weight_load: bool = True
|
||||
@@ -281,8 +346,11 @@ class OmniStageModelConfig:
|
||||
|
||||
@config
|
||||
class OmniStageLoadConfig:
|
||||
"""Per-stage loading behavior."""
|
||||
"""Per-stage loading behavior and resolved tokenizer input."""
|
||||
|
||||
tokenizer: str | None = None
|
||||
download_dir: str | None = None
|
||||
skip_tokenizer_init: bool = False
|
||||
load_format: str = "auto"
|
||||
tokenizer_mode: str = "auto"
|
||||
config_format: str | None = None
|
||||
@@ -297,9 +365,11 @@ class OmniStageCacheConfig:
|
||||
vLLM-Omni diffusion-specific cache backends such as TeaCache and Cache-DiT.
|
||||
"""
|
||||
|
||||
gpu_memory_utilization: float = Field(default=0.90, gt=0.0, le=1.0)
|
||||
enable_prefix_caching: bool = False
|
||||
disable_hybrid_kv_cache_manager: bool = False
|
||||
kv_cache_memory_bytes: int | None = Field(default=None, ge=0)
|
||||
# None preserves backend-owned defaults; explicit values still project.
|
||||
gpu_memory_utilization: float | None = Field(default=None, gt=0.0, le=1.0)
|
||||
enable_prefix_caching: bool | None = None
|
||||
disable_hybrid_kv_cache_manager: bool | None = None
|
||||
mm_processor_cache_gb: float | None = Field(default=None, ge=0.0)
|
||||
|
||||
|
||||
@@ -307,14 +377,18 @@ class OmniStageCacheConfig:
|
||||
class OmniStageSchedulerConfig:
|
||||
"""Per-stage request scheduling behavior."""
|
||||
|
||||
max_num_seqs: int = Field(default=128, ge=1)
|
||||
max_num_seqs: int | None = Field(default=None, ge=1)
|
||||
max_num_batched_tokens: int | None = Field(default=None, ge=1)
|
||||
max_model_len: int | None = Field(default=None, ge=-1)
|
||||
enable_chunked_prefill: bool = False
|
||||
async_scheduling: bool = True
|
||||
enable_chunked_prefill: bool | None = None
|
||||
async_scheduling: bool | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.max_num_batched_tokens is not None and self.max_num_batched_tokens < self.max_num_seqs:
|
||||
if (
|
||||
self.max_num_batched_tokens is not None
|
||||
and self.max_num_seqs is not None
|
||||
and self.max_num_batched_tokens < self.max_num_seqs
|
||||
):
|
||||
raise ValueError(
|
||||
f"max_num_batched_tokens ({self.max_num_batched_tokens}) must be >= max_num_seqs ({self.max_num_seqs})"
|
||||
)
|
||||
@@ -322,8 +396,10 @@ class OmniStageSchedulerConfig:
|
||||
|
||||
@config
|
||||
class OmniStageConnectorConfig:
|
||||
"""Per-stage inter-stage connector wiring."""
|
||||
"""Per-stage connector wiring and resolved transfer mode."""
|
||||
|
||||
async_chunk: bool = False
|
||||
omni_kv_config: dict[str, Any] | None = None
|
||||
stage_connector: dict[str, Any] = field(
|
||||
default_factory=lambda: {
|
||||
"name": "SharedMemoryConnector",
|
||||
@@ -336,8 +412,10 @@ class OmniStageConnectorConfig:
|
||||
|
||||
@config
|
||||
class OmniStageRuntimeConfig:
|
||||
"""Per-stage process placement and runtime behavior."""
|
||||
"""Per-stage process placement and backend runtime behavior."""
|
||||
|
||||
distributed_executor_backend: Any = None
|
||||
worker_cls: str | None = None
|
||||
devices: str | None = None
|
||||
num_replicas: int = Field(default=1, ge=1)
|
||||
env: dict[str, Any] | None = None
|
||||
@@ -448,6 +526,10 @@ class _DiffusionConfigProjection:
|
||||
stage_id: int = 0
|
||||
model: str | None = None
|
||||
model_class_name: str | None = None
|
||||
engine_backend: str | type = "default"
|
||||
diffusion_model_runner_cls: str | type | None = None
|
||||
request_batch_max_wait_ms: float = 0.0
|
||||
streaming_output: bool = False
|
||||
model_arch: str | None = None
|
||||
task_type: str | None = None
|
||||
dtype: Any = "auto"
|
||||
@@ -457,6 +539,7 @@ class _DiffusionConfigProjection:
|
||||
dist_timeout: int | None = None
|
||||
nccl_port: int | None = None
|
||||
master_port: int | None = None
|
||||
scheduler_port: int | None = None
|
||||
host: str | None = None
|
||||
port: int | None = None
|
||||
model_config: dict[str, Any] = field(default_factory=dict)
|
||||
@@ -468,6 +551,7 @@ class _DiffusionConfigProjection:
|
||||
enable_cache_dit_summary: bool = False
|
||||
enable_prompt_embed_cache: bool = False
|
||||
prompt_embed_cache_size: int = Field(default=32, ge=1)
|
||||
enable_session_state_manager: bool = False
|
||||
diffusion_load_format: str = "default"
|
||||
diffusers_load_kwargs: dict[str, Any] = field(default_factory=dict)
|
||||
diffusers_call_kwargs: dict[str, Any] = field(default_factory=dict)
|
||||
@@ -503,6 +587,7 @@ class _DiffusionConfigProjection:
|
||||
step_execution: bool = False
|
||||
supports_multimodal_inputs: bool = False
|
||||
max_multimodal_image_inputs: int | None = None
|
||||
supports_mixed_reference_inputs: bool = False
|
||||
model_paths: dict[str, str] = field(default_factory=dict)
|
||||
model_loaded: dict[str, bool] = field(
|
||||
default_factory=lambda: {
|
||||
@@ -696,6 +781,7 @@ _DIFFUSION_RUNTIME_CONFIG_FIELDS = frozenset(
|
||||
"port",
|
||||
"nccl_port",
|
||||
"master_port",
|
||||
"scheduler_port",
|
||||
"worker_extension_cls",
|
||||
"enable_stage_verification",
|
||||
"prompt_file_path",
|
||||
@@ -743,21 +829,85 @@ _MODEL_ENGINE_FIELDS = frozenset(_ModelEngineOverrides.__annotations__)
|
||||
_LOAD_ENGINE_FIELDS = frozenset(_LoadEngineOverrides.__annotations__)
|
||||
_CACHE_ENGINE_FIELDS = frozenset(_CacheEngineOverrides.__annotations__)
|
||||
_SCHEDULER_ENGINE_FIELDS = frozenset(_SchedulerEngineOverrides.__annotations__)
|
||||
_CONNECTOR_ENGINE_FIELDS = frozenset(_ConnectorEngineOverrides.__annotations__)
|
||||
_RUNTIME_ENGINE_FIELDS = frozenset(_RuntimeEngineOverrides.__annotations__)
|
||||
_PARALLEL_CONFIG_ENGINE_FIELDS = frozenset(_ParallelConfigEngineOverrides.__annotations__)
|
||||
_PARALLEL_ENGINE_FIELDS = _PARALLEL_CONFIG_ENGINE_FIELDS | {"parallel_config"}
|
||||
_COMMON_STAGE_ENGINE_FIELDS = (
|
||||
_QUANTIZATION_ENGINE_FIELDS
|
||||
| _MODEL_ENGINE_FIELDS
|
||||
| _LOAD_ENGINE_FIELDS
|
||||
| _CACHE_ENGINE_FIELDS
|
||||
| _SCHEDULER_ENGINE_FIELDS
|
||||
| _CONNECTOR_ENGINE_FIELDS
|
||||
| _RUNTIME_ENGINE_FIELDS
|
||||
)
|
||||
_LLM_PARALLEL_CONFIG_ENGINE_FIELDS = frozenset(f.name for f in fields(OmniStageParallelConfig)) - {"world_size"}
|
||||
_DIFFUSION_PARALLEL_CONFIG_ENGINE_FIELDS = frozenset(f.name for f in fields(OmniStageDiffusionParallelConfig)) - {
|
||||
"world_size"
|
||||
}
|
||||
_LLM_STAGE_ENGINE_FIELDS = _COMMON_STAGE_ENGINE_FIELDS | _LLM_PARALLEL_CONFIG_ENGINE_FIELDS | {"parallel_config"}
|
||||
_DIFFUSION_OWNED_STAGE_ENGINE_FIELDS = (
|
||||
_COMMON_STAGE_ENGINE_FIELDS
|
||||
| _DIFFUSION_PARALLEL_CONFIG_ENGINE_FIELDS
|
||||
| _DIFFUSION_STAGE_ENGINE_FIELDS
|
||||
| {"parallel_config"}
|
||||
)
|
||||
|
||||
# This all-stage union is used only to discover global CLI candidates. Stage
|
||||
# validation must use the execution-type-specific sets below.
|
||||
_STAGE_ENGINE_FIELDS = _LLM_STAGE_ENGINE_FIELDS | _DIFFUSION_OWNED_STAGE_ENGINE_FIELDS
|
||||
_STAGE_ENGINE_FIELDS_BY_EXECUTION_TYPE = {
|
||||
StageExecutionType.LLM_AR: _LLM_STAGE_ENGINE_FIELDS,
|
||||
StageExecutionType.LLM_GENERATION: _LLM_STAGE_ENGINE_FIELDS,
|
||||
StageExecutionType.DIFFUSION: _DIFFUSION_OWNED_STAGE_ENGINE_FIELDS,
|
||||
}
|
||||
_PARALLEL_CONFIG_ENGINE_FIELDS_BY_EXECUTION_TYPE = {
|
||||
StageExecutionType.LLM_AR: _LLM_PARALLEL_CONFIG_ENGINE_FIELDS,
|
||||
StageExecutionType.LLM_GENERATION: _LLM_PARALLEL_CONFIG_ENGINE_FIELDS,
|
||||
StageExecutionType.DIFFUSION: _DIFFUSION_PARALLEL_CONFIG_ENGINE_FIELDS,
|
||||
}
|
||||
|
||||
|
||||
def _validate_stage_engine_override_ownership(
|
||||
stage_id: int,
|
||||
execution_type: StageExecutionType,
|
||||
overrides: Mapping[str, Any],
|
||||
) -> None:
|
||||
try:
|
||||
owner_fields = _STAGE_ENGINE_FIELDS_BY_EXECUTION_TYPE[execution_type]
|
||||
parallel_owner_fields = _PARALLEL_CONFIG_ENGINE_FIELDS_BY_EXECUTION_TYPE[execution_type]
|
||||
except KeyError as exc:
|
||||
raise ValueError(f"Unsupported stage execution type: {execution_type!r}") from exc
|
||||
|
||||
unowned_fields = set(overrides) - owner_fields
|
||||
parallel_config = overrides.get("parallel_config")
|
||||
if isinstance(parallel_config, Mapping):
|
||||
unowned_fields.update(f"parallel_config.{name}" for name in set(parallel_config) - parallel_owner_fields)
|
||||
if unowned_fields:
|
||||
names = ", ".join(sorted(unowned_fields))
|
||||
raise ValueError(
|
||||
f"Stage {stage_id} ({execution_type.value}) has explicit engine argument(s) "
|
||||
f"with no structured config owner: {names}"
|
||||
)
|
||||
|
||||
|
||||
def _global_stage_cli_fields() -> frozenset[str]:
|
||||
# Lazy import avoids vllm_omni.config -> omni_config -> engine.arg_utils ->
|
||||
# vllm_omni.config during package-level config imports.
|
||||
from vllm_omni.engine.arg_utils import OmniEngineArgs
|
||||
from vllm_omni.engine.arg_utils import OmniEngineArgs, orchestrator_field_names
|
||||
|
||||
return (
|
||||
candidates = (
|
||||
frozenset(f.name for f in fields(OmniEngineArgs))
|
||||
| frozenset(_STAGE_DEPLOY_ENGINE_FIELDS)
|
||||
| frozenset(_PIPELINE_DEPLOY_CLI_FIELDS)
|
||||
) - {"model", "stage_id", "stage_configs_path", "async_chunk"}
|
||||
)
|
||||
externally_consumed = (
|
||||
_NON_STAGE_ENGINE_CLI_FIELDS
|
||||
| frozenset(f.name for f in fields(VllmOmniOrchestratorConfig))
|
||||
| (orchestrator_field_names() - _STAGE_ENGINE_FIELDS)
|
||||
)
|
||||
return candidates - externally_consumed
|
||||
|
||||
|
||||
def _mapping_or_empty(value: Any) -> Mapping[str, Any]:
|
||||
@@ -777,17 +927,33 @@ def _stage_engine_overrides(stage_deploy: StageDeployConfig | None) -> dict[str,
|
||||
value = getattr(stage_deploy, name)
|
||||
if value is not None:
|
||||
overrides[name] = _copy_value(value)
|
||||
overrides.update(_copy_value(stage_deploy.engine_extras))
|
||||
overrides.update(
|
||||
{
|
||||
name: _copy_value(value)
|
||||
for name, value in stage_deploy.engine_extras.items()
|
||||
if name not in _LEGACY_STAGE_METADATA_EXTRA_FIELDS
|
||||
}
|
||||
)
|
||||
return overrides
|
||||
|
||||
|
||||
def _stage_engine_values(
|
||||
stage_deploy: StageDeployConfig | None,
|
||||
topology: StagePipelineConfig,
|
||||
stage_cli_overrides: Mapping[str, Any] | None = None,
|
||||
) -> _StageEngineValues:
|
||||
engine = _stage_engine_overrides(stage_deploy)
|
||||
# Preserve legacy ordering: topology-owned KV roles override deploy
|
||||
# extras, while an explicit CLI override remains highest priority.
|
||||
if topology.omni_kv_config:
|
||||
engine["omni_kv_config"] = _copy_value(topology.omni_kv_config)
|
||||
if stage_cli_overrides:
|
||||
engine.update(_copy_value(stage_cli_overrides))
|
||||
_validate_stage_engine_override_ownership(
|
||||
topology.stage_id,
|
||||
topology.execution_type,
|
||||
engine,
|
||||
)
|
||||
return _StageEngineValues(
|
||||
quantization=cast(
|
||||
_QuantizationEngineOverrides,
|
||||
@@ -800,6 +966,10 @@ def _stage_engine_values(
|
||||
_SchedulerEngineOverrides,
|
||||
_select_engine_overrides(engine, _SCHEDULER_ENGINE_FIELDS),
|
||||
),
|
||||
connector=cast(
|
||||
_ConnectorEngineOverrides,
|
||||
_select_engine_overrides(engine, _CONNECTOR_ENGINE_FIELDS),
|
||||
),
|
||||
runtime=cast(_RuntimeEngineOverrides, _select_engine_overrides(engine, _RUNTIME_ENGINE_FIELDS)),
|
||||
parallel=cast(_ParallelEngineOverrides, _select_engine_overrides(engine, _PARALLEL_ENGINE_FIELDS)),
|
||||
diffusion=_DiffusionEngineOverrides.from_engine(engine),
|
||||
@@ -895,9 +1065,10 @@ class BaseVllmOmniStageConfig:
|
||||
|
||||
@property
|
||||
def scheduler_cls(self) -> str | None:
|
||||
return _resolve_scheduler_path(
|
||||
async_scheduling = self.scheduler_config.async_scheduling
|
||||
return self.stage_pipeline_config.scheduler_cls or _resolve_scheduler_path(
|
||||
self.stage_pipeline_config.execution_type,
|
||||
self.scheduler_config.async_scheduling,
|
||||
True if async_scheduling is None else async_scheduling,
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -959,11 +1130,14 @@ StageConfigType: TypeAlias = VllmOmniARStageConfig | VllmOmniGenerationStageConf
|
||||
|
||||
|
||||
def _build_common_stage_config_kwargs(
|
||||
pipeline: PipelineConfig,
|
||||
deploy: DeployConfig,
|
||||
topology: StagePipelineConfig,
|
||||
stage_deploy: StageDeployConfig | None,
|
||||
engine: _StageEngineValues,
|
||||
parallel_config_cls: type[OmniStageParallelConfig] = OmniStageParallelConfig,
|
||||
*,
|
||||
model: str | None,
|
||||
) -> tuple[dict[str, Any], str | None, str | None]:
|
||||
input_proc, next_stage_proc = _select_processor_funcs(topology, bool(deploy.async_chunk))
|
||||
quantization_config = _build_quantization_config(deploy, engine.quantization)
|
||||
@@ -973,16 +1147,36 @@ def _build_common_stage_config_kwargs(
|
||||
{
|
||||
"stage_pipeline_config": topology,
|
||||
"model_config": _build_model_config(
|
||||
pipeline,
|
||||
deploy,
|
||||
topology,
|
||||
stage_deploy,
|
||||
engine.model,
|
||||
duplex_max_sessions=(deploy.duplex_session.max_sessions if deploy.session_mode == "duplex" else 1),
|
||||
model=model,
|
||||
),
|
||||
"load_config": _build_load_config(topology, engine.load),
|
||||
"cache_config": _build_cache_config(
|
||||
deploy,
|
||||
engine.cache,
|
||||
topology.execution_type,
|
||||
),
|
||||
"scheduler_config": _build_scheduler_config(
|
||||
deploy,
|
||||
engine.scheduler,
|
||||
topology.execution_type,
|
||||
),
|
||||
"connector_config": _build_connector_config(
|
||||
deploy,
|
||||
stage_deploy,
|
||||
engine.connector,
|
||||
),
|
||||
"runtime_config": _build_runtime_config(
|
||||
deploy,
|
||||
stage_deploy,
|
||||
engine.runtime,
|
||||
parallel_config,
|
||||
),
|
||||
"load_config": _build_load_config(engine.load),
|
||||
"cache_config": _build_cache_config(deploy, engine.cache),
|
||||
"scheduler_config": _build_scheduler_config(deploy, engine.scheduler),
|
||||
"connector_config": _build_connector_config(stage_deploy),
|
||||
"runtime_config": _build_runtime_config(stage_deploy, engine.runtime, parallel_config),
|
||||
"parallel_config": parallel_config,
|
||||
"quantization_config": _copy_value(quantization_config),
|
||||
},
|
||||
@@ -1011,10 +1205,12 @@ def _build_ar_stage_config(
|
||||
model: str | None,
|
||||
) -> VllmOmniARStageConfig:
|
||||
common_kwargs, input_proc, next_stage_proc = _build_common_stage_config_kwargs(
|
||||
pipeline,
|
||||
deploy,
|
||||
topology,
|
||||
stage_deploy,
|
||||
engine,
|
||||
model=model,
|
||||
)
|
||||
return cast(
|
||||
VllmOmniARStageConfig,
|
||||
@@ -1036,10 +1232,12 @@ def _build_generation_stage_config(
|
||||
model: str | None,
|
||||
) -> VllmOmniGenerationStageConfig:
|
||||
common_kwargs, input_proc, next_stage_proc = _build_common_stage_config_kwargs(
|
||||
pipeline,
|
||||
deploy,
|
||||
topology,
|
||||
stage_deploy,
|
||||
engine,
|
||||
model=model,
|
||||
)
|
||||
return cast(
|
||||
VllmOmniGenerationStageConfig,
|
||||
@@ -1061,18 +1259,20 @@ def _build_diffusion_stage_config(
|
||||
model: str | None,
|
||||
) -> VllmOmniDiffusionStageConfig:
|
||||
common_kwargs, input_proc, next_stage_proc = _build_common_stage_config_kwargs(
|
||||
pipeline,
|
||||
deploy,
|
||||
topology,
|
||||
stage_deploy,
|
||||
engine,
|
||||
OmniStageDiffusionParallelConfig,
|
||||
model=model,
|
||||
)
|
||||
common_kwargs["diffusion_config"] = _build_diffusion_config_projection(
|
||||
pipeline,
|
||||
deploy,
|
||||
topology,
|
||||
engine.diffusion,
|
||||
model=model,
|
||||
model=common_kwargs["model_config"].model,
|
||||
quantization_config=common_kwargs["quantization_config"],
|
||||
)
|
||||
return cast(
|
||||
@@ -1127,14 +1327,28 @@ def _build_quantization_config(
|
||||
|
||||
|
||||
def _build_model_config(
|
||||
pipeline: PipelineConfig,
|
||||
deploy: DeployConfig,
|
||||
topology: StagePipelineConfig,
|
||||
stage_deploy: StageDeployConfig | None,
|
||||
engine: _ModelEngineOverrides,
|
||||
*,
|
||||
duplex_max_sessions: int,
|
||||
model: str | None,
|
||||
) -> OmniStageModelConfig:
|
||||
default_sampling_params = _stage_sampling_params(stage_deploy, topology)
|
||||
kwargs = _config_kwargs(engine)
|
||||
kwargs["model"] = _first_defined(kwargs.get("model"), model)
|
||||
if "model_arch" not in kwargs:
|
||||
kwargs["model_arch"] = topology.model_arch or pipeline.model_arch or None
|
||||
if "trust_remote_code" not in kwargs and deploy.trust_remote_code is not None:
|
||||
kwargs["trust_remote_code"] = _copy_value(deploy.trust_remote_code)
|
||||
if "dtype" not in kwargs and deploy.dtype is not None:
|
||||
kwargs["dtype"] = _copy_value(deploy.dtype)
|
||||
if "active_stream_window" not in kwargs:
|
||||
kwargs["active_stream_window"] = _copy_value(deploy.active_stream_window)
|
||||
if "custom_voice_dir" not in kwargs and deploy.custom_voice_dir is not None:
|
||||
kwargs["custom_voice_dir"] = _copy_value(deploy.custom_voice_dir)
|
||||
if "has_sampling_extra_args" not in kwargs:
|
||||
kwargs["has_sampling_extra_args"] = bool((default_sampling_params or {}).get("extra_args"))
|
||||
if "model_subdir" not in kwargs and topology.model_subdir is not None:
|
||||
@@ -1148,45 +1362,67 @@ def _build_model_config(
|
||||
)
|
||||
|
||||
|
||||
def _build_load_config(engine: _LoadEngineOverrides) -> OmniStageLoadConfig:
|
||||
return OmniStageLoadConfig(**_config_kwargs(engine))
|
||||
def _build_load_config(
|
||||
topology: StagePipelineConfig,
|
||||
engine: _LoadEngineOverrides,
|
||||
) -> OmniStageLoadConfig:
|
||||
kwargs = _config_kwargs(engine)
|
||||
if "skip_mm_profiling" not in kwargs and not topology.requires_multimodal_data:
|
||||
kwargs["skip_mm_profiling"] = True
|
||||
return OmniStageLoadConfig(**kwargs)
|
||||
|
||||
|
||||
def _build_cache_config(
|
||||
deploy: DeployConfig,
|
||||
engine: _CacheEngineOverrides,
|
||||
execution_type: StageExecutionType,
|
||||
) -> OmniStageCacheConfig:
|
||||
kwargs = _config_kwargs(engine)
|
||||
if "enable_prefix_caching" not in kwargs and deploy.enable_prefix_caching is not None:
|
||||
kwargs["enable_prefix_caching"] = _copy_value(deploy.enable_prefix_caching)
|
||||
if "disable_hybrid_kv_cache_manager" not in kwargs and execution_type == StageExecutionType.LLM_GENERATION:
|
||||
# Match the generation-stage override applied by legacy finalization.
|
||||
kwargs["disable_hybrid_kv_cache_manager"] = True
|
||||
return OmniStageCacheConfig(**kwargs)
|
||||
|
||||
|
||||
def _build_scheduler_config(
|
||||
deploy: DeployConfig,
|
||||
engine: _SchedulerEngineOverrides,
|
||||
execution_type: StageExecutionType,
|
||||
) -> OmniStageSchedulerConfig:
|
||||
kwargs = _config_kwargs(engine)
|
||||
if "enable_chunked_prefill" not in kwargs and deploy.enable_chunked_prefill is not None:
|
||||
kwargs["enable_chunked_prefill"] = _copy_value(deploy.enable_chunked_prefill)
|
||||
if "async_scheduling" not in kwargs and execution_type == StageExecutionType.LLM_AR:
|
||||
kwargs["async_scheduling"] = True
|
||||
return OmniStageSchedulerConfig(**kwargs)
|
||||
|
||||
|
||||
def _build_connector_config(stage_deploy: StageDeployConfig | None) -> OmniStageConnectorConfig:
|
||||
def _build_connector_config(
|
||||
deploy: DeployConfig,
|
||||
stage_deploy: StageDeployConfig | None,
|
||||
engine: _ConnectorEngineOverrides,
|
||||
) -> OmniStageConnectorConfig:
|
||||
output_connectors = stage_deploy.output_connectors if stage_deploy is not None else None
|
||||
input_connectors = stage_deploy.input_connectors if stage_deploy is not None else None
|
||||
return OmniStageConnectorConfig(
|
||||
async_chunk=bool(deploy.async_chunk),
|
||||
omni_kv_config=_copy_value(engine.get("omni_kv_config")),
|
||||
output_connectors=_copy_value(output_connectors) if output_connectors else None,
|
||||
input_connectors=_copy_value(input_connectors) if input_connectors else None,
|
||||
)
|
||||
|
||||
|
||||
def _build_runtime_config(
|
||||
deploy: DeployConfig,
|
||||
stage_deploy: StageDeployConfig | None,
|
||||
engine: _RuntimeEngineOverrides,
|
||||
parallel_config: OmniStageParallelConfig,
|
||||
) -> OmniStageRuntimeConfig:
|
||||
kwargs = _config_kwargs(engine)
|
||||
if "distributed_executor_backend" not in kwargs and deploy.distributed_executor_backend is not None:
|
||||
kwargs["distributed_executor_backend"] = _copy_value(deploy.distributed_executor_backend)
|
||||
if "devices" not in kwargs and stage_deploy is not None and stage_deploy.devices is not None:
|
||||
kwargs["devices"] = _copy_value(stage_deploy.devices)
|
||||
if "num_replicas" not in kwargs and stage_deploy is not None:
|
||||
@@ -1236,13 +1472,15 @@ def _build_diffusion_config_projection(
|
||||
topology.model_arch,
|
||||
pipeline.model_arch,
|
||||
)
|
||||
if "model_class_name" not in diffusion_kwargs and topology.model_arch is not None:
|
||||
diffusion_kwargs["model_class_name"] = _copy_value(topology.model_arch)
|
||||
if "dtype" not in diffusion_kwargs and deploy.dtype is not None:
|
||||
diffusion_kwargs["dtype"] = _copy_value(deploy.dtype)
|
||||
if "trust_remote_code" not in diffusion_kwargs and deploy.trust_remote_code is not None:
|
||||
diffusion_kwargs["trust_remote_code"] = _copy_value(deploy.trust_remote_code)
|
||||
if "distributed_executor_backend" not in diffusion_kwargs and deploy.distributed_executor_backend is not None:
|
||||
diffusion_kwargs["distributed_executor_backend"] = _copy_value(deploy.distributed_executor_backend)
|
||||
if model is not None:
|
||||
if "model" not in diffusion_kwargs and model is not None:
|
||||
diffusion_kwargs["model"] = model
|
||||
if quantization_config is not None:
|
||||
diffusion_kwargs["quantization_config"] = _copy_value(quantization_config)
|
||||
@@ -1304,6 +1542,7 @@ class VllmOmniConfig:
|
||||
deploy_by_id.get(topology.stage_id),
|
||||
_stage_engine_values(
|
||||
deploy_by_id.get(topology.stage_id),
|
||||
topology,
|
||||
_stage_cli_overrides(topology.stage_id, cli_overrides),
|
||||
),
|
||||
model=model,
|
||||
|
||||
@@ -8,6 +8,7 @@ out of StageEngineCoreClient into reusable functions.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import fcntl
|
||||
import importlib
|
||||
import multiprocessing as mp
|
||||
@@ -15,7 +16,7 @@ import os
|
||||
import time
|
||||
from collections.abc import Callable, Generator, Sequence
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass, replace
|
||||
from dataclasses import dataclass, fields, replace
|
||||
from typing import Any, Literal, cast
|
||||
|
||||
from vllm.logger import init_logger
|
||||
@@ -25,7 +26,10 @@ from vllm.usage.usage_lib import UsageContext
|
||||
from vllm.v1.engine.input_processor import InputProcessor
|
||||
from vllm.v1.executor import Executor
|
||||
|
||||
from vllm_omni.config.omni_config import BaseVllmOmniStageConfig
|
||||
from vllm_omni.config.omni_config import (
|
||||
BaseVllmOmniStageConfig,
|
||||
VllmOmniDiffusionStageConfig,
|
||||
)
|
||||
from vllm_omni.config.stage_config import StageType
|
||||
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
||||
from vllm_omni.engine.arg_utils import OmniEngineArgs
|
||||
@@ -348,6 +352,30 @@ class StageMetadata:
|
||||
replica_id: int = 0
|
||||
|
||||
|
||||
def _apply_rocm_attention_backend(
|
||||
engine_args: dict[str, Any],
|
||||
stage_type: str | StageType,
|
||||
) -> None:
|
||||
"""Preserve Omni's ROCm attention-backend compatibility default."""
|
||||
if (
|
||||
not current_omni_platform.is_rocm()
|
||||
or stage_type == StageType.DIFFUSION
|
||||
or engine_args.get("attention_backend") is not None
|
||||
):
|
||||
return
|
||||
|
||||
from vllm._aiter_ops import rocm_aiter_ops
|
||||
|
||||
if rocm_aiter_ops.is_enabled():
|
||||
engine_args["attention_backend"] = "ROCM_AITER_FA"
|
||||
# Before vLLM v0.19.0, the default attention backend is TRITON_ATTN for ROCm.
|
||||
# Since vLLM v0.19.0, the default attention backend is ROCM_ATTN for ROCm.
|
||||
# However, the compatibility of ROCM_ATTN with Omni is not guaranteed.
|
||||
# Therefore, we still use TRITON_ATTN as the default attention backend,
|
||||
# when the selected_backend is not specified.
|
||||
engine_args["attention_backend"] = "TRITON_ATTN"
|
||||
|
||||
|
||||
def extract_legacy_stage_metadata(stage_config: Any) -> StageMetadata:
|
||||
"""Extract metadata through the active production legacy path.
|
||||
|
||||
@@ -358,18 +386,7 @@ def extract_legacy_stage_metadata(stage_config: Any) -> StageMetadata:
|
||||
stage_type: Literal["llm", "diffusion"] = _get_attr_or_item(stage_config, "stage_type", "llm")
|
||||
engine_args = stage_config.engine_args
|
||||
|
||||
if current_omni_platform.is_rocm():
|
||||
if stage_type != "diffusion" and engine_args.get("attention_backend") is None:
|
||||
from vllm._aiter_ops import rocm_aiter_ops
|
||||
|
||||
if rocm_aiter_ops.is_enabled():
|
||||
engine_args["attention_backend"] = "ROCM_AITER_FA"
|
||||
# Before vLLM v0.19.0, the default attention backend is TRITON_ATTN for ROCm.
|
||||
# Since vLLM v0.19.0, the default attention backend is ROCM_ATTN for ROCm.
|
||||
# However, the compatibility of ROCM_ATTN with Omni is not guaranteed.
|
||||
# Therefore, we still use TRITON_ATTN as the default attention backend,
|
||||
# when the selected_backend is not specified.
|
||||
engine_args["attention_backend"] = "TRITON_ATTN"
|
||||
_apply_rocm_attention_backend(engine_args, stage_type)
|
||||
|
||||
runtime_cfg = stage_config.runtime
|
||||
engine_input_source: list[int] = _get_attr_or_item(stage_config, "engine_input_source", [])
|
||||
@@ -748,25 +765,110 @@ def stage_runtime_env(stage_id: int, runtime_cfg: Any) -> Generator[None, None,
|
||||
os.environ[key] = old_value
|
||||
|
||||
|
||||
def build_engine_args_dict(
|
||||
stage_config: Any,
|
||||
model: str,
|
||||
stage_connector_spec: dict[str, Any] | None = None,
|
||||
cli_tokenizer: str | None = None,
|
||||
def _project_omni_config_fields(
|
||||
config: Any,
|
||||
*,
|
||||
exclude: frozenset[str] = frozenset(),
|
||||
) -> dict[str, Any]:
|
||||
"""Build the normalized engine args dict for one stage."""
|
||||
engine_args = stage_config.engine_args
|
||||
# HACK (Alex) Tensor parallel size should not be passed as None;
|
||||
# remove it if this is the case so that we fall back to default
|
||||
# creation from vLLM's engine args.
|
||||
# NOTE: This will be fixed more generically in ongoing work for engine arg filtering.
|
||||
if "tensor_parallel_size" in engine_args and engine_args["tensor_parallel_size"] is None:
|
||||
del engine_args["tensor_parallel_size"]
|
||||
"""Copy defined typed config fields into backend adapter kwargs."""
|
||||
projected: dict[str, Any] = {}
|
||||
for config_field in fields(config):
|
||||
name = config_field.name
|
||||
if name in exclude:
|
||||
continue
|
||||
value = getattr(config, name)
|
||||
if value is not None:
|
||||
projected[name] = copy.deepcopy(value)
|
||||
return projected
|
||||
|
||||
stage_type = _get_attr_or_item(stage_config, "stage_type", "llm")
|
||||
stage_id = stage_config.stage_id
|
||||
|
||||
engine_args_dict = _to_dict(engine_args)
|
||||
def _project_omni_stage_engine_args(
|
||||
stage_config: BaseVllmOmniStageConfig,
|
||||
) -> dict[str, Any]:
|
||||
"""Read backend inputs from one structured stage config."""
|
||||
engine_args: dict[str, Any] = {}
|
||||
is_diffusion = isinstance(stage_config, VllmOmniDiffusionStageConfig)
|
||||
|
||||
if is_diffusion:
|
||||
engine_args.update(_project_omni_config_fields(stage_config.diffusion_config))
|
||||
|
||||
for config, excluded_fields in (
|
||||
(
|
||||
stage_config.model_config,
|
||||
frozenset({"default_sampling_params", "has_sampling_extra_args"}),
|
||||
),
|
||||
(stage_config.load_config, frozenset()),
|
||||
(stage_config.cache_config, frozenset()),
|
||||
(stage_config.scheduler_config, frozenset()),
|
||||
(
|
||||
stage_config.runtime_config,
|
||||
frozenset({"devices", "num_replicas", "env", "num_gpus"}),
|
||||
),
|
||||
):
|
||||
engine_args.update(
|
||||
_project_omni_config_fields(
|
||||
config,
|
||||
exclude=excluded_fields,
|
||||
)
|
||||
)
|
||||
|
||||
# The legacy builder always emits this key, including for pipelines such
|
||||
# as Audex that intentionally defer architecture discovery to HF config.
|
||||
engine_args["model_arch"] = copy.deepcopy(stage_config.model_config.model_arch)
|
||||
|
||||
topology = stage_config.stage_pipeline_config
|
||||
topology_engine_args = {
|
||||
"model_stage": stage_config.model_stage,
|
||||
"worker_type": stage_config.worker_type,
|
||||
"scheduler_cls": stage_config.scheduler_cls,
|
||||
"hf_config_name": stage_config.hf_config_name,
|
||||
"engine_output_type": stage_config.engine_output_type,
|
||||
"custom_process_next_stage_input_func": stage_config.custom_process_next_stage_input_func,
|
||||
"retains_state_across_chunks": topology.retains_state_across_chunks,
|
||||
}
|
||||
engine_args.update(
|
||||
{name: copy.deepcopy(value) for name, value in topology_engine_args.items() if value is not None}
|
||||
)
|
||||
|
||||
connector_config = stage_config.connector_config
|
||||
engine_args["async_chunk"] = connector_config.async_chunk
|
||||
if connector_config.omni_kv_config is not None:
|
||||
engine_args["omni_kv_config"] = copy.deepcopy(connector_config.omni_kv_config)
|
||||
|
||||
if is_diffusion:
|
||||
engine_args["parallel_config"] = _project_omni_config_fields(
|
||||
stage_config.parallel_config,
|
||||
exclude=frozenset({"world_size"}),
|
||||
)
|
||||
else:
|
||||
engine_args.update(
|
||||
_project_omni_config_fields(
|
||||
stage_config.parallel_config,
|
||||
exclude=frozenset({"world_size"}),
|
||||
)
|
||||
)
|
||||
|
||||
quantization_config = stage_config.quantization_config
|
||||
if quantization_config is not None:
|
||||
quantization_key = (
|
||||
"quantization" if isinstance(quantization_config, str) and not is_diffusion else "quantization_config"
|
||||
)
|
||||
engine_args[quantization_key] = copy.deepcopy(quantization_config)
|
||||
|
||||
return engine_args
|
||||
|
||||
|
||||
def _finalize_engine_args_dict(
|
||||
engine_args_dict: dict[str, Any],
|
||||
*,
|
||||
stage_type: str | StageType,
|
||||
stage_id: int,
|
||||
model: str,
|
||||
stage_connector_spec: dict[str, Any] | None,
|
||||
cli_tokenizer: str | None,
|
||||
has_sampling_extra_args: bool,
|
||||
) -> dict[str, Any]:
|
||||
"""Apply representation-independent engine adapter behavior."""
|
||||
pipeline_model_root = model
|
||||
model = engine_args_dict.pop("model", None) or model
|
||||
stage_defines_tokenizer = (
|
||||
@@ -826,15 +928,15 @@ def build_engine_args_dict(
|
||||
if stage_connector_spec:
|
||||
engine_args_dict["stage_connector_spec"] = dict(stage_connector_spec or {})
|
||||
|
||||
if stage_type == "diffusion":
|
||||
is_diffusion = stage_type == StageType.DIFFUSION
|
||||
if is_diffusion:
|
||||
from vllm_omni.diffusion.data import parse_attention_config
|
||||
|
||||
if engine_args_dict.get("diffusion_attention_config") is not None:
|
||||
engine_args_dict["diffusion_attention_config"] = parse_attention_config(
|
||||
engine_args_dict.get("diffusion_attention_config"),
|
||||
engine_args_dict["diffusion_attention_config"],
|
||||
)
|
||||
|
||||
if stage_type != "diffusion":
|
||||
else:
|
||||
resolve_worker_cls(engine_args_dict)
|
||||
|
||||
if engine_args_dict.get("worker_type") == "generation":
|
||||
@@ -844,17 +946,86 @@ def build_engine_args_dict(
|
||||
engine_args_dict.setdefault("disable_hybrid_kv_cache_manager", True)
|
||||
engine_args_dict.setdefault("enable_prefix_caching", False)
|
||||
|
||||
# Check whether the stage's default_sampling_params defines extra_args.
|
||||
default_sp = _to_dict(_get_attr_or_item(stage_config, "default_sampling_params", {}))
|
||||
engine_args_dict["has_sampling_extra_args"] = bool(default_sp.get("extra_args"))
|
||||
engine_args_dict["has_sampling_extra_args"] = has_sampling_extra_args
|
||||
|
||||
# TODO: Remove this after the performance regression is fixed
|
||||
# Set VLLM_USE_FLASHINFER_MOE_FP16=0 for Qwen3-Omni to avoid performance regression
|
||||
_maybe_set_qwen3_omni_moe_env(engine_args_dict)
|
||||
|
||||
return engine_args_dict
|
||||
|
||||
|
||||
def build_legacy_engine_args_dict(
|
||||
stage_config: Any,
|
||||
model: str,
|
||||
stage_connector_spec: dict[str, Any] | None = None,
|
||||
cli_tokenizer: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Implement engine-argument building for the legacy stage representation."""
|
||||
engine_args_dict = _to_dict(stage_config.engine_args)
|
||||
# Legacy configs can materialize an omitted optional TP size as None.
|
||||
# Remove it from the detached adapter dict so the backend default applies
|
||||
# without mutating stage_config.engine_args.
|
||||
if engine_args_dict.get("tensor_parallel_size") is None:
|
||||
engine_args_dict.pop("tensor_parallel_size", None)
|
||||
|
||||
default_sp = _to_dict(_get_attr_or_item(stage_config, "default_sampling_params", {}))
|
||||
return _finalize_engine_args_dict(
|
||||
engine_args_dict,
|
||||
stage_type=_get_attr_or_item(stage_config, "stage_type", "llm"),
|
||||
stage_id=stage_config.stage_id,
|
||||
model=model,
|
||||
stage_connector_spec=stage_connector_spec,
|
||||
cli_tokenizer=cli_tokenizer,
|
||||
has_sampling_extra_args=bool(default_sp.get("extra_args")),
|
||||
)
|
||||
|
||||
|
||||
def build_engine_args_dict(
|
||||
stage_config: Any,
|
||||
model: str,
|
||||
stage_connector_spec: dict[str, Any] | None = None,
|
||||
cli_tokenizer: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build engine arguments through the stable production entry point.
|
||||
|
||||
Production inputs still use the legacy stage representation. Keep that
|
||||
compatibility choice behind this function so callers do not bind directly
|
||||
to a representation-specific implementation.
|
||||
"""
|
||||
return build_legacy_engine_args_dict(
|
||||
stage_config,
|
||||
model,
|
||||
stage_connector_spec=stage_connector_spec,
|
||||
cli_tokenizer=cli_tokenizer,
|
||||
)
|
||||
|
||||
|
||||
def build_engine_args_dict_from_omni_stage_config(
|
||||
stage_config: BaseVllmOmniStageConfig,
|
||||
model: str,
|
||||
stage_connector_spec: dict[str, Any] | None = None,
|
||||
cli_tokenizer: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Project one typed stage config into backend engine arguments.
|
||||
|
||||
This projection is prepared for the RFC #4021 stage-init cutover. Current
|
||||
production startup reaches the legacy implementation through
|
||||
``build_engine_args_dict`` while strategy and startup-plan inputs still
|
||||
use the legacy representation.
|
||||
"""
|
||||
engine_args_dict = _project_omni_stage_engine_args(stage_config)
|
||||
_apply_rocm_attention_backend(engine_args_dict, stage_config.stage_type)
|
||||
return _finalize_engine_args_dict(
|
||||
engine_args_dict,
|
||||
stage_type=stage_config.stage_type,
|
||||
stage_id=stage_config.stage_id,
|
||||
model=model,
|
||||
stage_connector_spec=stage_connector_spec,
|
||||
cli_tokenizer=cli_tokenizer,
|
||||
has_sampling_extra_args=stage_config.model_config.has_sampling_extra_args,
|
||||
)
|
||||
|
||||
|
||||
def build_vllm_config(
|
||||
stage_config: Any,
|
||||
model: str,
|
||||
|
||||
@@ -370,6 +370,9 @@ class StageRuntime:
|
||||
executor_class = None
|
||||
engine_args_dict = None
|
||||
if base_metadata.stage_type != "diffusion":
|
||||
# The stable adapter entry point still receives the same
|
||||
# legacy stage object as replica planning. Its implementation
|
||||
# switches only at the coordinated RFC #4021 cutover.
|
||||
engine_args_dict = build_engine_args_dict(
|
||||
stage_cfg,
|
||||
self._model,
|
||||
|
||||
@@ -935,6 +935,9 @@ def run_headless(args: TrackingNamespace) -> None:
|
||||
# CUDA_VISIBLE_DEVICES; when ``--omni-dp-size-local > 1`` we additionally
|
||||
# bracket each replica's spawn below with setup_stage_devices so they
|
||||
# don't all stack on cuda:0 (see ``per_replica_devices`` above).
|
||||
# Headless startup still supplies the legacy OmegaConf stage shape through
|
||||
# the stable adapter entry point. The implementation switches only when
|
||||
# RFC #4021 threads structured stage configs through the launch plan.
|
||||
engine_args_dict = build_engine_args_dict(
|
||||
stage_cfg,
|
||||
model,
|
||||
|
||||
Reference in new issue
Block a user