Signed-off-by: zwhzzz0821 <zwhzzz0821@users.noreply.github.com> Co-authored-by: zwhzzz0821 <zwhzzz0821@users.noreply.github.com>
181 lines
7.8 KiB
Python
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)
|