Files
olivv-cs-ComfyUI-FunPack/modules/loaders/tests/test_attention_path.py
T
digital-garbageandClaude Opus 5 e272a3b443 loaders: three the attacker found, and the silence around one of them
A backend that carries a zero-copy container path lost it: wrap_attn offers
that path only when the OVERRIDE has the attribute, so dropping it selects
the backend, computes the right answer, and never uses the thing the backend
exists for. ComfyUI forwards it in set_model_optimized_attention; now so do we.

An unknown encoder family fell back to stable_diffusion and reported success.
A family mismatch in this project reads as an unrelated fault, so it refuses
and lists what this ComfyUI actually knows.

fp16 accumulation is one flag for the whole interpreter, so a per-node
checkbox cannot mean "this model": a second loader changes the first model's
maths, and loader caching outlives the run. It cannot be scoped, so it is at
least no longer silent -- every transition says what it really covers. The
honest fix is to move it to a machine-level setting.

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

157 lines
6.5 KiB
Python

"""The attention override, carried all the way to a real attention call.
This is the seam the loader is most likely to get silently wrong: a bad override
loads perfectly and then fails at the first attention call, which is sampling.
None of it needs model weights, so there is no excuse for not running it.
"""
import pytest
@pytest.fixture(scope="module")
def torch_and_attention(comfyui):
import torch
import comfy.ldm.modules.attention as attention
return torch, attention
@pytest.fixture
def qkv(torch_and_attention):
torch, _ = torch_and_attention
torch.manual_seed(0)
heads, dim = 2, 16
shape = (1, 8, heads * dim)
return torch.randn(shape), torch.randn(shape), torch.randn(shape), heads
def test_every_backend_this_machine_offers_survives_the_override(torch_and_attention, qkv):
"""The override is handed the WRAPPED function plus the wrapper's own kwargs
(`_inside_attn_wrapper` among them). If the unwrapped backend chokes on any
of that, choosing a backend breaks sampling and nothing else."""
torch, attention = torch_and_attention
from modules.loaders.common import attention_override
q, k, v, heads = qkv
reference = attention.get_attention_function("pytorch")(q, k, v, heads=heads)
for name in sorted(attention.REGISTERED_ATTENTION_FUNCTIONS):
override = attention_override(name)
assert override is not None, f"{name} is registered but produced no override"
out = override(attention.get_attention_function(name), q, k, v, heads=heads,
transformer_options={}, _inside_attn_wrapper=True)
assert out.shape == reference.shape, f"{name} changed the output shape"
def test_an_installed_override_is_actually_reached_by_comfys_dispatch(torch_and_attention, qkv):
"""Proves the KEY the loader writes is the key comfy reads. A typo here is
invisible: the model loads, sampling runs, and the chosen backend is simply
never used."""
torch, attention = torch_and_attention
from modules.loaders.common import attention_override
q, k, v, heads = qkv
inner = attention_override("pytorch")
reached = []
def spy(func, *args, **kwargs):
reached.append(True)
return inner(func, *args, **kwargs)
# Exactly the dict the diffusion loader writes into model_options.
transformer_options = {"optimized_attention_override": spy}
out = attention.optimized_attention(q, k, v, heads=heads,
transformer_options=transformer_options)
assert reached, "the override was installed where nothing reads it"
plain = attention.optimized_attention(q, k, v, heads=heads, transformer_options={})
assert torch.allclose(out, plain, atol=1e-5), (
"routing through the override changed the result")
def test_the_loader_writes_the_override_where_dispatch_looks(comfyui, monkeypatch):
"""The loader's own installation step, without loading a model: the key name
and its nesting under model_options are what this pins."""
import comfy.sd
import comfy.utils
import folder_paths
from comfy.ldm.modules import attention as attn
from modules.loaders.diffusion_model import nodes
class FakeModel:
def __init__(self):
self.model_options = {}
def wrapped(*args, **kwargs):
raise AssertionError("called the wrapped function, re-entering wrap_attn")
wrapped.__wrapped__ = lambda *a, **k: "attended"
monkeypatch.setattr(attn, "get_attention_function", lambda name, default: wrapped)
monkeypatch.setattr(folder_paths, "get_full_path_or_raise", lambda kind, name: f"/fake/{name}")
monkeypatch.setattr(comfy.utils, "load_torch_file", lambda p, **kw: ({}, None))
monkeypatch.setattr(comfy.sd, "load_diffusion_model_state_dict",
lambda sd, **kw: FakeModel())
out = nodes.FunPackDiffusionModelLoader.execute(
model_name="m.safetensors", weight_dtype="default",
compute_dtype="default", attention="pytorch")
model = out.result[0]
installed = model.model_options["transformer_options"]["optimized_attention_override"]
assert callable(installed)
assert installed(wrapped, "q") == "attended"
def test_a_model_comfy_could_not_identify_fails_at_the_loader(comfyui, monkeypatch):
"""comfy returns None instead of raising, and a None model reaches the
sampler before anything complains."""
import comfy.sd
import comfy.utils
import folder_paths
from modules.loaders.diffusion_model import nodes
monkeypatch.setattr(folder_paths, "get_full_path_or_raise", lambda kind, name: f"/fake/{name}")
monkeypatch.setattr(comfy.utils, "load_torch_file", lambda p, **kw: ({}, None))
monkeypatch.setattr(comfy.sd, "load_diffusion_model_state_dict", lambda sd, **kw: None)
with pytest.raises(RuntimeError, match="Could not detect a diffusion model"):
nodes.FunPackDiffusionModelLoader.execute(
model_name="actually_a_vae.safetensors", weight_dtype="default",
compute_dtype="default", attention="default")
def test_a_backends_zero_copy_path_is_forwarded_to_the_override(comfyui, monkeypatch):
"""`wrap_attn` offers the container fast path only when the OVERRIDE carries
`container_function`. Dropping it is invisible -- the backend is selected and
the maths is right, while the path it exists for is never used. ComfyUI's own
set_model_optimized_attention forwards it for exactly this reason."""
from comfy.ldm.modules import attention as attn
from modules.loaders import common
def backend(*args, **kwargs):
raise AssertionError("took the slow path")
backend.__wrapped__ = lambda *a, **k: "slow"
backend.container_function = lambda *a, **k: "fast"
monkeypatch.setattr(attn, "get_attention_function", lambda name, default: backend)
override = common.attention_override("kitchen")
assert getattr(override, "container_function", None) is not None
assert override.container_function() == "fast"
def test_a_backend_without_a_container_path_does_not_grow_one(comfyui, monkeypatch):
from comfy.ldm.modules import attention as attn
from modules.loaders import common
def backend(*args, **kwargs):
return "x"
backend.__wrapped__ = lambda *a, **k: "x"
monkeypatch.setattr(attn, "get_attention_function", lambda name, default: backend)
override = common.attention_override("plain")
# hasattr is what wrap_attn tests, so an attribute set to None would still
# divert every call into a container path that does not exist.
assert not hasattr(override, "container_function")