Commit Graph
35 Commits
Author SHA1 Message Date
Ethanfel bc404aa2fc feat: remove gray-out effect — enabled toggle is sufficient visual indicator 2026-04-09 12:38:29 +02:00
Ethanfel 55c24d784f fix: replace w.draw gray-out with onDrawForeground overlay — draw override hid widgets instead of graying them 2026-04-09 12:34:20 +02:00
Ethanfel 66a52dbd6c fix: explicit setDirty in Remove when count=1 — don't rely on enabled setter side-effect 2026-04-09 12:23:05 +02:00
EthanfelandClaude Sonnet 4.6 25588707a5 fix: convert graph_mouse to node-local Y in getExtraMenuOptions for correct slot detection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 12:13:59 +02:00
Ethanfel f80abf7a65 fix: remove redundant updateVisibility/setDirty in Remove callback — countWidget setter already triggers them 2026-04-09 12:08:06 +02:00
EthanfelandClaude Sonnet 4.6 c46ce74e4c feat: add right-click remove and move-up to LoRAStackDynamic
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 12:05:23 +02:00
EthanfelandClaude Sonnet 4.6 be87ab020f fix: setSlotGrayed idempotency guard, restore cleanup, bound gray loop to visible slots
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 12:03:37 +02:00
Ethanfel b244c62fb5 feat: gray out disabled LoRA slots in LoRAStackDynamic 2026-04-09 12:01:02 +02:00
EthanfelandClaude Opus 4.6 ce0a756a86 fix: show LoRA filename instead of full path in stack nodes
Override _displayValue getter on combo widgets to strip directory
paths, showing only the filename for readability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 12:13:44 +01:00
EthanfelandClaude Opus 4.6 3342ca2264 fix: space created nodes by type to prevent overlap
Use running Y offset with per-type spacing: 150px for LoraLoader
nodes, 300px for LoRAStackDynamic nodes. Prevents solo loader
nodes from overlapping each other.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:00:53 +01:00
EthanfelandClaude Opus 4.6 e45ee8a44b fix: let ComfyUI handle report and heatmap display natively
Remove custom DOM widget for report text — rely on ComfyUI's
built-in ui.text and ui.images rendering instead. JS extension
now only handles node creation/cleanup logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:49:20 +01:00
EthanfelandClaude Opus 4.6 dd42043766 feat: Compatibility Analyzer solo LoRA nodes, create_nodes toggle, and settings cleanup
- Solo LoRAs now create LoraLoader/LoraLoaderModelOnly nodes (based on CLIP)
- Merge groups continue creating LoRAStackDynamic nodes
- Use original strengths instead of auto-strengths for created nodes
- Add create_nodes toggle to disable automatic node creation
- Move smooth_slerp_gate from AutoTunerSettings to shared MergeSettings
- Add smooth_slerp_gate to main LoRAOptimizer and _SIMPLE_DEFAULTS
- Add decision_smoothing (0.25) to _SIMPLE_DEFAULTS
- Remove output_mode from AutoTunerSettings (hardcode "merge")
- Rewrite all settings node tooltips for clarity
- Update tests for new settings structure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:30:13 +01:00
EthanfelandClaude Opus 4.6 85f9e03f35 feat: add right-click swap between LoRA Optimizer and AutoTuner
Adds "Swap to LoRA AutoTuner" / "Swap to LoRA Optimizer" to the
right-click context menu. Transfers shared widget values, reconnects
all matching inputs/outputs by name, and removes the old node.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:04:08 +01:00
EthanfelandClaude Opus 4.6 7959ce81fd merge: integrate PR #1 correctness refactor into test branch
Merges srv1n's correctness refactor (PR #1) with conflict resolution
to preserve our recent setting renames and fixes while incorporating
their core improvements:

- Target-key grouping: analysis/merge operates on resolved model weights
  instead of raw LoRA prefixes, fixing trainer-specific aliasing bugs
- Exact low-rank linear patches: lossless linear merges for weighted_sum
  and weighted_average when no nonlinear ops are needed
- Excess conflict metric: sign disagreement beyond statistical expectation
  (cosine-sim aware), replacing raw conflict ratio in decisions
- Subspace overlap: measures how much LoRA weight perturbations align
- Block smoothing: reduces noisy per-prefix mode flips
- Activation-aware calibration scoring for Phase 2 quality evaluation
- ACE-Step architecture support
- Soft LoKr/LoHa imports for graceful degradation
- RS-LoRA compensation re-enabled with safe heuristic
- 519-line test suite (19 tests, all passing)

