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:
parent
27541663e1
commit
e02088f5fb
3 changed files with 50 additions and 1 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue