Files
olivv-cs-ComfyUI-FunPack/tests/test_finite_sample_guard.py
T
digital-garbageandClaude Opus 5 9ea0be0713 feat: a non-finite latent stops the run where it happened
A chunk that samples to completion and returns NaN/Inf currently sails through
everything: the scene blend spreads it into the already-finished scenes, the VAE
decodes it, and the first thing that objects is ffmpeg's AAC encoder — "Input
contains (near) NaN/+-Inf", naming nothing that produced it, after the whole
montage has been paid for. The video half never objects at all: NaN through
astype(np.uint8) is undefined but silent, so it degrades with a warning.

_sample_chunk now checks the returned latent and raises with the run-phase label
("scene 2/3 · pass 1 of 2"), which stream, how many values and what dtype, plus
the causes worth testing in order — mismatched LoRA, a mis-inferred checkpoint
layout, a dtype the model cannot hold. It raises rather than warns because a
non-finite latent is unrecoverable and blending it forward destroys the scenes
that were still good. One isfinite() per chunk against a multi-second sample is
not a cost worth weighing; a latent the helper cannot read is ignored, never
fatal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:41:45 +03:00

63 lines
2.1 KiB
Python

"""A chunk that comes back non-finite stops the run where it happened.
Nothing downstream notices a NaN latent on its own: the scene blend spreads it into every
already-finished scene, the VAE decodes it, and the first thing to object is ffmpeg's AAC
encoder — naming nothing that produced it, after the whole montage has been paid for. The
video half never objects at all (NaN through astype(np.uint8) is undefined but silent).
"""
import sys
import types
from pathlib import Path
import pytest
import torch
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
sys.modules.setdefault("folder_paths", types.SimpleNamespace(models_dir=""))
from samplers import FunPackLTXAVSceneChainSampler # noqa: E402
@pytest.fixture
def node():
return FunPackLTXAVSceneChainSampler()
def test_a_clean_latent_passes_silently(node):
node._assert_finite_sample(torch.zeros(1, 4, 2, 8, 8))
def test_nan_in_the_video_stream_raises(node):
x = torch.zeros(1, 4, 2, 8, 8)
x[0, 0, 0, 0, 0] = float("nan")
with pytest.raises(RuntimeError) as e:
node._assert_finite_sample(x)
assert "non-finite latent" in str(e.value)
assert "video" in str(e.value)
def test_inf_counts_too(node):
"""+-Inf is what the AAC encoder actually names, and it is just as unrecoverable."""
x = torch.zeros(1, 4, 2, 8, 8)
x[0, 0, 0, 0, 0] = float("inf")
with pytest.raises(RuntimeError):
node._assert_finite_sample(x)
def test_the_message_says_how_many_and_which_stream(node):
x = torch.zeros(2, 4, 2, 4, 4)
x[0, 0, 0, 0, 0] = float("nan")
x[0, 0, 0, 0, 1] = float("nan")
with pytest.raises(RuntimeError) as e:
node._assert_finite_sample(x)
msg = str(e.value)
assert "2/" in msg # count of bad values
assert "LoRA" in msg # the first thing worth testing
assert str(x.dtype) in msg
def test_the_check_never_fails_the_run_on_its_own(node):
"""A latent shape the helper cannot read is not a reason to kill a good render."""
node._assert_finite_sample("not a tensor")
node._assert_finite_sample(None)