Small side plan: temporary BOOLEAN input on LoRACombinationGenerator that
routes progress tracking to combo_progress_rerun.json, enabling a full
re-run of the 400-combo collection to backfill per-prefix decisions
once Phase A of the estimator plan lands. To be reverted after use.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Task-by-task TDD plan for the case-based retrieval estimator. Covers
per-prefix decision capture (Phase A), feature extractor + index builder,
LoRAEstimator runtime, and the ComfyUI node surface.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
k-NN retrieval over HF community cache, emits tuner_data for the
existing optimizer replay path. Per-prefix instrumentation lands
first, dataset gets re-collected, estimator ships with both global
and per-prefix retrieval.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Threads the detected architecture family (zimage, wan, flux, qwen_image,
etc.) into the config payload so the 'dit' arch_preset bucket can be
filtered by sub-family downstream (estimator training, targeted cache
reuse). Existing 400 configs on HF were backfilled with zimage in a
separate dataset commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
target_key can be a nested tuple like ("key", (dim, start, size)).
JSON round-trips flatten this to ["key", [dim, start, size]].
The shallow tuple() conversion only fixed the outer level, leaving
inner lists unhashable — causing TypeError in single_lora_keys.add().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: ComfyUI auto-randomizes INT inputs named "seed", generating
a new seed each queue run. Progress was keyed by seed, so each new seed
started from scratch.
- Rename "seed" input to "shuffle_order" to prevent auto-randomization
- Store completed combo hashes in a flat set instead of per-seed keys
(combo hashes are already order-independent, so progress persists
regardless of shuffle order)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logs seed, pool size, combo count, completed count, progress file path
on entry, confirms save success/failure, and logs returned combo info.
Improves error message for insufficient LoRAs to show filtered list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Empty filter no longer allowed — there's no point merging across
incompatible architectures. Users must specify prefixes like
'zit/,zib/' to combine compatible LoRA pools.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Filters the LoRA pool by path prefix (e.g. "zimage/" or "sdxl/")
so users can generate combos only for compatible architectures.
Empty string includes all LoRAs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Design doc requires logging when a LoRA file is missing and a combo
is skipped. Follows existing project convention of logging.warning
with the [LoRA ...] prefix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LoRACombinationGenerator previously raised RuntimeError when all combos
were done, showing a red error in ComfyUI's UI. Now uses ComfyUI's
InterruptProcessingException which sends an "execution_interrupted"
event — clean yellow stop instead of a scary error traceback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The missing-LoRA handling used unbounded recursion via
`return self.get_next_combo(...)` which would hit Python's recursion
limit of 1000 when many LoRAs are deleted. Replaced with an iterative
while-loop that skips deleted-LoRA combos without growing the stack.
Also removed redundant `OUTPUT_NODE = False` (it's the default).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add INPUT_TYPES, RETURN_TYPES, IS_CHANGED, __init__, and get_next_combo
methods so the class works as a ComfyUI node. Register in
NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS. Add 3 tests for the
node interface (input fields, return types, IS_CHANGED returns NaN).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
combo_size now accepts ComfyUI dropdown strings ("2", "3", "2_and_3")
instead of integers. Progress JSON keys use "seed_42" format instead
of bare "42" for clarity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements core static methods for generating, shuffling, hashing, and
tracking LoRA combinations (pairs/triples) with deterministic seeding
and JSON-based progress persistence. Includes 11 TDD tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node that exhaustively generates all 2/3-way LoRA combinations for
AutoTuner dataset collection. Uses seed-based deterministic shuffle
with lightweight completion tracking.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The doc previously stated voice identity enters "exclusively" through
cross-attention, which was an inference not supported by any source.
Replace with the actual quote from the ACE-Step 1.5 paper and clarify
that voice info propagates through self-attention/MLP after injection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use actual node names (LoRA Stack, LoRA Optimizer, LoRA AutoTuner)
instead of nonexistent "ZImage LoRA Merger node". Broaden recommendations
to cover all ACE-Step LoRA merges, not just vocal + music.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add dedicated architecture support for ACE-Step music generation model LoRAs
with optimizations informed by the v1.5 hybrid attention architecture — cross-
attention is the primary voice-identity pathway (always global, attending to
timbre+lyric+text conditioning), while self-attention alternates sliding window
(local timbre) and global GQA (long-range structure).
Changes:
- New `acestep_dit` preset: wider orthogonal band (0.30), higher TIES threshold
(0.35), full magnitude preservation (floor 1.0) — biases toward SLERP over
TIES to prevent voice signal trimming in cross-attn layers
- Fix `to_out` → `o_proj` normalization bug (was producing `out_proj`)
- Add v1.0 detection and normalization (transformer_blocks, speaker_embedder,
lyric_encoder mapped to v1.5 canonical form)
- Refine WAN detection to exclude transformer_blocks (prevents v1.0 misdetect)
- Add `acestep_dit` to all architecture_preset UI dropdowns
- 25 new tests (detection, normalization, preset)
- Technical reference doc with architecture analysis and design rationale
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The SDXL normalizer's .replace('.', '_') was mangling LoRA weight
suffixes (.lora_down.weight → _lora_down_weight), breaking prefix
extraction for diffusers-format keys. Added _split_lora_suffix to
preserve suffixes. Also split SD 1.5 from SDXL detection so single-TE
LoRAs normalize to lora_te_ instead of lora_te1_.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single-LoRA prefixes produce identical patches regardless of merge config
(forced to weighted_sum, early return at _merge_diffs). Cache results keyed
by (label_prefix, auto_strength) to skip diff recomputation for candidates
2+ sharing the same auto_strength value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Patch dict keys and single_lora_keys both store target_key values
from _group_target_key(). When target_key is a tuple (weight slices),
the lambda extracted k[0] (a string) which never matched the tuple
in the set. Direct comparison handles both string and tuple keys.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single-LoRA patches bypass all merge strategies (early return at line
2868). Score them once on the first candidate and merge as baseline
for subsequent candidates. All composite_score components (norm_cv,
effective_rank, sparsity) are scale-invariant, so auto_strength
differences between candidates don't affect correctness.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
A LoRA file uses one key format consistently. Caching the winning
format index (0-3) after first detection avoids up to 6 wasted
dict lookups per prefix on subsequent calls. Cache miss on stale
entries is safe — falls through to full cascade.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Moving fp16/bf16 tensors to score_device then converting to fp32
halves the PCIe transfer bandwidth vs converting first. For ~240
LoRAAdapter patches per candidate, saves meaningful transfer time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory diagnostics on every iteration adds overhead without value.
First and last iteration still show whether memory leaks occur.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace full gc.collect() with gc.collect(0) (youngest generation only,
~10x faster). Remove torch.cuda.empty_cache() — CUDA allocator handles
block reuse, forced sync is pure overhead. Post-loop cleanup at line
9354-9356 still does full gc.collect() + empty_cache().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New page covers how it works, privacy model, setup, file structure,
score-based replacement, cache hit log examples, version compatibility,
and troubleshooting. Wired into sidebar and home page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>