--- license: other license_name: ltx-2.5-community tags: - quantization - 4-bit - int4 - nvfp4 - gptq - awq - text-encoder - text-to-video - image-to-video - video-to-video - ltx-video - low-vram - comfyui base_model: Lightricks/LTX-2.5 --- # LTX-2.5 Text Encoder — 4-bit The Gemma4-12B text encoder that LTX-2.5 needs in order to read a prompt, compressed from **26.264 GB to 8.46 GB** (3.10x) and runnable on **any CUDA GPU** — no minimum compute capability, no custom kernels, no CUDA 13. If you have been unable to run LTX-2.5 because the text encoder alone wanted 26 GB, this is the part that was in your way. Drop it in and the rest of the model is unchanged. Built from the encoder Lightricks published on 2026-08-17 (`1b92891c`, *"Aligns the published encoders with the LTX-2.5 model checkpoints"*) — the revision that matches the released DiT. ## Why this one Every other public quantization of this encoder needs recent hardware: | build | size | needs | |---|---:|---| | Lightricks BF16 | 26.264 GB | — | | Lightricks `comfy-int8-convrot` | 15.373 GB | cc 8.9 | | DmitryDB `nvfp4` | 11.197 GB | cc 8.9, `comfy_kitchen`, CUDA 13 | | joeygambino / Winnougan `w4a8` | 10.604 GB | SM 8.0+ | | **this** | **8.46 GB** | **nothing beyond PyTorch** | Dequantization happens on the CPU at load and the resident model is BF16, so there is no kernel requirement to satisfy. Verified running on **Tesla V100 (cc 7.0)**, **A100 (cc 8.0)** and **RTX 6000 Ada (cc 8.9)**. It is also the smallest of the set, because it quantizes the three tensors the other nvfp4 recipe protects as BF16 "precision islands" — `embed_tokens` and both aggregate tables, 4.4 GB of the source. ## Files | file | build | video relL2 | audio relL2 | `\|\|Q\|\|/\|\|W\|\|` | |---|---|---:|---:|---:| | `A3.packed.safetensors` | bypass guard + group-bounded AWQ | **0.05204** | **0.04827** | **4.216** | | `A0.packed.safetensors` | legacy guard, for comparison | 0.06061 | 0.06095 | 302.654 | **Use `A3`.** `A0` is published only so the comparison can be checked; it carries weights up to 300x their proper norm in near-dead channels, which is harmless on this calibration set and fragile by construction. ## Samples `samples/` holds every clip twice — once from the **BF16 original (26.264 GB)** and once from this **4-bit build**, with everything downstream of the encoder held identical: same DiT, same seed, same schedule, same VAE settings, one process. `compare-NN.mp4` stacks each pair, BF16 on the left. Two sets, because they answer different questions. `compare-NN.mp4` uses the vendor's `euler_ancestral`, which is what you actually get; it re-rolls noise every step, so the two builds return different *takes*. `compare-det-NN.mp4` uses deterministic `euler`, where the seed fixes the starting noise and the conditioning is the only thing left that can move a pixel — that is the set that attributes a difference to the encoder. Mean absolute error against BF16 over every decoded frame: | clip | `euler_ancestral` | `euler` | |---|---:|---:| | robot in rain | 0.0172 | 0.0182 | | dune | 0.0596 | 0.0580 | | forge | 0.0098 | 0.0129 | | night road | 0.0702 | 0.0252 | | smoke | 0.0128 | 0.0165 | **Four of the five deterministic pairs hold together** — same composition, same lighting, same timing, differing in surface detail. **The dune does not**: BF16 renders a soldier in fatigues where the 4-bit renders a man in a business suit, from the same seed under a deterministic sampler. That difference belongs to the encoder, and it is published rather than cropped out. The qualification it deserves is that neither build followed that prompt — it asked for an astronaut and got neither — so the model had no confident answer there for a small conditioning change to disturb. All five deterministic pairs at a glance — BF16 left, 4-bit right, one row per prompt. Rows 1, 3, 4 and 5 hold together; row 2, the dune, is where the compression is visible. ![BF16 against 4-bit, five deterministic pairs](https://huggingface.co/topabaem/LTX-2.5-Text-Encoder-4bit/resolve/main/samples/comparison-sheet.png) ### Watch the two that matter **Forge — the pair holding together.** Deterministic sampler, same seed. Left is BF16, right is 4-bit. **Dune — the pair that does not.** Same terrain, same sun, same shadow, same walk, and a different person. Individually, the forge under the vendor's own sampler: | BF16 original | 4-bit | |---|---| | | | `samples/README.md` carries the prompts, the per-clip conditioning drift, and what these clips do and do not establish. All thirty clips are in `samples/`, and the Space plays them side by side under its **BF16 vs 4-bit** tab. ## Memory | mode | resident | encode | |---|---:|---| | `resident` (default) | **7.75 GiB** | dequantizes inside `forward` | | `dequantized` | ~26.3 GB | one dense build at load | The two produce `torch.equal` conditioning, so the choice is footprint against speed and never quality. `resident` measured at **8.34 GiB** peak on a 16 GB V100 — the encoder fits one consumer card for the first time. ## Use it ### Standalone ```python from huggingface_hub import hf_hub_download, snapshot_download from ltx_packed_codec import load_packed_model from transformers import AutoTokenizer repo = "topabaem/LTX-2.5-Text-Encoder-4bit" packed = hf_hub_download(repo, "A3.packed.safetensors") encoder_dir = snapshot_download(repo, allow_patterns=["encoder-hf/*"]) + "/encoder-hf" model = load_packed_model(encoder_dir, packed, resident=True) tokenizer = AutoTokenizer.from_pretrained(encoder_dir) ``` `encoder-hf/` here is config and tokenizer only, 31 MB. **The 26 GB original is not needed**: `load_packed_model` builds the skeleton with `AutoModel.from_config` and feeds every parameter from the packed file, so nothing reads the Lightricks weights. The asset blobs that ship inside the original safetensors are already in the packed file as raw BF16. ### ComfyUI `comfy_nodes/ComfyUI-LTXPacked/` provides **LTX Packed Encoder Loader** and **LTX Packed Text Encode**, which replace `CLIPTextEncode` and emit a CONDITIONING directly. They do not go through `LTXAVTextEncoderLoader`, and cannot: ComfyUI's LTX CLIP path wants a sentencepiece `spiece_model` where this checkpoint carries `tokenizer_json`, and its nvfp4 support requires `comfy_kitchen` plus CUDA 13 plus cc 8.9 — the floor this file exists to avoid. The DiT only ever needed a CONDITIONING. > **Not yet run inside ComfyUI.** The encode path is the same code this > project's `ltx_conditioning_dump.py` runs daily and the conditioning wrapper > is lifted verbatim from its renderer, so the pieces are exercised — but the > nodes themselves have not been loaded in a live ComfyUI, and that is a > different claim. ## Try it **[Space: LTX-2.5 Text Encoder 4bit](https://huggingface.co/spaces/topabaem/LTX-2.5-Text-Encoder-4bit-Demo)** — text-, image- and video-to-video, running this encoder on ZeroGPU. Measured there at 512x320, 25 frames: t2v 53.0 s, i2v 46.9 s, v2v 37.6 s. ## Image- and video-to-video Both work, and both needed a fix ComfyUI does not ship. `LTXVAddGuide` is the only producer of guided LTX latents and it cannot take an LTX-2.5 one: it calls `torch.cat` on what is a `NestedTensor` for this model. The `ValueError` in that function saying AV guides are unsupported never fires — `NestedTensor.shape` proxies to the video half, whose channel count is exactly the 128 it checks for — so the real failure is a `TypeError`, and the message is stale. Everything below the node already supports AV guides: the model routes `keyframe_idxs` to the video branch, the sampler pads a video-only denoise mask with ones for audio, and `model_base` splits the packed mask apart again. So `ltx_av_guide.py` unwraps the pair, runs the stock node on the video half and re-wraps; the guide arithmetic stays the vendor's. Measured on a 16 GB V100 at 512x320, 25 frames: t2v 98.8 s / 5.84 GiB, i2v 62.7 s / 6.80 GiB, v2v 86.5 s / 5.84 GiB. An i2v first frame lands **relL2 0.0766** from its guide image against **0.7061** for the same seed and prompt without the guide, so the guide is honoured rather than merely accepted. ## How it was built nvfp4 4.5 bpw (E2M1, group 16 with an fp8-e4m3 scale) on 320 projections; int8 row-wise on `embed_tokens` and both aggregate tables; norms and asset blobs left BF16. Per-tensor AWQ alpha search, then sequential GPTQ error compensation (blocksize 128, percdamp 0.01), packed inside the build because the group scales cannot be recovered afterwards. `A3` adds three things the plain recipe lacks: 1. **No weight column is ever zeroed.** Channels are classified by an *absolute* activation RMS, not a threshold relative to `mean(diag(H))` — the relative rule moves with the calibration Hessian's dynamic range, which made the old guard's best setting differ between Volta and Ada. 2. **The AWQ scale is shaped to the storage grid.** nvfp4 carries one scale per 16 input channels and its E2M1 grid spans 12:1; an unshaped per-channel scale spans far wider inside a group and pushes the low channels under the grid floor, where they quantize to exactly zero. Bounding the spread to 12:1 keeps both the smoothing and the channels. The identity `x·diag(1/s) @ Q(W·diag(s))ᵀ` holds for any `s`, so this needs no format change. 3. **Escalating damping, recorded.** Group shaping removes conditioning that per-channel smoothing was supplying as a side effect; `A3` needed a 10x escalation, written into the artifact metadata so a damped build is never silently compared with an undamped one. ## Measured limits * **Packing is lossless.** A twin BF16 build plus a full `verify`: all 686 tensors value-exact. No drift here is attributable to the format. * **GPTQ builds do not reproduce across GPU architectures.** Same code, plan, calibration and guard: V100 0.06879, Ada 0.11210 on the older source. Within one architecture they reproduce to five decimals across separate machines. These files were built on an **A100**; quote that alongside any figure. * **No KL or CE ratio is reported.** This artifact carries no LM head, so vocabulary KL and CE ratio are undefined for its deployment path. Measured instead: conditioning relL2/cosine per branch, and a five-prompt render comparison against a BF16 conditioning from the same card, where `A3` is closer on four of five (mean 0.05931 against `A0`'s 0.06571). * **Not evaluated**: human listening, native DiT cross-attention KL, or whether the remaining gap to BF16 is visible at all in finished video. * Prompts asking for four-legged or wheeled robots still render humans. That happens with the BF16 encoder too — a model limit, not compression damage. ## Provenance Source `Lightricks/LTX-2.5` revision `1b92891c`+, torch 2.11.0+cu128, transformers 5.14.1, plan `r45c`, calibration `calib-large.txt`. Evidence in `evidence/`: gate JSON, build logs with per-layer drift and `||Q||/||W||`, the A100 BF16 reference, all three conditionings, and fifteen rendered clips. Earlier builds against the pre-2026-08-17 encoder, and the method results that came from them, are at `topabaem/Pacific-LTX-2.5-Encoder-r45d`.