Refuse single-file loads for the dual-expert Wan A14B and write through the second-DiT view

A gguf or safetensors checkpoint carries one expert; the pipeline then pulls
the other dense bf16 from the base repo outside the memory plan. validate now
refuses it before any download, and the unused a14b gguf_repo pointer is gone
so nothing surfaces those repos as loadable. _SecondDiTView also delegates
attribute writes to the real pipe (transformer writes land on transformer_2)
so a helper's reassignment cannot vanish with the temporary view.
This commit is contained in:
Daniel Han 2026-07-05 00:13:50 +00:00
commit e02088f5fb
3 changed files with 50 additions and 1 deletions

View file

@ -227,6 +227,13 @@ class _SecondDiTView:
# ``transformer`` / ``_pipe``), so everything else delegates to the real pipe.
return getattr(object.__getattribute__(self, "_pipe"), name)
def __setattr__(self, name: str, value: Any) -> None:
# Writes must land on the real pipe, or a helper's side effect (for example
# reassigning the transformer it optimised) would vanish with the view.
# ``transformer`` mirrors the read property onto the second expert.
pipe = object.__getattribute__(self, "_pipe")
setattr(pipe, "transformer_2" if name == "transformer" else name, value)
def _views_for(pipe: Any, fam: VideoFamily) -> tuple[Any, ...]:
"""The pipe view(s) to pass through the ``getattr(pipe, "transformer")`` helpers so
@ -283,6 +290,14 @@ class VideoBackend:
)
if kind in ("gguf", "single_file") and not gguf_filename:
raise ValueError("A gguf/single_file load needs the checkpoint filename.")
if kind in ("gguf", "single_file") and fam.is_moe:
# A single checkpoint carries only one expert; the pipeline would then pull
# the other expert dense bf16 from the base repo, outside the memory plan.
raise ValueError(
f"'{fam.name}' is a dual-expert model: a single {kind} file covers only "
f"one of its two transformers. Load the diffusers pipeline repo "
f"('{fam.base_repo}') instead."
)
# Reject a malformed transformer_quant scheme cheaply, before the GPU handoff
# (normalize_transformer_quant raises ValueError on an unknown scheme). It applies
# only on pipeline-kind loads (the dense DiT from the base repo); an ignored value

View file

@ -188,7 +188,8 @@ _FAMILIES: tuple[VideoFamily, ...] = (
# total ~114.3; UMT5 text encoder 11.4; VAE 0.5. The two-expert DiT total is the
# memory headline (~114 GB bf16-resident before offload).
bf16_components_gb = (114.3, 11.4, 0.5),
gguf_repo = "QuantStack/Wan2.2-T2V-A14B-GGUF",
# No gguf_repo: community GGUFs ship the two experts as separate files, and a
# single-file load covers only one (validate_load_request refuses it).
),
)

View file

@ -774,3 +774,36 @@ def test_wan_validate_trusted_repos(fake_runtime):
model_kind = "pipeline",
transformer_quant = "bogus",
)
def test_wan_a14b_refuses_single_file_loads(fake_runtime):
# A single gguf/safetensors checkpoint carries only one of the A14B's two experts;
# the pipeline would pull the other dense bf16 from the base repo outside the
# memory plan, so validate refuses it up front (before any download).
backend = VideoBackend()
with pytest.raises(ValueError, match = "dual-expert"):
backend.validate_load_request(
"QuantStack/Wan2.2-T2V-A14B-GGUF",
gguf_filename = "HighNoise/Wan2.2-T2V-A14B-HighNoise-Q4_K_M.gguf",
)
# The single-DiT 5B family still accepts GGUF.
fam = backend.validate_load_request(
"QuantStack/Wan2.2-TI2V-5B-GGUF",
gguf_filename = "Wan2.2-TI2V-5B-Q4_K_M.gguf",
)
assert fam.name == "wan2.2-ti2v-5b"
def test_second_dit_view_write_through():
# Attribute writes on the proxy must land on the real pipe (a helper's side
# effect would otherwise vanish with the temporary view); a ``transformer``
# write mirrors the read property onto the second expert.
from core.inference.video import _SecondDiTView
pipe = types.SimpleNamespace(transformer = "t1", transformer_2 = "t2", flag = None)
view = _SecondDiTView(pipe)
assert view.transformer == "t2"
view.transformer = "t2-compiled"
assert pipe.transformer_2 == "t2-compiled" and pipe.transformer == "t1"
view.flag = "set"
assert pipe.flag == "set"