Skip to content

feat(ltx-2.5): port the prompt-side AdaLN, and stop the opt-in from clearing it - #654

Merged
localai-bot merged 3 commits into
mainfrom
row/LTX25-PROMPT-ADALN
Aug 13, 2026
Merged

feat(ltx-2.5): port the prompt-side AdaLN, and stop the opt-in from clearing it#654
localai-bot merged 3 commits into
mainfrom
row/LTX25-PROMPT-ADALN

Conversation

@localai-bot

@localai-bot localai-bot commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Closes part of #644 (row 0).

The defect

use_prompt_adaln_single defaults TRUE in both references — diffusers
transformer_ltx2.py:1185, LTX-2 model.py:77 and model_configurator.py:76/:138
(config.get("use_prompt_adaln_single", True)) — and the shipped LTX-2.5 DiT carries
the module's tensors. ltx2_loader.cpp cleared the flag unconditionally in three
places, so every render kept only the static prompt_scale_shift_table and dropped
the timestep-conditioned half of the prompt K/V modulation.

It was invisible: shapes unchanged, values finite, and every committed golden was
generated with the flag OFF (gen-ltx2-goldens.py:149), so the gate agreed with the
defect. Reaching it required allow_unported_modules=1, which a real render needs
for keyframes_abs_pos_embedding — so the only way to load a real DiT was also the
way to silently disable a correctness setting
.

Measured magnitude, flag ON vs OFF over the same shared weights

