fix: loop roll failures reach the guard (recorded, dropped, call rerun); sampler keeps an empty fault record

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-10-03 11:34:36 +03:00
co-authored by Claude Sonnet 5.5
parent 6ac6acf612
commit 397f36014a
3 changed files with 36 additions and 27 deletions
+5 -2
View File
@@ -102,8 +102,11 @@ class FunPackSampler(io.ComfyNode):
# its cache -- so it is emptied here rather than trusted to be fresh.
# Otherwise a modifier dropped in one generation stays dropped in every
# later one, with nothing said.
dropped = (getattr(model, "model_options", {}).get("funpack_dropped") or getattr(model, "funpack_dropped", None)
or patching.Dropped())
dropped = getattr(model, "model_options", {}).get("funpack_dropped")
if dropped is None:
dropped = getattr(model, "funpack_dropped", None)
if dropped is None:
dropped = patching.Dropped()
dropped.clear()
latent = latent.copy()
+13 -25
View File
@@ -156,12 +156,6 @@ def _loop_audio_tail_frames(mask):
return 0
def _roll_failed(e, call, args):
log.once("temporal_style:loop_failed", log.ALERT, "FunPack Temporal style",
f"the loop roll failed ({type(e).__name__}: {e}); the calls it failed on ran without it")
return call(args) if call else None
def make_loop_temporal_wrapper(old_wrapper):
"""Build the loop-style model_function_wrapper. Installed INNERMOST (closest to
apply_model): prediction-modifying wrappers layered above it (dynashift, output
@@ -216,25 +210,19 @@ def make_loop_temporal_wrapper(old_wrapper):
state["logged"] = True
tail_note = f", {v_tail} guide frame(s) pinned" if v_tail else ""
log.once("temporal_style:loop", log.INFO, "FunPack Temporal style", f"loop: Mobius latent roll active (T={t_content}{tail_note})")
try:
new_c = dict(c)
if isinstance(new_c.get("denoise_mask"), torch.Tensor):
new_c["denoise_mask"] = _loop_roll_mask(new_c["denoise_mask"], frac, 1, tail=v_tail)
if isinstance(new_c.get("audio_denoise_mask"), torch.Tensor):
new_c["audio_denoise_mask"] = _loop_roll_mask(
new_c["audio_denoise_mask"], frac, 1,
tail=_loop_audio_tail_frames(new_c["audio_denoise_mask"]))
rolled = dict(args)
rolled["input"] = _loop_roll_packed(args["input"], shapes, frac, 1, tails=tails)
rolled["c"] = new_c
except Exception as e: # noqa: BLE001 -- only the roll is ours to catch
return _roll_failed(e, _call, args)
out = _call(rolled) # the model's errors are the model's: not caught here
try:
return _loop_roll_packed(out, shapes, frac, -1, tails=tails)
except Exception as e: # noqa: BLE001
_roll_failed(e, None, None)
return out
# A roll or unroll that fails is not caught here: the guard records it, drops the loop for the
# run and reruns the call without it. The model's own errors pass through the same guard.
new_c = dict(c)
if isinstance(new_c.get("denoise_mask"), torch.Tensor):
new_c["denoise_mask"] = _loop_roll_mask(new_c["denoise_mask"], frac, 1, tail=v_tail)
if isinstance(new_c.get("audio_denoise_mask"), torch.Tensor):
new_c["audio_denoise_mask"] = _loop_roll_mask(
new_c["audio_denoise_mask"], frac, 1,
tail=_loop_audio_tail_frames(new_c["audio_denoise_mask"]))
rolled = dict(args)
rolled["input"] = _loop_roll_packed(args["input"], shapes, frac, 1, tails=tails)
rolled["c"] = new_c
return _loop_roll_packed(_call(rolled), shapes, frac, -1, tails=tails)
return _loop_wrapper
@@ -195,3 +195,21 @@ def test_a_model_error_through_the_loop_is_not_blamed_on_the_roll_or_rerun(tiny_
with pytest.raises(RuntimeError, match="model blew up"):
p.model_options["model_function_wrapper"](apply_fn, args)
assert len(calls) == 1 and not dropped
def test_a_failing_roll_drops_the_loop_once_and_the_call_runs_unrolled(tiny_ltx, monkeypatch):
from modules.sampling.temporal_style import install, loop
monkeypatch.setattr(loop, "_loop_roll_packed", lambda *a, **k: 1 / 0)
p = tiny_ltx.patcher.clone()
dropped = patching.Dropped()
install(patching.GuardedPatcher(p, KEY, dropped), {"style": "loop"}, key=KEY)
seen = []
def apply_fn(x, t, **c):
seen.append(x)
return x
x = torch.arange(8.0).view(1, 1, 8, 1, 1)
bad = {"input": x, "timestep": torch.tensor([0.5]), "c": {"denoise_mask": torch.ones(1, 1, 8, 1, 1)}}
out = p.model_options["model_function_wrapper"](apply_fn, bad)
assert KEY in dropped and torch.equal(out, x) and torch.equal(seen[-1], x)