Fix two silent no-ops in LTXLikenessAnchor / LTXLatentAnchorAware - #15
Open
jjdejong wants to merge 2 commits into
Open
Fix two silent no-ops in LTXLikenessAnchor / LTXLatentAnchorAware#15jjdejong wants to merge 2 commits into
jjdejong wants to merge 2 commits into
Conversation
Both anchors discovered their grid dimensions by scanning forward()'s args/kwargs for "the first 5D tensor", commented as "typically the latent input". It never was. LTX-AV passes x as a video+audio container rather than a Tensor -- separate_audio_and_video_latents indexes it as x[0]/x[1] and recombine_audio_and_video_latents returns a plain [vx, ax] list -- so isinstance(..., torch.Tensor) skips it and the scan never descends. What the scan actually picked up was denoise_mask, shaped (B, 1, F, H, W) by model_base, whose F/H/W coincidentally equal the latent's. So the dims came out right on i2v graphs by accident, while graphs that pass no mask captured nothing at all: LTXLikenessAnchor's latent_frame_0 mode returned its input unmodified and LTXLatentAnchorAware's blend never ran, both silently and with no output even under debug. Add _extract_video_latent, which resolves the x argument (keyword or first positional), unwraps a .tensors wrapper or plain list to reach the video element, and falls back to a rank-5 scan that explicitly excludes denoise_mask/concat_mask so a mask can never masquerade as the latent. Report the failure under debug instead of going quiet. Verified against the real argument shapes: a [video, audio] list, a NestedTensor-style wrapper, a bare 5D tensor, and the x-as-keyword form all resolve to the 128-channel video latent even when denoise_mask is present; a mask alone now returns None rather than passing itself off as the latent.
Registration was gated on a sentinel attribute living on the backbone, which model.clone() shares by reference. From the second apply onward in a single ComfyUI process the gate was already set, so the new pre-hook was never attached and the first apply's closure stayed live, writing into a state dict nothing else could read. The effect was that captured_latent_shape and current_sigma stayed None for the rest of the process: latent_frame_0 silently returned unmodified output from the second prompt onward, and the skip_when_sigma_above gate stopped working in every mode including guide. Only the first prompt after a restart ever worked, which makes it easy to mistake for a working node. Track the handle, remove any prior registration, and re-register unconditionally -- the same lifecycle the attn1 hooks already use, and that LTXLatentAnchorAware already applies to both its hook types. The bypass path now detaches the pre-hook too instead of only dropping the sentinel, which previously left it attached and firing. Verified by applying three times against a shared module: each apply's state dict is the one populated, and exactly one pre-hook stays live.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two bugs that make the anchors silently do nothing. Both fail without raising, without logging, and while still returning a valid MODEL — so the node looks like it is working. Found while debugging LTX-AV i2v runs on ComfyUI.
Two commits, reviewable independently.
1. The video latent is located by guessing tensor rank
Both anchors discover their grid dimensions in the backbone pre-hook by scanning
forward()'s args/kwargs for "the first 5D tensor", commented as "typically the latent input".It never is. LTX-AV passes
xas a video+audio container, not a Tensor —separate_audio_and_video_latentsindexes it asx[0]/x[1], andrecombine_audio_and_video_latentsreturns a plain[vx, ax]list — soisinstance(..., torch.Tensor)skips it and the scan never descends.What it actually finds is
denoise_mask, reshaped to(B, 1, F, H, W)bymodel_base. Its F/H/W coincidentally equal the latent's, so the dims come out right by accident:denoise_maskNoneIn the second case
LTXLikenessAnchor'slatent_frame_0mode returns its input unmodified at every block, andLTXLatentAnchorAware's blend never runs.Fix:
_extract_video_latent()resolves thexargument (keyword or first positional), unwraps a.tensorswrapper or plain list to reach the video element, and only then falls back to a rank-5 scan that excludesdenoise_mask/concat_mask.Verified against the real argument shapes:
The channel count is the tell: 128 is the video latent, 1 was the mask. Confirmed live —
LTXLatentAnchorAwarenow logscaptured video latent: (1, 128, 31, 17, 17).2. The backbone pre-hook is never re-registered
That sentinel lives on the backbone module, which
model.clone()shares by reference. From the second apply onward in a single ComfyUI process the gate is already set, so the new pre-hook is never attached and the first apply's closure stays live — writingcaptured_latent_shapeandcurrent_sigmainto a state dict nothing else can read.For every apply after the first, for the process lifetime:
latent_frame_0silently returns unmodified output —captured_latent_shapestaysNone, so the hook bails at every block.skip_when_sigma_abovestops working in all modes, includingguide—current_sigmacomes from the same dead closure.Only the first prompt after a restart ever worked, which makes it easy to mistake for a functioning node.
The
attn1hooks in this same node already remove-and-re-register correctly, andLTXLatentAnchorAwaredoes so for both its hook types — this path was the outlier.Fix: track the handle, remove any prior registration, re-register unconditionally. The bypass path now detaches the pre-hook too, rather than only dropping the sentinel — which previously left it attached and firing.
Verified by applying three times against a shared module:
Confirmed live: before the fix a second apply logged
video latent shape was never captured; after it, the same second apply reportsHOOK ACTIVE.Tested on LTX-2.3 22B i2v, ComfyUI on ROCm/gfx1151. No API, node-name or default-value changes; behaviour is unchanged on graphs that were already working (i2v with a mask, first apply after restart).