CORRECTED 2026-08-13 (#663). These four rows are GATE-FLOOR numbers from
SYNTHETIC weights and NONE of them answers "does this matter". The static table
AND the prompt-AdaLN MLP are both drawn from param_spec's scale=0.05
(gen-ltx2-goldens.py:100-106), so every ratio below is a property of the
FIXTURE and moves with the init scale (0.005 → 4.1%, 0.2 → 1450%). They are
reported because the mutation has to be shown to move something.

Quantity (SYNTHETIC fixture, gate floor) Delta
timestep term vs the static table it is added to max|term| 0.0252 vs max|table| 0.0487 — 51.7%
block-0 modulated prompt K/V 0.0310 — 5.82% of max|off|
DiT video output (2 blocks) 1.4567e-4 — 73x the gate's 2e-6 f32 floor
DiT audio output 7.367e-5 — 37x the floor

On the SHIPPED checkpoint the term DOMINATES the table it is added to, measured
through upstream's own AdaLayerNormSingle on the real weights: rms|term| /
rms|table| = 1347% video, 1583% audio; max|term| / max|table| =
7119% / 2817%. Compared like for like against the 51.7% above, this fixture
UNDERSTATED the defect by 138x (video) and 54x (audio). Reproduce with
scripts/measure-ltx2-prompt-adaln.py; details in
.agents/specs/ltx25-prompt-adaln.md §Outcome.

What was ported, and from where

Piece Upstream
the module model.py:222-227 (video), :252-257 (audio); AdaLayerNormSingle(dim, embedding_coefficient=2), adaln.py:19-45
the producer transformer_args.py:274-277, driven by modality.sigma [batch] and not the per-token timesteps; scaled by timestep_scale_multiplier at :173-186
the consumer transformer.py:441-446, summed into the table before (1 + scale) applies; threaded per stream at :288-296 / :317-325
diffusers twin transformer_ltx2.py:1536-1547, :1648-1649, :677-693

Both arms: Ltx2DitForward (f32 host) and Ltx2DitForwardDevice (f32 and the bf16
production stream). Pins: LTX-2 fd4ded7f, diffusers 3a2f35d4.

What replaced the cleared flag

The three = false assignments become an invariant: the resolved flag must equal
whether the FILE carries prompt_adaln_single
, refused by name otherwise. And
Ltx2AdoptDeclaredDitParams now clears exactly ONE flag in its config copy —
use_keyframes_abs_pos_embedding, whose module really is unported — so
allow_unported_modules can no longer switch a ported feature off. A config that
disagrees with the shapes about the flag is now refused rather than forced into
agreement on both sides.

Both shipped DiTs were re-read off the NAS to confirm this refuses neither: the FP8
file carries no __metadata__ at all, the NVFP4 file's config OMITS the key, so
upstream's TRUE default decides on both and matches their tensors.

Evidence

Goldens executed from upstream ltx_core at reduced dims (gen-ltx2-goldens.py
section 6). Every pre-existing golden VALUE is byte-identical; only case-header
comments changed.

Five mutations, each RED, each restored byte-for-byte (source md5s re-checked):

Mutation Result
host ModulateContext ignores the prompt term 3/35 cases, 6/2435 assertions, exit 1
device path always takes the static-only branch 1/15 cases, 6/523 assertions, exit 1
re-add use_prompt_adaln_single = false in the loader guard throws by name; assertion count DROPS 4826 → 4815, exit 1
MLP driven by timesteps instead of sigma 2/35 cases, 4/2435, exit 1
shift and scale rows swapped 2/35 cases, 4/2435, exit 1

A permanent in-tree case asserts the flag-ON forward misses the flag-OFF golden,
so a module that is bound but never added cannot pass.

Gate

BUILD_EXIT=0 throughout; build logs grepped for No space left|BFD assertion (0).
ctest -N = 423; full ctest -j8 423/423 pass, exit 0 after merging
origin/main (b81a5e413) and rebuilding.

Suite counts vs the cefacd2d0 baseline, measured by reverting the tree to HEAD,
rebuilding and running, then re-applying the diff and re-verifying its md5:

Suite HEAD this row
test_ltx2 30 / 1627 35 / 2435
test_ltx2_loader 24 / 4817 26 / 4826
test_ltx2_device 13 / 498 15 / 523
test_ltx2_video 30 / 502 30 / 502 (SKIPPED default; 30 / 8734 with LTX2_CHECKPOINT_ROOT set)

Spec: .agents/specs/ltx25-prompt-adaln.md, committed at baa92ccf7 before any
implementation.

🤖 Generated with Claude Code

mudler added 3 commits August 13, 2026 17:05
`use_prompt_adaln_single` defaults TRUE in both references — diffusers
`transformer_ltx2.py:1185` and LTX-2 `model.py:77` /
`model_configurator.py:76,138` — and the shipped FP8 DiT carries the 18
tensors the flag builds. `ltx2_loader.cpp:988` (and `:573`, `:626`) cleared
it unconditionally, so every render kept only the static
`prompt_scale_shift_table` and dropped the timestep-conditioned half of the
prompt K/V modulation. Shapes, finiteness and every committed golden agree
with the defect, because the goldens were generated with the flag OFF.

Spec only; no code. Records the upstream anchors for the module
(`model.py:222-227`, `:252-257`), the producer
(`transformer_args.py:274-277`, driven by `modality.sigma` and NOT
`timesteps`), the consumer (`transformer.py:441-446`), and what replaces
the cleared flag so `allow_unported_modules` can no longer silently disable
a ported feature.

Issue #644, row 0.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…learing it

`use_prompt_adaln_single` defaults TRUE in both references — diffusers
`transformer_ltx2.py:1185`, LTX-2 `model.py:77` and
`model_configurator.py:76`/`:138` — and the shipped DiT carries the module's
tensors. `ltx2_loader.cpp` cleared it in three places, so every render kept
only the static `prompt_scale_shift_table` and dropped the
timestep-conditioned half of the prompt K/V modulation. Nothing saw it:
shapes unchanged, values finite, and every committed golden was generated
with the flag OFF.

MEASURED, flag ON vs OFF over the same shared weights: the timestep term is
51.7% the magnitude of the static table it is added to, moves the block-0
modulated prompt K/V by 5.82%, and moves the DiT output by 1.46e-4 — 73x the
gate's f32 floor.

Ported, citing what each piece came from:
  the module      <- model.py:222-227 (video), :252-257 (audio);
                     AdaLayerNormSingle(dim, embedding_coefficient=2), adaln.py:19-45
  the producer    <- transformer_args.py:274-277 driven by `modality.sigma`
                     [batch] and NOT the per-token `timesteps`, scaled by
                     timestep_scale_multiplier at :173-186
  the consumer    <- transformer.py:441-446, summed into the table BEFORE
                     `(1 + scale)` applies, threaded per stream at :288-296/:317-325
  diffusers twin  <- transformer_ltx2.py:1536-1547, :1648-1649, :677-693

Both arms: `Ltx2DitForward` and `Ltx2DitForwardDevice` (f32 and the bf16
production stream).

The three `= false` assignments are replaced by an invariant: the resolved
flag must equal whether the FILE carries `prompt_adaln_single`. And
`Ltx2AdoptDeclaredDitParams` now clears exactly ONE flag in its config copy —
`use_keyframes_abs_pos_embedding`, whose module really is unported — so
`allow_unported_modules=1`, which a real render still needs, can no longer
switch a ported feature off. A config that disagrees with the shapes about
the flag is refused rather than forced into agreement.

Both shipped DiTs were re-read off the NAS to check this refuses neither: the
FP8 file carries no `__metadata__` at all and the NVFP4 file's config OMITS
the key, so upstream's TRUE default decides on both, matching their tensors.

Evidence. Goldens executed from upstream LTX-2 @fd4ded7f at reduced dims
(`gen-ltx2-goldens.py` section 6); every pre-existing golden VALUE is
byte-identical. Five mutations, each RED and each restored byte-for-byte:
dropping the term host-side (3/35 cases), dropping it device-side (1/15),
re-adding the cleared flag (the guard throws; assertion count drops
4826->4815), driving the MLP from `timesteps` instead of `sigma` (2/35), and
swapping the shift and scale rows (2/35). A permanent in-tree case asserts
the flag-ON forward MISSES the flag-OFF golden, so a bound-but-unused module
cannot pass.

Suites vs `cefacd2d0`: test_ltx2 30/1627 -> 35/2435, test_ltx2_loader
24/4817 -> 26/4826, test_ltx2_device 13/498 -> 15/523, test_ltx2_video
30/502 -> 30/502 (its fixture now carries the module, which is the shipped
shape). Full `ctest` 423 registered, 422 pass under -j8 and
`test_serve_low_tools` passes on its serial re-run.

Spec .agents/specs/ltx25-prompt-adaln.md @baa92ccf7. Issue #644, row 0.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
Brings in b81a5e4 (the secondary-oracle registry). No conflict and no
overlap with this row's files; rebuilt clean and re-ran the full gate after
the merge, because a clean merge is not a compiling one: BUILD_EXIT=0,
ctest 423/423.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants