fix(v5): block repeat and shadow negative sample with the model compiler off

Both replace what a block allocates, which crashes aimdo's compiler (seen on a
rental with v4's shadow negative). Attached to the model, so any sampler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-09-27 17:59:18 +03:00
co-authored by Claude Opus 5.5
parent 6838e3b997
commit 3cd0c1e00a
4 changed files with 66 additions and 0 deletions
+36
View File
@@ -183,3 +183,39 @@ def target_rows(mod_segments, seq_len, device, stream):
if mask is not None:
return mask
return None
def without_compiler(patcher, key: str) -> None:
"""Run this model's sampling with ComfyUI's model compiler off.
The compiler (aimdo malloc-graph) records what each block allocates and
assumes it never changes. A hook that REPLACES a block's forward with one
allocating differently -- a repeated block, a second attention stream --
crashes it with "aimdo memory compile error" (v4, on rentals: block repeat,
REINS injection, shadow negative). Comfy-Org's own fix is
--disable-comfy-compiler; it is read live on every scope, so switching it
for the one sampling call is enough and the next run keeps the compiler.
Attached to the MODEL as an OUTER_SAMPLE wrapper, so it holds for any
sampler, not only FunPack's.
"""
from comfy.patcher_extension import WrappersMP
def outer(executor, *args, **kwargs):
import comfy.cli_args
cli = comfy.cli_args.args
had = hasattr(cli, "disable_comfy_compiler")
prior = getattr(cli, "disable_comfy_compiler", None)
cli.disable_comfy_compiler = True
try:
return executor(*args, **kwargs)
finally:
if had:
cli.disable_comfy_compiler = prior
else:
try:
del cli.disable_comfy_compiler
except AttributeError:
pass
patcher.add_wrapper_with_key(WrappersMP.OUTER_SAMPLE, key, outer)
+26
View File
@@ -100,3 +100,29 @@ def test_parse_blocks_reports_what_it_ignores(spec, expect, problems):
from core import dit_hooks
got, said = dit_hooks.parse_blocks(spec, 4)
assert got == expect and len(said) == problems
def test_without_compiler_switches_it_off_for_the_sample_and_back(tiny_h3):
import comfy.cli_args
from comfy.patcher_extension import WrappersMP
from core import dit_hooks
p = tiny_h3.patcher.clone()
dit_hooks.without_compiler(p, "funpack.x")
(outer,), = p.wrappers[WrappersMP.OUTER_SAMPLE].values()
had = hasattr(comfy.cli_args.args, "disable_comfy_compiler")
prior = getattr(comfy.cli_args.args, "disable_comfy_compiler", None)
seen = outer(lambda: comfy.cli_args.args.disable_comfy_compiler)
assert seen is True
assert hasattr(comfy.cli_args.args, "disable_comfy_compiler") == had
assert getattr(comfy.cli_args.args, "disable_comfy_compiler", None) == prior
@pytest.mark.parametrize("module,settings", [
("block_repeat", {"enabled": True, "blocks": "1"}),
("shadow_negative", {"enabled": True}),
])
def test_block_replacing_modules_turn_the_compiler_off(tiny_h3, module, settings):
from comfy.patcher_extension import WrappersMP
from modules.sampling.modifiers.nodes import FunPackLoadModifiers
patched, _ = FunPackLoadModifiers.execute(tiny_h3.patcher, {module: settings}).result
assert patched.wrappers.get(WrappersMP.OUTER_SAMPLE), f"{module} left the compiler on"
@@ -171,6 +171,8 @@ def install(patcher, values, key):
dit_hooks.add_block_hook(patcher, key, i, repeat)
what = f"blocks {','.join(map(str, blocks))} run {times + 1}x each"
# Replaces what the hooked blocks allocate: see dit_hooks.without_compiler.
dit_hooks.without_compiler(patcher, key)
if video_only:
what += ", video only"
if steps:
@@ -153,6 +153,8 @@ def install(patcher, values, key):
for i, block in enumerate(dm.blocks):
dit_hooks.add_block_hook(patcher, key, i, make(block))
# Replaces what the hooked blocks allocate: see dit_hooks.without_compiler.
dit_hooks.without_compiler(patcher, key)
note = f"picture {video_scale:g}, sound {audio_scale:g}, steps {start:.2f}-{end:.2f}"
if taken:
note += (f"; leaves block(s) {','.join(map(str, taken))} to the feature already there"