94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
229 lines
8.0 KiB
Python
229 lines
8.0 KiB
Python
### Based on https://github.com/huggingface/diffusers/blob/main/scripts/convert_wan_to_diffusers.py
|
|
|
|
import argparse
|
|
import json
|
|
import pathlib
|
|
import shutil
|
|
from typing import Any, Dict, List
|
|
|
|
from safetensors.torch import load_file, save_file
|
|
|
|
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
TRANSFORMER_KEYS_RENAME_DICT = {
|
|
"time_embedding.0": "condition_embedder.time_embedder.linear_1",
|
|
"time_embedding.2": "condition_embedder.time_embedder.linear_2",
|
|
"text_embedding.0": "condition_embedder.text_embedder.linear_1",
|
|
"text_embedding.2": "condition_embedder.text_embedder.linear_2",
|
|
"time_projection.1": "condition_embedder.time_proj",
|
|
"head.modulation": "scale_shift_table",
|
|
"head.head": "proj_out",
|
|
"modulation": "scale_shift_table",
|
|
"ffn.0": "ffn.net.0.proj",
|
|
"ffn.2": "ffn.net.2",
|
|
# Hack to swap the layer names
|
|
# The original model calls the norms in following order: norm1, norm3, norm2
|
|
# We convert it to: norm1, norm2, norm3
|
|
"norm2": "norm__placeholder",
|
|
"norm3": "norm2",
|
|
"norm__placeholder": "norm3",
|
|
# For the I2V model
|
|
"img_emb.proj.0": "condition_embedder.image_embedder.norm1",
|
|
"img_emb.proj.1": "condition_embedder.image_embedder.ff.net.0.proj",
|
|
"img_emb.proj.3": "condition_embedder.image_embedder.ff.net.2",
|
|
"img_emb.proj.4": "condition_embedder.image_embedder.norm2",
|
|
# for the FLF2V model
|
|
"img_emb.emb_pos": "condition_embedder.image_embedder.pos_embed",
|
|
# Add attention component mappings
|
|
"self_attn.q": "attn1.to_q",
|
|
"self_attn.k": "attn1.to_k",
|
|
"self_attn.v": "attn1.to_v",
|
|
"self_attn.o": "attn1.to_out.0",
|
|
"self_attn.norm_q": "attn1.norm_q",
|
|
"self_attn.norm_k": "attn1.norm_k",
|
|
"cross_attn.q": "attn2.to_q",
|
|
"cross_attn.k": "attn2.to_k",
|
|
"cross_attn.v": "attn2.to_v",
|
|
"cross_attn.o": "attn2.to_out.0",
|
|
"cross_attn.norm_q": "attn2.norm_q",
|
|
"cross_attn.norm_k": "attn2.norm_k",
|
|
"attn2.to_k_img": "attn2.add_k_proj",
|
|
"attn2.to_v_img": "attn2.add_v_proj",
|
|
"attn2.norm_k_img": "attn2.norm_added_k",
|
|
# MXFP4 msmodelslim wraps Linear layers with a `.linear.` subpath;
|
|
# strip it so keys match the SGLang model parameters.
|
|
".linear.": ".",
|
|
# NonFusionSmoothQuantWrapper exports smooth quant scale as `.div.mul_scale`;
|
|
# strip `.div.` so it loads as a direct parameter `mul_scale` on the linear layer.
|
|
".div.": ".",
|
|
}
|
|
|
|
SUPPORTED_MODEL_TYPES = ["Wan2.2-T2V-A14B", "Wan2.2-I2V-A14B", "Wan2.2-TI2V-5B"]
|
|
|
|
# Cascade models have two transformers (high_noise + low_noise)
|
|
CASCADE_MODEL_TYPES = {"Wan2.2-T2V-A14B", "Wan2.2-I2V-A14B"}
|
|
|
|
|
|
def get_transformer_config(model_type: str) -> Dict[str, Any]:
|
|
if model_type in SUPPORTED_MODEL_TYPES:
|
|
return TRANSFORMER_KEYS_RENAME_DICT
|
|
else:
|
|
raise ValueError(
|
|
f"Unsupported model_type: {model_type}. Supported: {SUPPORTED_MODEL_TYPES}"
|
|
)
|
|
|
|
|
|
def get_transformer_dirs(model_type: str) -> List[str]:
|
|
"""Return the list of transformer directory names for a given model type."""
|
|
if model_type in CASCADE_MODEL_TYPES:
|
|
return ["transformer", "transformer_2"]
|
|
return ["transformer"]
|
|
|
|
|
|
def get_quant_subpath(
|
|
model_type: str, quant_path: pathlib.Path, transformer_dir: str
|
|
) -> pathlib.Path:
|
|
"""Return the quant weights subdirectory for a given transformer."""
|
|
if model_type in CASCADE_MODEL_TYPES:
|
|
sub = (
|
|
"high_noise_model"
|
|
if transformer_dir == "transformer"
|
|
else "low_noise_model"
|
|
)
|
|
return quant_path / sub
|
|
return quant_path
|
|
|
|
|
|
def update_dict_(d: Dict[str, Any], old_key: str, new_key: str) -> None:
|
|
d[new_key] = d.pop(old_key)
|
|
|
|
|
|
def load_sharded_safetensors(directory: pathlib.Path, pattern: str) -> dict:
|
|
candidates = sorted(directory.glob(pattern))
|
|
if not candidates:
|
|
raise FileNotFoundError(f"No file matching '{pattern}' found in {directory}")
|
|
|
|
state_dict = {}
|
|
for f in candidates:
|
|
state_dict.update(load_file(f))
|
|
return state_dict
|
|
|
|
|
|
def convert_transformer(
|
|
model_type: str, model_dir: pathlib.Path, output_dir: pathlib.Path
|
|
) -> None:
|
|
"""Convert a single quantized transformer directory into Diffusers format."""
|
|
model_path = pathlib.Path(model_dir)
|
|
out_path = pathlib.Path(output_dir)
|
|
out_path.mkdir(parents=True, exist_ok=True)
|
|
RENAME_DICT = get_transformer_config(model_type)
|
|
|
|
state_dict = load_sharded_safetensors(model_path, "quant_model_weight*.safetensors")
|
|
|
|
json_candidates = sorted(model_path.glob("quant_model_description*.json"))
|
|
if not json_candidates:
|
|
raise FileNotFoundError(
|
|
f"No quant_model_description*.json found in {model_path}"
|
|
)
|
|
with open(json_candidates[0]) as f:
|
|
quant_config = json.load(f)
|
|
|
|
for key in list(state_dict.keys()):
|
|
new_key = key[:]
|
|
for replace_key, rename_key in RENAME_DICT.items():
|
|
new_key = new_key.replace(replace_key, rename_key)
|
|
if new_key != key:
|
|
update_dict_(state_dict, key, new_key)
|
|
# The quant JSON only covers quantized layers, not all model keys
|
|
if key in quant_config:
|
|
update_dict_(quant_config, key, new_key)
|
|
|
|
save_file(state_dict, out_path / "diffusion_pytorch_model.safetensors")
|
|
|
|
with open(out_path / "quant_model_description.json", "w") as f:
|
|
json.dump(quant_config, f, indent=2)
|
|
|
|
|
|
def repack(
|
|
model_type: str,
|
|
original_model_path: pathlib.Path,
|
|
quant_path: pathlib.Path,
|
|
output_path: pathlib.Path,
|
|
) -> None:
|
|
"""
|
|
Full one-step repack workflow:
|
|
1. Copy the original HF Diffusers model to output_path, excluding transformer dir(s).
|
|
2. For each transformer: convert quant weights and copy config.json from original.
|
|
"""
|
|
transformer_dirs = get_transformer_dirs(model_type)
|
|
|
|
# Step 1: Copy original model, skipping transformer dirs (they will be replaced)
|
|
logger.debug(f"Step 1: Copying original model to {output_path}")
|
|
logger.debug(f" (skipping: {transformer_dirs})")
|
|
shutil.copytree(
|
|
str(original_model_path),
|
|
str(output_path),
|
|
ignore=shutil.ignore_patterns(*transformer_dirs),
|
|
)
|
|
|
|
# Step 2+: Convert each transformer
|
|
for i, tdir in enumerate(transformer_dirs):
|
|
q_path = get_quant_subpath(model_type, quant_path, tdir)
|
|
out_tdir = output_path / tdir
|
|
logger.debug(
|
|
f"\nStep {i + 2}: Converting {tdir} (quant source: {q_path.name})..."
|
|
)
|
|
convert_transformer(model_type, q_path, out_tdir)
|
|
|
|
# Copy config.json from the original transformer dir
|
|
src_config = original_model_path / tdir / "config.json"
|
|
if src_config.is_file():
|
|
shutil.copy2(str(src_config), str(out_tdir / "config.json"))
|
|
logger.debug(f" Copied config.json from original {tdir}/")
|
|
|
|
logger.info(f"\nDone! Repacked model saved to: {output_path}")
|
|
|
|
|
|
def get_args():
|
|
parser = argparse.ArgumentParser(
|
|
description="Repack msmodelslim quantized Wan2.2 weights into HF Diffusers format"
|
|
)
|
|
parser.add_argument(
|
|
"--model-type",
|
|
type=str,
|
|
required=True,
|
|
choices=SUPPORTED_MODEL_TYPES,
|
|
help="Model type to convert",
|
|
)
|
|
parser.add_argument(
|
|
"--original-model-path",
|
|
type=str,
|
|
required=True,
|
|
help="Path to the original HF Diffusers model (e.g., /weights/Wan2.2-TI2V-5B-Diffusers)",
|
|
)
|
|
parser.add_argument(
|
|
"--quant-path",
|
|
type=str,
|
|
required=True,
|
|
help="Path to msmodelslim quantized weights directory",
|
|
)
|
|
parser.add_argument(
|
|
"--output-path",
|
|
type=str,
|
|
required=True,
|
|
help="Output path for the repacked model (e.g., /weights/Wan2.2-TI2V-5B-Diffusers-MXFP8)",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = get_args()
|
|
repack(
|
|
model_type=args.model_type,
|
|
original_model_path=pathlib.Path(args.original_model_path),
|
|
quant_path=pathlib.Path(args.quant_path),
|
|
output_path=pathlib.Path(args.output_path),
|
|
)
|