Files
olivv-cs-ComfyUI-FunPack/tests/test_conditioning_payload_length.py
T
digital-garbageandClaude Opus 5 8e3c707c81 fix(sampler): a wired reference conditioning is not a second scene
The Chain Sampler reads one conditioning entry per SCENE
(scene_conditionings = positive[:max_scenes]). When Studio stopped discarding
the entries past the first of a wired CONDITIONING — the fix for a reference
surviving while the prompt did not — those entries reached the sampler
untagged, and the r2v reference block was counted as a scene of its own. The
run sampled the shot twice, which from outside is "the model ran a second pass
for no reason".

Studio tags every pass-through entry as a companion; the sampler excludes
companions when counting scenes and hands them to every scene instead, so they
still reach the model. Both halves of the original bug stay fixed: nothing is
dropped, and nothing is mistaken for a scene.

Also fixes the liked-conditioning blend, which was the last payload path still
doing a raw lerp: _v2_shape_compatible ignores sequence length by design, so a
direction learned at 586 positions met a 494-position prompt, threw, and took
the whole pull toward what the user rated well with it. Routed through
_v2_payload_like like every other payload.

Adds stage timers for the nine learning steps the first pass did not cover —
55% of a 26.9s run was accounted for, and the rest was unnamed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:06:52 +03:00

94 lines
3.7 KiB
Python

"""A learned conditioning survives a prompt whose length has changed.
`_v2_shape_compatible` checks the channel width and the batch, deliberately not the sequence
length. On LTX that was fine — the encoder pads to a fixed length, so a stored conditioning
always matched. Qwen does not pad (`pad_to_max_length=False`), so on H3 the length changes
with every prompt edit: a direction learned at 499 positions met a conditioning of 492 and
the blend threw, taking the whole learned direction down with it.
`_resize_conditioning_sequence_like` was already in the file for exactly this, with no
callers.
"""
import sys
import types
import pytest
import torch
sys.path.insert(0, ".")
sys.modules.setdefault("folder_paths", types.SimpleNamespace(models_dir=""))
@pytest.fixture
def studio(monkeypatch):
import conditioning
monkeypatch.setattr(conditioning, "_log",
types.SimpleNamespace(failed=lambda *a, **k: None,
note_on_change=lambda *a, **k: None),
raising=False)
return conditioning.FunPackVideoRefinerV2.__new__(conditioning.FunPackVideoRefinerV2)
def _payload(seq, dim=8, value=1.0):
import conditioning
return conditioning.tensor_to_serializable(torch.full((1, seq, dim), value))
def test_a_payload_from_a_longer_prompt_still_blends(studio):
"""499 -> 492 is one prompt edit. It used to throw and lose the direction entirely."""
mixed = torch.zeros(1, 492, 8)
out = studio._v2_apply_conditioning_payload(mixed, _payload(499), 0.5)
assert out.shape == mixed.shape
assert torch.allclose(out, torch.full_like(mixed, 0.5))
def test_a_payload_from_a_shorter_prompt_still_blends(studio):
mixed = torch.zeros(1, 528, 8)
out = studio._v2_apply_conditioning_payload(mixed, _payload(492), 0.5)
assert out.shape == mixed.shape
assert torch.allclose(out, torch.full_like(mixed, 0.5))
def test_repel_survives_the_same_mismatch(studio):
mixed = torch.zeros(1, 492, 8)
out = studio._v2_repel_conditioning_payload(mixed, _payload(528, value=1.0), 0.5)
assert out.shape == mixed.shape
assert torch.allclose(out, torch.full_like(mixed, -0.5))
def test_an_exact_match_is_not_resampled(studio):
"""The common case — same prompt, re-rolled — must stay bit-exact."""
mixed = torch.zeros(1, 64, 8)
out = studio._v2_apply_conditioning_payload(mixed, _payload(64, value=2.0), 1.0)
assert torch.equal(out, torch.full_like(mixed, 2.0))
def test_a_different_channel_width_is_still_refused(studio):
"""Resampling is along the SEQUENCE. A different model's conditioning is not blendable."""
mixed = torch.zeros(1, 64, 8)
out = studio._v2_apply_conditioning_payload(mixed, _payload(64, dim=16), 0.5)
assert torch.equal(out, mixed)
def test_a_broken_payload_leaves_the_conditioning_alone(studio):
mixed = torch.zeros(1, 64, 8)
assert torch.equal(studio._v2_apply_conditioning_payload(mixed, {"shape": [1, 64, 8]}, 0.5),
mixed)
def test_the_liked_blend_survives_a_prompt_whose_length_changed(monkeypatch):
"""The Absolute/liked pull was the one payload path still doing a raw lerp. On H3 the
encoder does not pad, so a direction learned at 586 positions met a 494-position prompt
and the blend threw — silently dropping the pull toward what the user rated well."""
import torch
from conditioning import FunPackVideoRefinerV2, tensor_to_serializable
refiner = FunPackVideoRefinerV2()
mixed = torch.zeros(1, 494, 16)
liked = torch.ones(1, 586, 16)
out = refiner._v2_payload_like(tensor_to_serializable(liked), mixed)
assert out is not None
assert list(out.shape) == [1, 494, 16]
assert float(mixed.lerp(out, 0.5).mean()) > 0.0