Files
vllm-omni/vllm_omni/diffusion/vllm_config.py
T

181 lines
7.8 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Build native vLLM configuration views for the Omni diffusion runtime."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
import torch
from vllm.config import CompilationConfig, DeviceConfig, VllmConfig
from vllm.transformers_utils.config import get_hf_text_config
from vllm_omni.diffusion.data import OmniDiffusionConfig
from vllm_omni.diffusion.diffusion_kv.config import DiffusionKVCacheMode
def resolve_diffusion_max_model_len(od_config: OmniDiffusionConfig) -> int:
"""Resolve the native KV admission bound for a diffusion stage."""
configured = getattr(od_config, "max_model_len", None)
if configured is not None:
configured = int(configured)
if configured == -1:
configured = None
elif configured <= 0:
raise ValueError(f"max_model_len must be positive for Scheduler-managed Diffusion KV, got {configured}")
else:
return configured
# ``max_model_len`` keeps the native vLLM meaning: the per-sequence KV
# admission ceiling consumed by ``KVCacheManager``. HunyuanImage3 exposes
# that ceiling directly as ``max_position_embeddings`` because its text,
# reference-image, and generated-image tokens share one Transformer
# sequence. Other diffusion models commonly express their serving limits
# through resolution, duration, or frame-count constraints instead. When
# paged KV support expands to those models, the model integration must
# derive ``max_model_len`` from its serving limits, while model-owned
# preprocessing validates each request and derives its matching
# ``seq_len``. Text-encoder length fields must not be mistaken for the DiT
# KV limit.
hf_config = getattr(od_config, "tf_model_config", None)
for field_name in ("max_position_embeddings", "max_sequence_length", "seq_length"):
value = getattr(hf_config, field_name, None)
if value is None and isinstance(hf_config, dict):
value = hf_config.get(field_name)
if value is not None and int(value) > 0:
return int(value)
mode = getattr(getattr(od_config, "diffusion_kv_mode", None), "value", None)
if mode == "paged_scheduler":
raise ValueError(
"paged_scheduler Diffusion KV requires max_model_len or a model config "
"with max_position_embeddings/max_sequence_length/seq_length"
)
# Dense execution does not consume the native KV admission bound, but the
# shared Worker VllmConfig still requires a positive placeholder.
return 1
@dataclass
class _DiffusionVllmModelConfig:
"""Private adapter for the native ``VllmConfig.model_config`` surface."""
model: str | None
dtype: torch.dtype
max_model_len: int
original_max_model_len: int | None
runner_type: str = "generate"
quantization: str | None = None
quantization_config: Any | None = None
hf_config: Any | None = None
hf_text_config: Any | None = None
multimodal_config: Any | None = None
enforce_eager: bool = False
disable_cascade_attn: bool = False
enable_return_routed_experts: bool = False
enable_prompt_embeds: bool = False
logits_processors: tuple[str, ...] = ()
use_mla: bool = False
is_moe: bool = False
@property
def is_quantized(self) -> bool:
return self.quantization is not None
def is_nvfp4_quantized(self) -> bool:
return self.quantization == "modelopt_fp4"
@property
def is_diffusion(self) -> bool:
# This config is also installed as the current VllmConfig for the
# existing Omni diffusion runtime, which does not use vLLM's native
# diffusion runner. Preserve that established backend-selection flag.
return False
def _make_diffusion_vllm_model_config(od_config: OmniDiffusionConfig) -> _DiffusionVllmModelConfig:
quant_config = getattr(od_config, "quantization_config", None)
quantization = quant_config.get_name() if quant_config is not None and hasattr(quant_config, "get_name") else None
hf_config = getattr(od_config, "tf_model_config", None)
hf_text_config = get_hf_text_config(hf_config) if hasattr(hf_config, "get_text_config") else hf_config
configured_max_model_len = getattr(od_config, "max_model_len", None)
max_model_len = resolve_diffusion_max_model_len(od_config)
paged_scheduler = (
getattr(od_config, "diffusion_kv_mode", DiffusionKVCacheMode.DENSE_LEGACY)
is DiffusionKVCacheMode.PAGED_SCHEDULER
)
return _DiffusionVllmModelConfig(
model=od_config.model,
dtype=od_config.dtype,
max_model_len=max_model_len,
# Preserve the user's -1 sentinel only for the native paged-cache
# sizing flow. Dense keeps its established resolved model-config view.
original_max_model_len=configured_max_model_len if paged_scheduler else max_model_len,
quantization=quantization,
quantization_config=quant_config,
hf_config=hf_config,
hf_text_config=hf_text_config,
enforce_eager=getattr(od_config, "enforce_eager", False),
is_moe=bool(getattr(od_config, "is_moe", False)),
)
def create_base_diffusion_vllm_config(
device: torch.device,
od_config: OmniDiffusionConfig,
) -> VllmConfig:
"""Create the native vLLM 0.27 config used by diffusion."""
return VllmConfig(
compilation_config=CompilationConfig(),
device_config=DeviceConfig(device=device),
additional_config=od_config.additional_config,
)
def configure_diffusion_vllm_config(vllm_config: VllmConfig, od_config: OmniDiffusionConfig) -> VllmConfig:
"""Populate native model, parallel, cache, and Scheduler configuration."""
parallel_config = od_config.parallel_config
vllm_config.parallel_config.tensor_parallel_size = parallel_config.tensor_parallel_size
vllm_config.parallel_config.data_parallel_size = parallel_config.data_parallel_size
if parallel_config.enable_expert_parallel and od_config.is_moe:
vllm_config.parallel_config.data_parallel_size = (
parallel_config.data_parallel_size * parallel_config.cfg_parallel_size
)
vllm_config.parallel_config.prefill_context_parallel_size = parallel_config.sequence_parallel_size
vllm_config.parallel_config.enable_expert_parallel = parallel_config.enable_expert_parallel
vllm_config.model_config = _make_diffusion_vllm_model_config(od_config) # type: ignore[assignment]
vllm_config.quant_config = od_config.quantization_config
vllm_config.profiler_config = od_config.profiler_config
if (
getattr(od_config, "diffusion_kv_mode", DiffusionKVCacheMode.DENSE_LEGACY)
is DiffusionKVCacheMode.PAGED_SCHEDULER
):
vllm_config.parallel_config.pipeline_parallel_size = parallel_config.pipeline_parallel_size
vllm_config.cache_config.gpu_memory_utilization = float(getattr(od_config, "gpu_memory_utilization", 0.9))
vllm_config.cache_config.kv_cache_memory_bytes = getattr(od_config, "kv_cache_memory_bytes", None)
# Prefix hashes/publication are intentionally deferred to the next PR.
vllm_config.cache_config.enable_prefix_caching = False
max_num_batched_tokens = getattr(od_config, "max_num_batched_tokens", None)
if max_num_batched_tokens is not None:
vllm_config.scheduler_config.max_num_batched_tokens = int(max_num_batched_tokens)
return vllm_config
def create_diffusion_vllm_config(
device: torch.device,
od_config: OmniDiffusionConfig,
) -> VllmConfig:
"""Build the same fully populated native config in Engine and Workers."""
vllm_config = create_base_diffusion_vllm_config(
device,
od_config,
)
return configure_diffusion_vllm_config(vllm_config, od_config)