[FunPack] K/V conditioning patch: Inactive | Non-LTX model loaded
[FunPack] Chunk cache: Active | 22 chunks, 4 steps each, consistency
`feature(tag, name, active, reason)` is the shape, and `skipped` renders
identically — a deliberate skip and an inactive feature are the same fact and
should not have two spellings. `failed` becomes `Name: Failed | <effect>`,
keeping the effect rather than the exception text: "that explains what I am
looking at" is the half worth the line, and an exception message rarely says it.
Everything funpack_log prints is now cut at the first sentence end, so the call
sites keep their reasoning without spending it on every run. SENTENCE ENDS ONLY
— an em-dash introduces the reason as often as the essay, and a renderer that
guesses wrong eats the one part of the line worth reading. A single long
sentence is hard-trimmed instead of left to run.
FUNPACK_LOG=verbose (or set_verbose) prints the full text, exception included.
Converted to the new shape: the chunk cache, region locks, interior pins, the
taste push, the token-refiner edit, AdaLN gains, rating-learned gains, phrase
emphasis, the K/V conditioning patch, the scene split, Bounded Attention,
per-block LoRA weights, LTX enhancements. The rest of the log gets the trim for
free and keeps its own phrasing until converted.
Tests: 1815 python (+9), 68 JS.
94 lines
3.7 KiB
Python
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, feature=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
|