Conflict resolution kept our:
- Setting renames (merge_refinement, patch_compression, strategy_set)
- Qwen-Image detection fix
- LoRACompatibilityAnalyzer node
- Conflict-mode masking in _apply_conflict_modes (PR's approach)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:19:34 +01:00
srv1n be7cb96f1e Sync correctness refactor onto upstream dfd3920 2026-03-08 16:02:45 +05:30
EthanfelandClaude Opus 4.6 00f6f315d1 fix: SaveMergedLoRA produces correct rank and dtype for Z-Image/fused-QKV models
Three compounding bugs caused saved LoRAs to be 2.3GB (instead of ~350MB) and
produce absurd images:

1. QKV refusion expanded all SVD-compressed LoRAAdapters to full-rank float32
   diffs, destroying all compression. SaveMergedLoRA saw 0 low-rank patches.

2. With no surviving LoRAAdapters, auto-rank fell back to 128 — too low for
   a merge of two rank-128 LoRAs (needs sum_rank=256), losing ~50% of signal.

3. _compress_to_lowrank returned float32 tensors (SVD needs float32 internally
   but never cast back), doubling file size vs expected fp16.

Fixes:
- _compress_to_lowrank now preserves input dtype (also halves merge-time RAM)
- QKV refusion downcasts fused diffs back to original dtype
- Optimizer passes sum_rank in lora_data so SaveMergedLoRA uses correct rank
- SaveMergedLoRA detects native dtype and enforces it on all saved tensors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:00:20 +01:00
EthanfelandClaude Opus 4.6 9cd0907ab6 fix: output_mode toggle and widget clipping on show/hide
- Remove slot name check for AutoTuner output connections (name
  may differ from RETURN_NAMES in some frontends)
- Preserve node width when resizing after widget toggle
- Defer initial hide to after node layout is finalized

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:51:51 +01:00
EthanfelandClaude Opus 4.6 7156947a2e feat: hide output_mode/settings_source until tuner_data connected
These widgets are only relevant when AutoTuner and Optimizer are
linked via tuner_data. Hidden by default, shown on connection,
reset to defaults on disconnect.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:36:36 +01:00
EthanfelandClaude Opus 4.6 ac7010390c feat: AutoTuner ↔ Optimizer bridge with opposing switches
Single model line workflow: AutoTuner → Optimizer. Only one node
merges at a time — the other is passthrough. A JS bridge syncs
the two switches as opposites when tuner_data is connected.

LoRAOptimizer:
- Add settings_source (manual/from_autotuner) and tuner_data inputs
- New execute_node entry point: from_autotuner = passthrough with
  widget sync via UI data, manual = normal merge
- Subclass safety: LoRAOptimizerSimple and WanVideoLoRAOptimizer
  explicitly set FUNCTION="optimize_merge" to bypass bridge

LoRAAutoTuner:
- Add output_mode (merge/tuning_only): tuning_only skips final
  merge and returns base model unchanged while still outputting
  scored tuner_data for downstream use
- Fix pre-existing bug: two error paths returning 5-element tuples
  instead of required 6

JS (new lora_optimizer_bridge.js):
- onExecuted: sync Optimizer widgets from applied_settings
- Opposing switch sync with recursion guard:
  from_autotuner ↔ merge, manual ↔ tuning_only

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 02:59:33 +01:00
EthanfelandClaude Opus 4.6 5a19e3f502 refactor: rename strength_mode to settings_visibility in LoRAStackDynamic
The widget now controls visibility of multiple advanced settings (conflict
mode, key filter) beyond just strength sliders, so the name better reflects
its purpose.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:56:34 +01:00
EthanfelandClaude Opus 4.6 38339e390a feat: move key_filter to per-LoRA setting with simple/advanced visibility
Migrate key_filter from a stack-wide setting on LoRAStackDynamic to a
per-LoRA per-slot control. Users can now filter keys independently for
each LoRA (e.g. "only take unique VACE keys from LoRA #2") without
chaining multiple stacker nodes.

- LoRAStack: add key_filter dropdown (all/shared_only/unique_only)
- LoRAStackDynamic: replace node-level key_filter with per-slot
  key_filter_{i}; hide conflict_mode and key_filter in simple mode
- _normalize_stack: propagate key_filter from both tuple and dict formats
- _compute_cache_key: include per-entry key_filter, remove sentinel logic
- _merge_one_prefix: filter per-LoRA in both low-rank and full-rank paths
  using raw n_loras from Pass 1
- _build_report: show key_filter per-LoRA instead of globally
- Add LoRAOptimizerSimple with sensible defaults; rename existing
  optimizer to "LoRA Optimizer (Advanced)"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:25:49 +01:00
EthanfelandClaude Opus 4.6 da924b9f82 fix: copy filename stem (not full path) when syncing dropdown to text
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:30:04 +01:00
EthanfelandClaude Opus 4.6 fb8173ad7b feat: sync LoRA values when switching input_mode
Switching dropdown->text copies COMBO values to text fields.
Switching text->dropdown matches text values against COMBO options
by exact path, filename stem, or path suffix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:23:38 +01:00
EthanfelandClaude Opus 4.6 433364493f fix: rename mode to strength_mode, restore base_model_filter
Rename "mode" to "strength_mode" to avoid confusion with "input_mode".
Restore base_model_filter for dropdown mode (hidden in text mode).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:12:56 +01:00
EthanfelandClaude Opus 4.6 3e87562cd8 feat: add input_mode toggle — dropdown (COMBO) or text (STRING)
New "input_mode" widget switches between dropdown and text modes:
- "dropdown": COMBO dropdowns to browse and pick LoRAs (original behavior)
- "text": STRING text inputs for typing short names or connecting text nodes

Both modes share the same strength/conflict_mode controls. Text mode
resolves short names (filename without path/extension) to full paths
via _resolve_lora_name. JS toggles lora_name_N vs lora_name_text_N
visibility based on input_mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:06:04 +01:00
EthanfelandClaude Opus 4.6 d0cc763713 feat: change lora_name slots to STRING inputs for text node connections
Replace COMBO dropdowns with STRING text inputs so users can type
short LoRA names (e.g. "Milena_20260216180133") or connect text nodes
directly. Names are resolved to full paths via _resolve_lora_name.
Remove base_model_filter (no longer applicable without COMBO widgets).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:57:47 +01:00
EthanfelandClaude Opus 4.6 6a60059cf0 refactor: simplify lora_text to plain text input with name resolution
Remove the autocomplete JS class (was ~155 lines). Instead, resolve
short LoRA names server-side: typing "Milena_20260216180133" matches
against installed LoRAs by filename stem, so it resolves to e.g.
"ZImageTurbo/character/Milena_20260216180133.safetensors". No Lora
Manager dependency needed — just plain text input with smart matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:34:42 +01:00
EthanfelandClaude Opus 4.6 3e1eb45065 feat: add lora_text input with autocomplete to LoRAStackDynamic
Add a multiline text widget where users can type LoRA paths directly
(one per line) with optional strength values. Supports three formats:
path.safetensors (strength 1.0), path:0.8 (both), path:0.8:0.5
(model:clip). When ComfyUI-Lora-Manager is installed, a lightweight
autocomplete dropdown suggests matches via its API. Works without
Lora Manager too (no autocomplete, user types exact paths).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:17:44 +01:00
EthanfelandClaude Opus 4.6 0363d23732 fix: cap retry attempts in base model filter init
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:52:36 +01:00
EthanfelandClaude Opus 4.6 20c72fab5c fix: base model filter — path matching, pagination, data safety
- Fix absolute→relative path matching (endsWith against fullLoraList)
- Fix page_size cap (paginate through all pages, build local map)
- Add explicit base_model_filter param to build_stack (no kwargs leak)
- Add retry when widget options not yet populated
- Preserve selected LoRA value when filter changes (no silent reset)
- Handle workflow restore (apply filter after interceptor setup)
- Extract interceptWidgetValue helper, add configurable:true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:48:53 +01:00
EthanfelandClaude Opus 4.6 58a2ec07db feat: add base model filter to LoRAStackDynamic
Adds a dropdown that filters LoRA dropdowns by base model type (SDXL,
Flux, Wan, etc.) using ComfyUI-Lora-Manager's API. If Lora Manager is
not installed, the filter widget is automatically hidden and dropdowns
show the full unfiltered list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:43:40 +01:00
EthanfelandClaude Opus 4.6 2cad48fda6 feat: add JS extension stub for LoRAConflictEditor
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:09:22 +01:00
EthanfelandClaude Opus 4.6 f48898f6ca feat: add per-LoRA conflict_mode masking (all/low_conflict/high_conflict)
Allow users to control how each LoRA behaves in contested weight regions.
'low_conflict' applies only where the LoRA agrees with the majority sign,
'high_conflict' applies only where it disagrees. Defaults to 'all' (no
filtering) with zero overhead when unused.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:17:52 +01:00
EthanfelandClaude Opus 4.6 77fcabb1f7 fix: dynamic stacker widget hiding (add widget.hidden, canvas dirty, timing)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 23:20:22 +01:00
EthanfelandClaude Opus 4.6 e9304617f4 feat: interference-aware auto-strength, save to loras folder, dynamic stacker JS
- Auto-strength now uses pairwise cosine similarity for exact vector-sum
  energy normalization instead of assuming orthogonal LoRAs
- Save Merged LoRA defaults to ComfyUI loras folder; supports absolute paths
- Add JS extension for LoRA Stack (Dynamic) widget show/hide
- Fix LTX architecture detection for middle_block/mid_block keys
- Update banner SVG with new feature tags

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 23:07:41 +01:00