Files
vllm-omni/vllm_omni/diffusion/models/sana_wm/config.py
T
BruceLoveDecimaland汪志鹏 adf6d0c1ea [Model] Add SANA-WM support(Stage-1 only) (#4061)
Signed-off-by: BruceLoveDecimal <liuqihao970610@gmail.com>
Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com>
Signed-off-by: liuqihao <liuqihao970610@gmail.com>
Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com>
2026-08-18 23:57:55 -06:00

94 lines
3.3 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Config for the SANA-WM Stage-1 transformer.
Loaded from the standard diffusers ``transformer/config.json`` (a flat dict
keyed by the dataclass field names). The bespoke ``config.yaml`` release format
is handled once, offline, at checkpoint-conversion time — the runtime only ever
sees the converted, diffusers-standard config.
"""
from __future__ import annotations
import json
from collections.abc import Mapping
from dataclasses import dataclass, field, fields
from pathlib import Path
from typing import Any
def _as_tuple3(value: Any, default: tuple[int, int, int]) -> tuple[int, int, int]:
if value is None:
return default
if isinstance(value, int):
return (value, value, value)
if isinstance(value, (list, tuple)):
if len(value) == 2:
return (1, int(value[0]), int(value[1]))
if len(value) == 3:
return (int(value[0]), int(value[1]), int(value[2]))
return default
# LTX-2 VAE compression ratios (SANA-WM ships ``AutoencoderKLLTX2Video``).
# They live here rather than in the pipeline so the request-normalisation module
# can validate latent geometry without importing the pipeline.
SANA_WM_VAE_SPATIAL_COMPRESSION = 32
SANA_WM_VAE_TEMPORAL_COMPRESSION = 8
@dataclass(frozen=True)
class SanaWmConfig:
"""SANA-WM Stage-1 architecture and runtime config.
Defaults mirror the first public HF release. ``from_json`` (reading
``transformer/config.json``) is the normal construction path.
"""
architecture_name: str | None = "SanaMSVideoCamCtrl_1600M_P1_D20"
num_blocks: int = 20
hidden_size: int = 2240
mlp_ratio: float = 3.0
attn_type: str = "BidirectionalGDNTriton"
softmax_every_n: int = 4
linear_head_dim: int = 112
conv_kernel_size: int = 4
t_kernel_size: int = 3
k_conv_only: bool = True
ffn_type: str = "GLUMBConvTemp"
pos_embed_type: str = "wan_rope"
patch_size: tuple[int, int, int] = (1, 1, 1)
qk_norm: bool = True
cross_norm: bool = True
mixed_precision: str = "bf16"
fp32_attention: bool = True
image_size: int = 720
cam_attn_compress: int = 1
use_chunk_plucker_post_attn: bool = True
chunk_plucker_channels: int = 48
chunk_plucker_post_attn_blocks: int = 20
inference_flow_shift: float = 9.8
scheduler_type: str = "flow_dpm-solver"
chi_prompt: list[str] = field(default_factory=list)
model_max_length: int = 300
@classmethod
def from_dict(cls, data: Mapping[str, Any]) -> SanaWmConfig:
"""Build from a flat mapping of field name -> value.
Unknown keys (e.g. the diffusers ``_class_name`` marker or the
``latent_channels`` / ``prompt_channels`` constructor kwargs) are
ignored. ``patch_size`` is coerced from a JSON list back to a tuple.
"""
field_names = {f.name for f in fields(cls)}
known = {k: v for k, v in data.items() if k in field_names}
if "patch_size" in known:
known["patch_size"] = _as_tuple3(known["patch_size"], cls.patch_size)
return cls(**known)
@classmethod
def from_json(cls, path: str | Path) -> SanaWmConfig:
with open(path, encoding="utf-8") as f:
data = json.load(f)
return cls.from_dict(data if isinstance(data, Mapping) else {})