Files
olivv-cs-ComfyUI-FunPack/modules/sampling/output_guidance/tests/test_output_guidance.py
T

88 lines
3.0 KiB
Python

"""Taste guidance through the loader, on H3's latent."""
import pytest
import torch
@pytest.fixture(autouse=True)
def _needs_comfy(comfyui):
"""Imports comfy."""
@pytest.fixture(autouse=True)
def root(tmp_path, monkeypatch):
from modules.system.taste import store
monkeypatch.setattr(store, "ROOT", tmp_path / "taste")
def _to(index, steps=4):
sched = torch.linspace(1.0, 0.0, steps + 1)
return {"sample_sigmas": sched, "sigmas": sched[index:index + 1]}
def _load(tiny, module):
from comfy.patcher_extension import WrappersMP
from modules.sampling.modifiers.nodes import FunPackLoadModifiers
patched, _ = FunPackLoadModifiers.execute(tiny.patcher, {
"taste": {"key": "fox"}, module: {"enabled": True, "strength": 0.05}}).result
wrap = [w for ws in patched.wrappers[WrappersMP.APPLY_MODEL].values() for w in ws][-1]
outer = [w for ws in patched.wrappers[WrappersMP.OUTER_SAMPLE].values() for w in ws]
return wrap, outer
def _teach(kind, names):
from modules.system.taste import store, value
torch.manual_seed(0)
liked = torch.randn(value.DIM)
for i in range(12):
sign = 1.0 if i % 2 else -1.0
store.capture("fox", kind, {n: sign * liked + 0.3 * torch.randn(value.DIM) for n in names},
prompt_id=f"t{i}")
store.rate(f"t{i}", "liked" if sign > 0 else "disliked")
def _t(index, steps=4):
return torch.linspace(1.0, 0.0, steps + 1)[index:index + 1]
def _x0():
from conftest import packed_av
torch.manual_seed(3)
return packed_av(torch.randn(1, 4, 3, 8, 8), torch.randn(1, 8, 5))
def _start(outer):
for o in outer:
o(lambda: None)
def test_late_guidance_banks_the_final_picture_and_steers_only_late(tiny_h3, monkeypatch):
from modules.system.taste import store
monkeypatch.setattr(store, "current_prompt_id", lambda: "run-1")
x0, shapes = _x0()
wrap, outer = _load(tiny_h3, "output_guidance")
for o in outer:
o(lambda: wrap(lambda *a, **k: x0, x0, None, None, None, None, _to(3), latent_shapes=shapes))
assert store.rate("run-1", "liked")["recorded"] == ["x0_final"]
_teach("x0_final", ["final"])
wrap, outer = _load(tiny_h3, "output_guidance")
_start(outer)
from conftest import unpacked
seen = []
def model(x, *a, **k):
seen.append(x)
return x0
def call(i):
return wrap(model, x0, _t(i), None, None, None, _to(i), latent_shapes=shapes)
assert call(1) is x0 and call(2) is x0 # the model's own answer, never the edit
assert torch.equal(seen[0], x0) # step 1: no push yet
assert call(3) is x0
push = seen[-1] - x0 # the late step's input carries it
assert not torch.allclose(unpacked(push, shapes)[0], torch.zeros_like(unpacked(x0, shapes)[0]))
assert torch.equal(unpacked(push, shapes)[1], torch.zeros_like(unpacked(x0, shapes)[1])) # sound untouched
assert len(seen) == 3 and torch.equal(seen[1], x0) # step 2's input had no push