From 9c2107e8d303a2b2ed6925ff023016171385162e Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sat, 4 Jul 2026 13:55:42 +0000 Subject: [PATCH] Refuse scaled fp8 LTX single files with a pointer to GGUF The Lightricks/LTX-2.3-fp8 checkpoints store float8 weights with per-tensor weight_scale and input_scale companions (verified from the file headers: 1496 F8_E4M3 tensors, 2924 scale tensors). A plain dtype cast would silently corrupt every quantized layer, so the 2.3 assembly now detects the companions and raises with a pointer to the GGUF quants, which offer comparable fidelity through the supported path. Dequantizing the scaled fp8 layout is a possible follow-up. --- studio/backend/core/inference/video_ltx2.py | 11 ++++++++ studio/backend/tests/test_video_backend.py | 31 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/studio/backend/core/inference/video_ltx2.py b/studio/backend/core/inference/video_ltx2.py index e24fe67aca..93cc88506c 100644 --- a/studio/backend/core/inference/video_ltx2.py +++ b/studio/backend/core/inference/video_ltx2.py @@ -506,6 +506,17 @@ def load_ltx23_pipeline(checkpoint_path: Path | str, *, base_repo: str, torch_dt groups = _split_checkpoint(state) del state + # The Lightricks fp8 single files store SCALED float8 weights (per-tensor + # .weight_scale/.input_scale companions). Casting those without applying the + # scales silently corrupts every quantized layer, so refuse loudly. GGUF + # Q8_0 offers comparable fidelity at similar size through the supported path. + if any(k.endswith((".weight_scale", ".input_scale")) for k in groups["dit"]): + raise ValueError( + "This LTX checkpoint stores scaled fp8 weights, which this loader does " + "not dequantize yet. Use the GGUF quants from unsloth/LTX-2.3-GGUF " + "instead (Q8_0 for the highest fidelity) or the official bf16 checkpoint." + ) + transformer = load_ltx23_transformer( groups["dit"], base_repo = base_repo, torch_dtype = torch_dtype, is_gguf = is_gguf, hf_token = hf_token, diff --git a/studio/backend/tests/test_video_backend.py b/studio/backend/tests/test_video_backend.py index 65f5a037d4..9e93ed39ba 100644 --- a/studio/backend/tests/test_video_backend.py +++ b/studio/backend/tests/test_video_backend.py @@ -327,6 +327,37 @@ def test_ltx23_split_and_variant(tmp_path): assert checkpoint_variant("x/ltx-2.3-22b-dev-Q8_0.gguf") == "dev" +def test_ltx23_scaled_fp8_refused(monkeypatch, tmp_path): + # The Lightricks fp8 files carry .weight_scale/.input_scale companions; a + # plain dtype cast would silently corrupt them, so the loader must refuse + # with a pointer to the supported GGUF path. + from core.inference import video_ltx2 + + # Stub the module tree so this also runs under the CI sim, which blocks the + # real diffusers import. + diffusers = types.ModuleType("diffusers") + diffusers.LTX2Pipeline = object + loaders = types.ModuleType("diffusers.loaders") + sfu = types.ModuleType("diffusers.loaders.single_file_utils") + sfu.load_single_file_checkpoint = lambda path: { + "model.diffusion_model.transformer_blocks.0.attn1.to_q.weight": object(), + "model.diffusion_model.transformer_blocks.0.attn1.to_q.weight_scale": object(), + } + diffusers.loaders = loaders + loaders.single_file_utils = sfu + monkeypatch.setitem(sys.modules, "diffusers", diffusers) + monkeypatch.setitem(sys.modules, "diffusers.loaders", loaders) + monkeypatch.setitem(sys.modules, "diffusers.loaders.single_file_utils", sfu) + monkeypatch.setitem(sys.modules, "transformers", types.ModuleType("transformers")) + + path = tmp_path / "ltx-2.3-22b-distilled-fp8.safetensors" + path.write_bytes(b"x") + with pytest.raises(ValueError, match = "scaled fp8"): + video_ltx2.load_ltx23_pipeline( + path, base_repo = "Lightricks/LTX-2", torch_dtype = None, is_gguf = False + ) + + def test_generate_without_load_raises(fake_runtime): backend = VideoBackend() with pytest.raises(RuntimeError, match = VIDEO_NOT_LOADED_MSG):