feat(ltx-2.5): image conditioning at crf=0, and the VAE encoder load path that did not exist (#644) - #657
feat(ltx-2.5): image conditioning at crf=0, and the VAE encoder load path that did not exist (#644)#657localai-bot wants to merge 4 commits into
Conversation
…path that was missing (#644) Row 1 of the #644 full-port campaign, spec committed BEFORE any code. The refusal at `src/vllm/multimodal/ltx2_video.cpp:1122` is honest and its anchors verify, but the campaign record made the CRF round trip look like the blocker. Re-verified against the current tree, it is not: * `Ltx2ConvVideoEncode` (encoder MATH) landed in phase L11 and is gated. * `Ltx2ConditionVideoByLatentIndex` (the composition) landed and is gated. * `Ltx2VideoVaeEncoderKeyRules` matches NOWHERE in the tree, and neither does any encoder CONFIG parser. The engine loads the DECODER filter only (`ltx2_video.cpp:752`), so the encoder is a brick with no delivery route. * `preprocess` short-circuits at `if crf == 0: return image` (`media_io/decode.py:425-426`), so a crf=0 path is upstream-legal and needs no codec. This environment has no `av` module at all, which is the demonstration rather than the obstacle. So the row builds the load path, serves an image at latent frame 0 at crf=0 as an explicitly OUT-OF-DISTRIBUTION arm (a 2.5 checkpoint resolves default_image_crf=18 via constants.py:130-133 -> LTX_2_4_PARAMS), and narrows the refusal into four messages that each name a different missing piece. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…path that did not exist (#644) Row 1 of the #644 campaign. Spec `0c1390f8e`, committed before any code. WHAT WAS ACTUALLY MISSING. The refusal at `ltx2_video.cpp:1122` covered four conditioning kinds with one reason, and the reason that mattered was not the CRF round trip it led with: * `Ltx2ConvVideoEncode` (the encoder MATH) landed in phase L11, gated. * `Ltx2ConditionVideoByLatentIndex` (the placement) landed, gated. * `Ltx2VideoVaeEncoderKeyRules` matched NOWHERE in the tree, and no parser produced an `Ltx2ConvVideoEncoderConfig`. The engine materialized `Ltx2VideoVaeDecoderKeyRules()` alone, so the encoder was a brick with no delivery route. * `preprocess` short-circuits at `if crf == 0: return image` (media_io/decode.py:425-426), so a crf=0 path is upstream-legal and needs no codec. This box has no `av` module at all, which is the demonstration. So this builds the delivery route — `Ltx2VideoVaeEncoderKeyRules`, `Ltx2ParseConvVideoEncoderConfig`, `Ltx2CheckpointHasVideoEncoder` — plus the pixel front-end (`Ltx2DecodePpmRgb`, `Ltx2ResizeAndCenterCrop`, `Ltx2LoadImageAndPreprocess`, `Ltx2ResolveDefaultImageCrf`), and serves an image at latent frame 0 at crf=0. Both new TUs are additive rather than additions to `ltx2_loader.cpp`, which a concurrent row of this campaign also needs to edit. CRF 0 IS OUT OF DISTRIBUTION AND SAYS SO. An LTX-2.5 checkpoint resolves `default_image_crf = 18` (constants.py:37/124/130-133), and that round trip is unported, so the DEFAULT REFUSES: a caller must pass `image_crf=0` knowingly. THE OTHER THREE REFUSALS NOW NAME DIFFERENT MISSING PIECES — the DiT's unported `keyframes_abs_pos_embedding` for keyframes, the IC-LoRA scale factors for the reference arms, the audio VAE's absent encoder filter for reference audio — so a later reader can re-check a reason instead of trusting it. That is the defect this campaign has hit five times, including in the message replaced here. EVIDENCE. New generator executes upstream ltx_core AND ltx_pipelines under the pinned SHA fd4ded7f (package __init__ bypassed, `av`/`OpenImageIO` stubbed as TRIPWIRES; `preprocess(image, 0) is image` is asserted by EXECUTION, and `preprocess(image, 18)` is asserted to reach the codec and raise). Bands derived by measurement, not picked: 2e-6 latent-space (worst 4.77e-07), 2e-4 pixel-space (worst 6.10e-05). Six source mutations RED with counts, one SURVIVOR analysed rather than hidden — see the spec's section 8. THREE OF THIS ROW'S OWN CLAIMS WERE REFUTED AND ARE CORRECTED IN THE SPEC: the `ceil` does not guard what upstream's comment says it guards (swept, no pair in range); no golden here can see a resize/normalize ORDER swap (the two are algebraically equal, gap 1.94e-07); and torch's bilinear cannot be matched bit for bit portably (FMA contraction, and this tree builds -ffp-contract=off). Gate: BUILD_EXIT=0, 0 ENOSPC/BFD hits, `ctest -N` 424, `ctest` 424/424 exit 0. Focused: test_ltx2_image_cond 15 cases / 198 assertions, test_ltx2_video 32 / 550. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
Picks up #650's secondary-oracle registry. Clean merge: the only file touched by both sides is `.agents/roadmap_v1.md`, and the two edits add DIFFERENT keyed rows (#644 here, #647 there), so no key was rewritten. `check-oracle-pins.py` — new on main — passes on this tree at 7 oracles pinned. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…, and the FALSE refusal reason it exposed (#644) FOLLOWING_AGENTS_PROTOCOL Image conditioning for LTX-2.5 at crf=0, plus the VAE encoder load path that did not exist, plus the repair of that feature's own review findings. This branch CONTAINS #657, which closes as superseded. The finding worth reading: the refusal for keyframe conditioning named a FALSE reason, and a test had pinned it. It blamed `keyframes_abs_pos_embedding`. Refuted against Lightricks/LTX-2 @ fd4ded7f, and the decisive link was one the original refutation had not cited -- `conditioning/mask_utils.py:101`, where `marked=False` writes ZEROS onto the appended tokens, and `:97-98`, where a `None` prior mask makes the whole mask `None` so `apply_keyframes_absolute_embedding` returns `hidden_states` unchanged. Meanwhile `tools.py:184` + `_first_frame_keyframes_mask` mark the TARGET's first latent frame unconditionally -- the tokens the served arm writes into. So the contrast was backwards: the appended keyframe gets nothing. The new message names the real reason (the token-append machinery against a phase loop fixed at the target grid's token count), labels the old one "WHAT IS *NOT* THE REASON" with #658, and switches local anchors from same-file line numbers to SYMBOLS -- which is how the previous citation went stale. The second finding was a gate that caught nothing: mutating to `if (wants_image && phase_index == 0)` left the suite byte-for-byte at 32/550 exit 0. The replacement pins the last phase's per-latent-frame count against a `max_phase = 0` engine, comparing two MEASURED values from two different engines rather than a compile-time constant. Reviewed PASS (9 findings, none blocking) by a fresh reviewer who re-derived the upstream refutation itself, verified the hand-resolved merge left zero keyed-record keys lost or duplicated, and measured the shipped checkpoints directly: the first-party NVFP4 DiT declares the flag with the tensor ABSENT, while the vonkaiser FP8 copy carries it TRAINED at 4096/4096 non-zero bytes. Operator gate at the exact pushed SHA, main pinned to an immutable SHA rather than the ref (this is a shared checkout and `origin/main` moves mid-operation): WINPORT_EXIT=0, check-device-leakage OK at DSR 32 == baseline 32, BUILD_EXIT=0, 0 errors, 0 ENOSPC, REGISTERED=451, ctest 450/451, `test_ltx2_video` 32 cases/565 assertions exit 0, `test_ltx2_image_cond` 15/223 exit 0. The single ctest failure is #737, main's own Music3 golden red. Honest limit: `LTX2_CHECKPOINT_ROOT` is set in no workflow (#673), so CI exercises about 5.7% of `test_ltx2_video`'s assertions and the shipped-checkpoint subcase SKIPS. This row's own claims are not in that gated set, but the checkpoint-derived evidence in this campaign is host-local. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5[1m] [claude-code]
Superseded by #786 — please do not merge thisThis PR's head branch, Measured consequences on this branch as it stands:
The add/add set includes The content is not lost. The tree at Close this once #786 lands. 🤖 Generated with Claude Code |
…, and the FALSE refusal reason it exposed (#644) FOLLOWING_AGENTS_PROTOCOL Image conditioning for LTX-2.5 at crf=0, the VAE encoder load path that did not exist, and the repair of that feature's own review findings. THE FINDING WORTH READING: the keyframe refusal named a FALSE reason, and a test had pinned it. It blamed `keyframes_abs_pos_embedding`. Refuted at Lightricks/LTX-2 @ fd4ded7f, and the decisive link was one the original refutation had not cited -- `conditioning/mask_utils.py:101`, `new = torch.ones if marked else torch.zeros`, with `:97-98` returning None for an unmarked append onto a None mask. The sole consumer `transformer_args.py:42-43` is `hidden + mask * embedding`, called ONCE at `:269`, so the appended keyframe contributes exactly nothing. Meanwhile `tools.py:184` + `_first_frame_keyframes_mask` mark the TARGET's first latent frame unconditionally -- the tokens the served arm writes into. The contrast was backwards. The new message names the real reason, labels the old one "WHAT IS *NOT* THE REASON" with #658, and switches local anchors from same-file line numbers to SYMBOLS, which is how the previous citation went stale. The second finding was a gate that caught nothing: mutating to `if (wants_image && phase_index == 0)` left the suite byte-for-byte at 32/550 exit 0. The replacement pins the last phase's per-latent-frame count against a `max_phase = 0` engine -- two MEASURED values from two different engines, not two literals. Re-mutated at review: 32/33 cases, 570/576 assertions, exit 1. THIS IS A RECONSTRUCTION, and why matters. The predecessor PR's base branch was the row's own branch rather than main -- an operator error, ancestry read instead of baseRefName -- so merging it squashed onto that branch and FLATTENED AWAY the `merge: origin/main` commit beneath it. Measured consequence: the merge base fell 67 commits back, the row's apparent diff became 252 files / 42,426 insertions, and merging main produced 19 conflicts including add/add on files that are purely main's. The row was rebuilt from a pinned main SHA instead. So the review had to prove BOTH directions, and did: - nothing of main's in: the complete 19-file list, no path filter, with a positive control (ltx2 = 13) and a negative one; gpt2/talker/w2vbert/ music3_oracle/bigvgan/website/indextts/lenreg all ZERO - nothing of the row's out: the substantive +/- payload of the row against the main it was last gated on is BYTE-IDENTICAL to the reconstruction -- 4471 lines each, sha256 8e6ba77f… both, differing only in 16 hunk headers Counts attributed three ways: `test_ltx2_video` 33/576, where main's fc903b8 (#674) contributed +1 case / +11 assertions and the row +2 cases; fc903b8's own commit body records 31/513, and 502+63 = the pre-reconstruction 565. Operator gate at the exact pushed SHA, main pinned to an immutable SHA, with the leak check INSIDE the gate so it cannot pass silently: 19 row files, 0 main-only names, positive control 13. BUILD_EXIT=0, 0 errors, 0 ENOSPC, REGISTERED=463, ctest 463/463 CTEST_EXIT=0, `test_ltx2_video` 33/576 and `test_ltx2_image_cond` 15/223, both exit 0. Non-blocking follow-up filed as #794: six mis-cited upstream anchors, two pointing at blank lines. Every substantive claim is true at the pin; only the line numbers are wrong. #769's duplicate FEATURES key is carried at multiplicity 2 -- neither introduced nor multiplied. Supersedes #657 and #666. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5[1m] [claude-code]
|
Closing as superseded by #786, which landed on Why this one could not landThis branch's head is That squash flattened away the
Merging it would have dragged that diff onto main. Hand-resolving add/add conflicts on files this row never authored is precisely how main's newest work gets silently reverted. Nothing was lost#786 was rebuilt from a pinned main SHA and carries this row's content provably intact: the substantive Both directions were verified, because for a reconstruction "nothing leaked in" is only half the claim: the complete 19-file list contains zero of main's files (positive control The transferable partA PR's base is not implied by its ancestry, and the only reliable proof a merge landed is that its commit is an ancestor of |
Row 1 of the #644 LTX-2.5 full-port campaign: closes the image conditioning arm and builds the thing every arm was blocked on.
Spec
0c1390f8e, committed before any code:.agents/specs/ltx25-image-conditioning.md.What was actually missing
The refusal at
ltx2_video.cpp:1122was honestly written and its anchors verify, but it led with the CRF round trip, and that is not what blocked anything. Re-verified against the current tree:Ltx2ConvVideoEncodeLtx2ConditionVideoByLatentIndexltx2_video.cpp:752grep -rn Ltx2VideoVaeEncoderKeyRules src include testsgrep -rn ParseConvVideoEncoder src include testsSo the encoder was a brick with no delivery route.
preprocessshort-circuits atif crf == 0: return image(media_io/decode.py:425-426), so acrf=0path is upstream-legal and needs no codec — this box has noavmodule at all, which is the demonstration rather than the obstacle.What this adds
Ltx2VideoVaeEncoderKeyRules/Ltx2ParseConvVideoEncoderConfig/Ltx2CheckpointHasVideoEncoder—VAE_ENCODER_COMFY_KEYS_FILTER(video_vae/model_configurator.py:267-276) andVideoEncoderConfiguratorover_prepare_video_encoder_kwargs(:37-69, 72-78).Ltx2DecodePpmRgb,Ltx2ResizeAndCenterCrop,Ltx2LoadImageAndPreprocess,Ltx2ResolveDefaultImageCrf(media_io/decode.py:46-79, 139-170, 413-435,resize.py:41-73,range_map.py:8-9,constants.py:36-37, 124, 126-133).image_crf, the family's first per-generation extra, reachable through the existing ABI arrays with no ABI change, and throughltx2-gen --first-frame / --image-crf.Both new TUs are additive files rather than additions to
ltx2_loader.cpp, which a concurrent row of this campaign also needs to edit.CRF 0 is out of distribution, and says so
An LTX-2.5 checkpoint resolves
default_image_crf = 18(constants.py:37/124/130-133), and the H.264 round trip is unported. The default therefore refuses — a caller has to ask forimage_crf=0knowingly and gets a render conditioned on uncompressed pixels. Both halves are stated rather than one of them.The other three refusals now name different missing pieces
This campaign has had five refusals whose stated reason went stale, including the one replaced here. Each surviving message names the exact symbol a later reader can go and check:
keyframes_abs_pos_embedding(the encoder andLtx2ConditionVideoByKeyframeare both here now);downscale_factor/temporal_scale_factor, which live in LoRA metadata this project does not read;Evidence
scripts/gen-ltx2-image-cond-goldens.pyexecutes upstreamltx_coreandltx_pipelinesat the pinned SHAfd4ded7f. The package__init__files are bypassed andav/OpenImageIOare stubbed as tripwires, not shortcuts:preprocess(image, 0) is imageis asserted by object identity (no stub can manufacture that) andpreprocess(image, 18)is asserted to reach the stubbed codec and raise, so the branch is measured rather than read.Bands derived from measurement, not picked (both tolerances set to
1e-12and the reported worst read back):6.10352e-052e-44.76837e-072e-62.68221e-072e-61.78814e-072e-6Six source mutations RED, each rebuilt and restored byte-for-byte (md5 verified). Green baseline
test_ltx2_image_cond15 / 198 andtest_ltx2_video32 / 550:ceil→llroundin the resize1 - strength→strengthvae.out_channelsEncoder weights are loaded AND used, proven separately: one tensor of the encoder half is perturbed in a second fixture (offset read from the safetensors header, not guessed) with the request byte-identical, and the conditioning digest must move. The first version of that check searched for the tensor NAME and flipped a byte a fixed distance past it — which lands in a neighbouring tensor's JSON — and it failed, which is the only reason that is a footnote rather than a false green.
Three of this row's own claims were REFUTED
Recorded in the spec's §8 rather than quietly fixed:
ceildoes not guard what upstream's comment says it guards. Swept every source/target pair in3..40 → {16, 24}: no pair makessrc * scaleland just above an integer. It is still load-bearing, for the ordinary reason that it disagrees withroundandfloor, and the generator asserts that.1.94e-07, below every band and not amplifiable. The order is mirrored because it is upstream's, and that is written down in three places instead of assumed covered.-ffp-contract=off.One mutation SURVIVED and is analysed rather than hidden (spec §8.4): phase 0 runs at
noise_scale = 1.0, where the mutated value is genuinely discarded — and separately,test_ltx2_videogates no value of the composed latent, whichtest_ltx2_image_conddoes against executed upstream. That survivor is why the trace digest is taken over the tokens as written rather than over the encoder's output.Gate
BUILD_EXIT=0,0ENOSPC/BFD hits, CPU Release, gcc.ctest -N→ 424;ctest -j 4→ 424/424 passed, 0 failed, exit 0 — denominator asserted against the listing.test_ltx2_image_cond15 cases / 198 assertions,test_ltx2_video32 / 550, both exit 0.scripts/agent-preflight.sh --staged→ all gates green, including the newcheck-oracle-pins.pyfrom policy(oracle): admit a pinned secondary oracle where vLLM implements nothing (#647) #650.md5 da4f7423…a439before and after a regeneration.Closes part of #644.
🤖 Generated with Claude Code