It is not an argument of the sampler node, but it decides whether two
runs can be identical at all: comfy_kitchen_int8 costs ~10% of the
picture between runs with everything else fixed (quantisation flips
buckets, which shows up as eyes and mouth changing while the motion
stays put), and pytorch is bit-exact. An A/B unknowingly spanning a
backend switch compares two different machines.
The override function carries its own name now, so the fingerprint can
report which backend was live rather than "something overrode it".
fp16 and fp8_e4m3fn_fast both wreck H3's audio before anything looks wrong,
and the loader's own notes go into a status string the Editor never renders —
so the only symptom was a bad soundtrack. One console line, once per run, on
H3 only. Names the fix in the first sentence, which is the half that survives
the console trim.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Validated on the rental and it does not earn its place: 3.5 minutes against 1
for the same clip, worse picture, garbage audio. Some of that was mine — no
weight prefetch across the 50 blocks, a host-offloaded K/V cache, and an extra
commit forward per chunk — but fixing those only buys parity on speed, and the
technique's real win is unbounded length, which H3 already covers natively at
the lengths in use here.
Gone: h3_causal.py, the four h3_causal_* sampler widgets and their Engine
Settings rows, `chunk_causal` on the diffusion loader, and five test files.
KEPT, because it is not about this at all: the LoRA key RENAME table. Stripping
a wrapper head leaves `blocks.0...`, comfy's key map has no bare form, and the
file silently applies nothing — 532 keys, zero applied, no error. That was
breaking any PEFT-style adapter named this way, not just the one that found it.
Its tests moved to test_lora_shape_mismatch.py alongside the MATCHED NOTHING
diagnostics.
Two INPUT_TYPES widgets go with this (h3_causal_* on the sampler, chunk_causal
on the loader). A saved workflow carrying them loads fine — both were optional
and are matched by name — but the values are ignored from here.
Tests: 1700 python, 68 JS.
The refusal asserted a cause it had never checked — "switch on chunk_causal" —
which was wrong, and sent you looking at a switch that was already on.
Three changes, none of them a guess:
- build_session no longer refuses a stock H3. Re-classing adds no parameter,
renames no key and costs nothing, and with no cache passed the model is
bit-identical to stock — so a mode that is already switched ON has no
reason to refuse itself over a switch on another node. It does the work.
- when it does that, it says so, naming the loader as the end that did not
take. That is the line that tells us whether `chunk_causal` is off or its
value never reaches the node.
- the loader PRINTS its chunk-causal outcome. It only ever went into the
`status` string, which the Editor's fixed graph shows nowhere, so a
"REQUESTED BUT NOT APPLIED" was invisible and the sampler downstream had
nothing to go on but a guess.
If make_causal genuinely cannot run, the message now names the diffusion
model's actual class instead of asserting what the user did.
Tests: 1804 python (+3), 68 JS.
Every message about loading the chunk-causal adapter named FunPack Apply LoRA
Weights. That is the wrong node: it is the optional stack producer for
prompt-specific trained strengths. FunPack LoRA Loader loads AND applies from
its own file list, and always has — so the instruction sent the user to add a
node they do not need.
Its own description read the same way ("Loads LoRAs from a FunPack LoRA
stack"), which is where the confusion came from. Both it and the docstring now
say the stack is the narrow case, not the way in.
Tests: 1767 passed (+5).
Both halves now go through nodes we already own, so nothing third-party has to
be installed for the feature to exist.
The diffusion loader gains `chunk_causal`, which RE-CLASSES an already-loaded
H3 in place. That is what removes the need for a separate loader entirely: the
alternative is reimplementing ComfyUI's whole diffusion-model load path so the
adapter can be attached before the ModelPatcher exists and its memory ledger
counts it. Re-classing adds no parameter, renames no key and copies nothing —
only which forward runs changes — so the ledger and offload accounting are
untouched and the switch is idempotent.
The adapter is then an ORDINARY LoRA through FunPack Apply LoRA Weights. Our
loader already scores prefix variants against the model's real key map; it just
did not know this one. RAVEN's keys are `base_model.model.dit.<path>`, so
stripping only `base_model.model.` left `dit.` in front of every key and the
whole file matched nothing, silently. One entry in LORA_KEY_PREFIXES fixes it.
A ComfyUI too old for H3, or a non-H3 model, declines with a reason rather than
raising — one optional switch must not turn the loader into a dead node.
Tests: 1751 passed.
FunPack owns its loading pipeline, so the causal lane must not need a
third-party loader node in the graph. `raven_lora` on FunPack Diffusion Model
Loader turns an ordinary H3 checkpoint into the chunk-causal DiT with the
adapter attached. Same checkpoint either way — it is a LoRA, there is no
separate RAVEN model file, and every message now says so.
The weight load itself is DELEGATED to the RAVEN package rather than
reimplemented, and not for convenience: the adapter is an FP32 activation
residual registered on the base Linear leaves and must be attached before the
ModelPatcher is built. ModelPatcher.model_size() memoises, so an adapter
attached afterwards is invisible to Comfy's memory ledger and never moved by
partial CPU offload — the model would report itself smaller than it is and the
residual would end up on the wrong device.
Selecting a LoRA when the causal class is unavailable RAISES with the fix in
the message, rather than quietly loading a plain model: a model without the
adapter would leave the causal lane reading an attention pattern nothing was
trained for, which reads as a quality problem with no visible cause.
Strength is fixed at 1.0 — the adapter is what teaches the chunked attention
pattern, so "partly on" is not a useful state.
Tests: 1730 passed (+12).
Reverts 6bf533e, 78c9625, 4bacc9c, 993e5e4, ae337b5 and c4d8018 —
gguf_support.py, loaders.py and their tests are byte-identical to
6f04642. Nothing else is touched.
The quantized path loaded fast and gave the sampler a model it could not
run: shape [21504, 5376] against an input of 22848x5376, one 1344-token
unit apart. Expanding at load is slow and works.
Dequantizing produces float32 — four times the width the weight ends up
at. Every tensor was collected before any was cast, so the peak was the
whole checkpoint in float32 plus a copy of each: tens of GB of transient
host allocation on a video model, enough to take the machine down rather
than just the load. ThreadPoolExecutor.map made it worse by submitting
everything at once and buffering results in order.
Now cast-and-release per tensor, with a fixed in-flight window. Peak is
the finished model plus a handful of tensors. Pool capped at 4.
Also prints the path and tensor count before the wait, so a load killed
part way leaves evidence of which backend it chose.
A bare "'dict' object has no attribute 'startswith'" names neither the
step nor the file. Each stage now labels itself in the re-raise. The
ComfyUI-GGUF architecture override is opt-in, not default.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Attention and SLA are applied to a clone of a one-entry weight cache
keyed on file identity + dtypes, so a sparsity change skips the disk.
GGUF dequantization now runs across threads instead of serially.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A .gguf renamed .safetensors reached the safetensors parser (UTF-8 decode
error); content now decides. ComfyUI-GGUF's loader uses relative imports, so
its directory is mounted as a package instead of loaded file-by-file.
Core lists no .gguf, so the files were invisible. Backend is whichever is
installed: ComfyUI-GGUF keeps weights quantized, the gguf package
dequantizes at load. Family detection reads GGUF containers too.
It is a toggle now, not a value in the list: SLA takes H3's packed self-attention
and the chosen backend takes the text refiner, masked calls and dense-last steps.
When SLA does not take, the chosen backend is still installed on its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Picked in the diffusion model loader's attention list, not wired as a node.
Six settings folded under Advanced; offered only where CUDA and Triton run it
and ignored on any model that is not H3. Kernel vendored from LightX2V.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Quantization + a per-model attention backend read from ComfyUI's own registry, so only
installed ones are offered. The LoRA loader now takes its own list and scores wrapper
prefixes against the model's key map, instead of applying a diffusers LoRA as a silent no-op.