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:
co-authored by
Claude Sonnet 5.5
parent
6ac6acf612
commit
397f36014a
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user