LTX video-in pipelines saved with the first-frame wiring are upgraded on run

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzJLJWVu7RZnbRqm3nYAnX
This commit is contained in:
Claude
2026-10-27 08:17:30 +00:00
parent 4ed0685dbc
commit 702aea0f08
3 changed files with 23 additions and 2 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ import torch
from comfy.nested_tensor import NestedTensor
from ..._core import log, streams as _streams, traits as _traits, vae_tiles
from .pipeline import optional_nodes as _optional_nodes, presets as _pipeline_presets
from .pipeline import optional_nodes as _optional_nodes, presets as _pipeline_presets, upgrade as _upgrade
has_block = _traits.has_block
@@ -150,4 +150,4 @@ def decode(latent, model=None, vae=None, audio_vae=None, tile_size=0):
TRAITS = traits
PROVIDES = {"empty_latent": empty_latent, "decode": decode, "detect": detect,
"probe_traits": probe_traits, "pipeline_presets": _pipeline_presets, "optional_nodes": _optional_nodes,
"video_stream": _streams.av_video_stream}
"video_stream": _streams.av_video_stream, "upgrade_slots": _upgrade}
+11
View File
@@ -172,6 +172,17 @@ def _uncrop(as_guide):
OPTIONAL_NODES = {"FunUIIdentityTransfer": {"pass": {"0": "model", "1": "positive", "2": "negative"}}}
def upgrade(slots, _outputs=None):
"""A pipeline saved while the picture inputs still read the loader's first output: a start slot that takes whole
videos feeds them from the frames output now (output 0 is always one frame, so such a pipeline ran on a single frame)."""
start = next((s for s in slots if s.get("id") == "source_image" and s.get("node") == "FunUILoadMedia"
and any(r.get("video_frames") for r in s.get("roles") or [])), None)
if not start:
return slots
return [dict(s, inputs={k: (["source_image", 1] if isinstance(v, (list, tuple)) and list(v) == ["source_image", 0] else v)
for k, v in (s.get("inputs") or {}).items()}) for s in slots]
def optional_nodes():
return OPTIONAL_NODES
+10
View File
@@ -210,3 +210,13 @@ def test_what_a_run_puts_on_the_start_slot_is_accepted(registered, pid):
assert problems == []
users = [(s["id"], k) for s in slots for k, v in s["inputs"].items() if isinstance(v, list) and v[0] == "source_image"]
assert users and all(slots_v == 1 for slots_v in [next(s for s in slots if s["id"] == i)["inputs"][k][1] for i, k in users])
def test_a_pipeline_saved_with_the_old_first_frame_wiring_is_brought_up_to_date():
from modules.models.ltx.pipeline import presets, upgrade
slots = next(p["slots"] for p in presets() if p["id"] == "ltx23_video_to_video_direct")
old = [dict(s, inputs={k: (["source_image", 0] if v == ["source_image", 1] else v) for k, v in s["inputs"].items()}) for s in slots]
assert old != slots
assert upgrade(old) == slots
plain = [{"id": "source_image", "node": "FunUILoadMedia", "roles": [{"at": "assets.source_image"}], "inputs": {}}, {"id": "a", "node": "X", "inputs": {"image": ["source_image", 0]}}]
assert upgrade(plain) == plain