Merge branch 'claude/confident-bhaskara'
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
"WebFetch(domain:api.comfy.org)",
|
||||
"WebFetch(domain:github.com)",
|
||||
"WebFetch(domain:raw.githubusercontent.com)",
|
||||
"WebFetch(domain:api.github.com)"
|
||||
"WebFetch(domain:api.github.com)",
|
||||
"Bash(grep:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+303
-24
@@ -14,14 +14,25 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
├── model_cache.py # 3-Tier Model Caching System
|
||||
├── remote_vae.py # Remote VAE Offloading
|
||||
│
|
||||
├── batch_encoding.py # Batch CLIP Encoding with Combinator Support
|
||||
├── conditioning_cache.py # Disk-based Conditioning Cache
|
||||
├── lora_utils.py # LoRA Searching & Validation
|
||||
├── trigger_words.py # CivitAI Trigger Word Fetching
|
||||
├── trigger_words.py # CivitAI Trigger Word Fetching & Prompt Assembly
|
||||
├── config_utils.py # Config Parsing, Expansion & Cartesian Products
|
||||
├── directory_scanner.py # External Directory Session Scanner
|
||||
├── metadata_packer.py # PNG/WebP Metadata Embedding for Exports
|
||||
├── manifest_utils.py # Manifest Read/Write/Merge Helpers
|
||||
├── html_generator.py # Reads /resources/ to build the Dashboard HTML
|
||||
│
|
||||
├── web/ # [ComfyUI Integration Layer]
|
||||
│ ├── dashboard.js # Registers Dashboard Node & Message Handling
|
||||
│ ├── config_builder.js # Registers Builder Node & Custom UI Widget
|
||||
│ └── smart_json_text.js # Registers JSON Node & Syntax Highlighting
|
||||
│ ├── smart_json_text.js # Registers JSON Node & Syntax Highlighting
|
||||
│ └── conf_builder/ # [Config Builder UI Modules]
|
||||
│ ├── conf-builder-main.js # Main entry, widget registration
|
||||
│ ├── conf-builder-ui-components.js # UI rendering (dropdowns, sliders, cards)
|
||||
│ ├── conf-builder-config-management.js # Save/Load/Duplicate config logic
|
||||
│ └── conf-builder-utilities.js # State-to-JSON conversion, iteration counting
|
||||
│
|
||||
└── resources/ # [The Dashboard SPA Core]
|
||||
├── template.html # HTML Skeleton for the generated report
|
||||
@@ -36,6 +47,22 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
|
||||
```
|
||||
|
||||
**Output Directory Structure (Generated at Runtime):**
|
||||
```text
|
||||
ComfyUI/output/
|
||||
├── ultimate-configs/ # Saved config presets (NOT under benchmarks/)
|
||||
│ └── {name}.json
|
||||
└── benchmarks/
|
||||
├── loras_tags.json # LoRA trigger word cache (shared)
|
||||
├── model_hashes.json # SHA256 hash cache for CivitAI lookups
|
||||
├── model-data/ # CivitAI metadata cache per model
|
||||
│ └── {model_name}/metadata.json
|
||||
└── {session_name}/
|
||||
├── manifest.json # Generation metadata + user annotations
|
||||
├── images/
|
||||
│ └── img_{n}.webp
|
||||
└── favorites/ # Created by export_favorites endpoint
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -49,21 +76,72 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
|
||||
---
|
||||
|
||||
### **Node Class Mappings**
|
||||
|
||||
| Internal Name | Python Class | Purpose |
|
||||
|---|---|---|
|
||||
| `UltimateSamplerGrid` | `SamplerGridTester` | Main generation node — receives configs, runs Cartesian grid |
|
||||
| `UltimateGridDashboard` | `SamplerConfigDashboardViewer` | Dashboard viewer — renders iframe with results |
|
||||
| `UltimateConfigBuilder` | `UltimateConfigBuilder` | Visual config builder — outputs `configs_json` string |
|
||||
| `SmartJSONText` | `SmartJSONTextNode` | JSON text widget with syntax highlighting |
|
||||
|
||||
---
|
||||
|
||||
### **API Routes Reference**
|
||||
|
||||
All routes registered in `__init__.py` via `PromptServer.instance.routes`.
|
||||
|
||||
**Config Builder APIs** (`/configbuilder/`):
|
||||
|
||||
| Method | Route | Purpose |
|
||||
|---|---|---|
|
||||
| GET | `/configbuilder/list_configs` | List saved config preset filenames |
|
||||
| POST | `/configbuilder/save_config` | Save config preset `{name, data}` |
|
||||
| POST | `/configbuilder/load_config` | Load config preset `{name}` |
|
||||
| POST | `/configbuilder/lookup_triggers` | Bulk LoRA trigger word lookup `{loras: [...]}` |
|
||||
| POST | `/configbuilder/lookup_lora_metadata` | Full CivitAI metadata for LoRA `{lora_name}` |
|
||||
| POST | `/configbuilder/lookup_model_metadata` | Full CivitAI metadata for model `{model_name, model_type}` |
|
||||
| GET | `/configbuilder/model_lists` | All model/sampler/scheduler/VAE lists |
|
||||
| POST | `/configbuilder/refresh_models` | Signal frontend cache clear |
|
||||
|
||||
**Dashboard/Tester APIs** (`/config_tester/`):
|
||||
|
||||
| Method | Route | Purpose |
|
||||
|---|---|---|
|
||||
| POST | `/config_tester/delete_session` | Delete session folder `{session_name}` |
|
||||
| POST | `/config_tester/save_changes` | Delta save — only changed items `{session_name, changed_items}` |
|
||||
| POST | `/config_tester/save_manifest` | Full merge save `{session_name, manifest}` |
|
||||
| POST | `/config_tester/get_session_html` | Generate dashboard HTML on-demand `{session_name, node_id}` |
|
||||
| POST | `/config_tester/export_favorites` | Export favorited images `{session_name, pack_metadata, organize_by_prompt, organize_by_lora}` |
|
||||
| POST | `/config_tester/scan_directory` | Scan external dir for images `{directory_path, session_name}` |
|
||||
| GET | `/config_tester/view_external` | Serve whitelisted external images `?path=...` |
|
||||
| POST | `/config_tester/whitelist_directory` | Re-whitelist dir after restart `{directory_path}` |
|
||||
|
||||
---
|
||||
|
||||
### **1. The Backend: Core Logic & Orchestration**
|
||||
|
||||
*Located in the root directory.*
|
||||
|
||||
* **`sampler_node.py`** (The Interface)
|
||||
* **Role:** The primary ComfyUI node. Validates inputs and passes control to the orchestrator.
|
||||
* **Key Logic:** Disables caching features if "Optional" inputs are connected (since external changes can't be tracked).
|
||||
* **Role:** The primary ComfyUI node (`SamplerGridTester`). Validates inputs and passes control to the orchestrator.
|
||||
* **Key Functions:** `run_tests()` (entry point → delegates to `generation_orchestrator.run_generation_loop()`), `find_existing_match()`, `get_latent_channels()`
|
||||
* **Key Logic:** Disables caching features if "Optional" inputs are connected (since external changes can't be tracked). `IS_CHANGED()` returns `NaN` when optionals connected (forces re-execution), deterministic hash otherwise.
|
||||
* **Required Inputs:** `ckpt_name`, `positive_text`, `negative_text`, `seed`, `denoise`, `configs_json`, `resolutions_json`, `session_name`, `overwrite_existing`, `flush_batch_every`, `add_random_seeds_to_gens`, `lora_triggerwords_mode`, `remote_vae_endpoint`, `save_conditioning_cache_to_file`, `enable_model_cache`
|
||||
* **Optional Inputs:** `optional_model` (MODEL), `optional_clip` (CLIP), `optional_vae` (VAE), `optional_positive` (CONDITIONING), `optional_negative` (CONDITIONING), `optional_latent` (LATENT)
|
||||
|
||||
|
||||
* **`generation_orchestrator.py`** (The Conductor)
|
||||
* **`generation_orchestrator.py`** (The Conductor — largest file, ~987 lines)
|
||||
* **Role:** Manages the generation loop.
|
||||
* **Key Functions:** `run_generation_loop()`, `check_if_job_completed()`, `setup_session_directories()`, `load_model_by_type()`, `get_model_cache_key()`, `calculate_clip_hash()`
|
||||
* **Key Logic:**
|
||||
* **Smart Skip:** Checks `manifest.json` to skip existing images.
|
||||
* **Lookahead:** Triggers background loading for the *next* job while the current one runs.
|
||||
* **Interrupts:** Catches cancel signals to save the manifest before stopping.
|
||||
* **Smart Skip:** `check_if_job_completed()` matches against manifest items by seed/resolution/sampler/scheduler/steps/cfg/denoise. Skips model/lora/prompt matching when optional inputs connected.
|
||||
* **Lookahead:** Triggers `model_cache.preload_lora_model()` / `preload_base_model()` for the *next* job while the current one runs.
|
||||
* **Interrupts:** Catches `InterruptProcessingException` during text encoding AND image generation. Flushes pending VAE batch and saves manifest before stopping.
|
||||
* **ETA Tracking:** Sends `ultimate_grid.progress` events to dashboard via `PromptServer.instance.send_sync()`.
|
||||
* **Node Change Detection:** Detects when connected upstream nodes have changed for smarter job resuming.
|
||||
* **Pre-encoding:** Single model → batch-encode all unique prompts upfront. Multi-model → re-encode per model switch.
|
||||
* **VAE Switching:** Flushes pending batch before switching VAE. Tracks `default_model_vae` for reverting to "Default".
|
||||
|
||||
|
||||
|
||||
@@ -73,9 +151,17 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
* **Key Logic:** Implements `flush_batch_with_vae` to save images and update the manifest in chunks (preventing VRAM overflow).
|
||||
|
||||
|
||||
* **`config_builder_node.py`** (The Builder UI Backend)
|
||||
* **Role:** `UltimateConfigBuilder` node. All state stored in single `lora_config` widget as JSON string.
|
||||
* **Key Functions:** `generate_config()`, `process_lora_array()`, `get_available_sessions()`, `expand_lora_folders()`, `lookup_lora_triggers()`
|
||||
* **Key Logic:** Python reads ONLY `lora_config` widget — all other INPUT_TYPES widgets (samplers, schedulers, steps, cfg) are vestigial and ignored. Also defines API routes for trigger/metadata lookup and model lists.
|
||||
|
||||
|
||||
* **`html_generator.py`** (The Builder)
|
||||
* **Role:** Compiles the SPA.
|
||||
* **Key Logic:** Reads all files from `/resources/`, injects them into `template.html`, and embeds the `manifest.json` data as a JavaScript variable (`fullManifest`), allowing the dashboard to load instantly without an initial API call.
|
||||
* **Role:** Compiles the SPA into a single inlined HTML string.
|
||||
* **Key Functions:** `get_html_template(title, manifest_data, node_id)`
|
||||
* **Key Logic:** Reads all files from `/resources/` in dependency order, injects them into `template.html`, embeds `manifest.json` as `fullManifest` JS variable. Replaces placeholders: `__CSS_CONTENT__`, `__JS_CONTENT__`, `__TITLE__`, `__NODE_ID__`. No separate HTTP requests — everything inlined.
|
||||
* **JS Load Order:** `logic_state.js` → `logic_utils.js` → `logic_ui.js` → `logic_virtual.js` → `logic_pipeline.js` → `logic_events.js` → `logic_init.js`
|
||||
|
||||
|
||||
|
||||
@@ -86,20 +172,134 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
*Located in the root directory.*
|
||||
|
||||
* **`model_cache.py`** (The 3-Tier Cache)
|
||||
* **Role:** Thread-safe resource manager.
|
||||
* **Tiers:** 1. Raw Files (RAM), 2. Incremental States (Partial LoRA stacks), 3. Final Results (Patched Models).
|
||||
* **Role:** Thread-safe resource manager. Single `ModelCache` class.
|
||||
* **Tiers:** 1. LoRA File Cache (raw state_dicts in RAM), 2. Incremental States (partial LoRA stacks via `check_incremental_cache()`), 3. Final Patched Models (`get_lora_model()` / `put_lora_model()`).
|
||||
* **Key Methods:** `get_base_model()`, `put_base_model()`, `preload_lora_model()`, `preload_base_model()`, `register_schedule()`, `set_current_step()`, `clear()`, `print_stats()`
|
||||
* **Async Preloading:** `preload_*` methods run loaders in background threads via `concurrent.futures`, results retrieved later.
|
||||
|
||||
|
||||
* **`conditioning_cache.py`** (The Prompt Cache)
|
||||
* **Role:** Caches CLIP text encodings to disk.
|
||||
* **Key Logic:** Hashes `(Prompt + CLIP State + LoRA Config)` to ensure validity.
|
||||
* **Key Logic:** Hashes `(Prompt + CLIP State + LoRA Config)` to ensure validity. Auto-disabled when optional inputs connected.
|
||||
|
||||
|
||||
* **`batch_encoding.py`** (The Accelerator)
|
||||
* **Role:** Bulk prompt encoder.
|
||||
* **Key Logic:** Scans the grid for all prompts using the current model and encodes them in one massive GPU batch.
|
||||
* **Role:** Bulk prompt encoder with combinator support.
|
||||
* **Key Functions:** `encode_prompt_with_combinators(clip, text, clip_skip)`, `batch_encode_prompts(clip, positives, negatives, cond_cache, clip_skip)`
|
||||
* **Combinators:** `AND` (multi-cond with optional weights like `:1.5`), `CAT` (tensor concatenation), `AVG(weight)` (weighted average, default 0.5), `BREAK` (native tokenizer segment break at 77-token chunks).
|
||||
|
||||
|
||||
* **`model_loader.py`** (The Loader)
|
||||
* **Role:** Checkpoint, LoRA, VAE, GGUF, and diffusion model loading.
|
||||
* **Key Functions:** `load_checkpoint()`, `load_loras()`, `load_vae_by_name()`, `load_diffusion_model_and_clip()`, `load_loras_for_preencoding()`, `cleanup_model_references()`, `print_incompatible_loras_summary()`
|
||||
|
||||
|
||||
* **`trigger_words.py`** (Trigger Word Engine)
|
||||
* **Role:** CivitAI trigger word fetching and prompt assembly.
|
||||
* **Key Functions:** `collect_unique_prompts_with_triggers(expanded_configs, mode)`, `build_prompt_with_triggers(config, mode)`, `get_filtered_lora_triggers(lora_string, omit_list)`
|
||||
* **Modes:** `"None"`, `"Append To End"`, `"Append To Start"`, `"Read From Config"`. Per-LoRA placement via `lora_triggerwords_append_settings`. Also applies `model_prompt_prefix` / `model_prompt_suffix`.
|
||||
|
||||
|
||||
* **`config_utils.py`** (Expansion Engine)
|
||||
* **Role:** Config parsing, Cartesian product expansion, folder expansion.
|
||||
* **Key Functions:** `expand_configs()`, `parse_prompt_input_nested()`, `prepare_input_jobs()`, `parse_lora_definition()`, `get_files_from_folder()`, `sanitize_session_name()`
|
||||
* **Key Logic:** `parse_prompt_input_nested()` supports arbitrarily deep nested arrays for Cartesian prompting. `"folder/"` = expand individually, `"folder/*"` = stack all together.
|
||||
|
||||
|
||||
* **`manifest_utils.py`** (Manifest Persistence)
|
||||
* **Key Functions:** `load_existing_manifest()`, `save_manifest()`, `merge_manifest_user_changes()`
|
||||
* **Key Logic:** `save_manifest()` calls `merge_manifest_user_changes()` first — reloads from disk to preserve user favorites/rejected/notes that may have changed concurrently.
|
||||
|
||||
|
||||
* **`directory_scanner.py`** (External Image Scanner)
|
||||
* **Key Functions:** `scan_directory_for_images()`, `parse_a1111_parameters()`
|
||||
* **Key Logic:** Parses A1111-style PNG metadata into dashboard-compatible manifest items. Preserves user tags on re-scan by merging with existing manifest.
|
||||
|
||||
|
||||
* **`metadata_packer.py`** (Export Metadata)
|
||||
* **Key Functions:** `pack_metadata_into_image()`, `extract_metadata_from_image()`, `calculate_file_hash()`
|
||||
* **Key Logic:** Packs ComfyUI-compatible metadata into PNG for CivitAI uploads. Maintains SHA256 hash cache at `benchmarks/model_hashes.json`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **Key Data Shapes**
|
||||
|
||||
**Config Array State** (`node.state.config_arrays[n]` in JS, parsed from `lora_config` widget in Python):
|
||||
```json
|
||||
{
|
||||
"name": "Config 1",
|
||||
"samplers": ["euler", "dpmpp_2m"],
|
||||
"schedulers": ["normal", "karras"],
|
||||
"steps": "20, 30",
|
||||
"cfg": "7.0",
|
||||
"models": [{"path": "model.safetensors", "type": "checkpoint"}],
|
||||
"vaes": ["None"],
|
||||
"loras": ["lora.safetensors:1.00:1.00"],
|
||||
"lora_bypass_states": {"lora_name": true},
|
||||
"model_bypass_states": {"model_path": true},
|
||||
"lora_omit_triggers": [],
|
||||
"lora_triggerwords_append_settings": {},
|
||||
"lora_strength_lock": {},
|
||||
"combine": false,
|
||||
"positive_prompt_groups": [],
|
||||
"negative_prompt": "",
|
||||
"use_custom_prompts": false,
|
||||
"model_prompt_prefix": "",
|
||||
"model_prompt_suffix": "",
|
||||
"attention_modes": ["default"],
|
||||
"seed_behavior": "fixed",
|
||||
"text_encoders": [],
|
||||
"clip_type": "stable_diffusion",
|
||||
"gguf_options": {}
|
||||
}
|
||||
```
|
||||
|
||||
**Manifest Item** (`manifest.json` `items[n]`):
|
||||
```json
|
||||
{
|
||||
"id": "uuid-string",
|
||||
"seed": 12345, "cfg": 7.0, "steps": 20,
|
||||
"sampler": "euler", "scheduler": "normal",
|
||||
"model": "model.safetensors", "lora": "lora:1.0:1.0",
|
||||
"vae": "vae.safetensors", "attention_mode": "default",
|
||||
"positive": "prompt text", "negative": "negative text",
|
||||
"denoise": 1.0, "width": 1024, "height": 1024,
|
||||
"batch_idx": 0, "generation_time": 45.2,
|
||||
"file": "/view?filename=img_0.webp&type=output&subfolder=benchmarks/session/images",
|
||||
"favorite": false, "rejected": false, "notes": ""
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Communication Patterns**
|
||||
|
||||
**Widget Bridge (Config Builder → Python):**
|
||||
```
|
||||
JS node.state → JSON.stringify() → lora_config widget.value → Python json.loads(lora_config)
|
||||
```
|
||||
All other widget params in INPUT_TYPES (samplers, schedulers, steps, cfg) are ignored by Python.
|
||||
|
||||
**Live Dashboard Updates (Server → Dashboard iframe):**
|
||||
```
|
||||
generation_orchestrator → PromptServer.send_sync("ultimate_grid.update")
|
||||
→ dashboard.js api.addEventListener → postMessage({type: 'update_data'})
|
||||
→ iframe logic_events.js message handler
|
||||
```
|
||||
|
||||
**Progress Updates (Server → Dashboard iframe):**
|
||||
```
|
||||
generation_orchestrator → PromptServer.send_sync("ultimate_grid.progress")
|
||||
→ dashboard.js → postMessage({type: 'progress_update'})
|
||||
→ iframe #eta-bar update
|
||||
```
|
||||
|
||||
**Dashboard → Parent Frame:**
|
||||
```
|
||||
iframe postMessage({type: 'toggle_fullscreen', node_id})
|
||||
→ dashboard.js listener → toggle .dashboard-fullscreen class
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -108,15 +308,32 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
*Located in `/web/`.*
|
||||
|
||||
* **`dashboard.js`**
|
||||
* **Role:** Registers the Dashboard node. Listens for server events to update the iframe.
|
||||
* **Role:** Registers the Dashboard node (`UltimateGridDashboard`). Listens for `ultimate_grid.update` and `ultimate_grid.progress` server events.
|
||||
* **Key Logic:** Forwards events to matching iframe via `postMessage()`. Auto-loads session when update received for unloaded session. Handles fullscreen toggle via `.dashboard-fullscreen` CSS class.
|
||||
* **Widgets:** "RELOAD / SHOW SESSION" button → `forceLoadSession()`, "DELETE SESSION" button → POST `/config_tester/delete_session`
|
||||
|
||||
|
||||
* **`config_builder.js`**
|
||||
* **Role:** Registers the Builder node. Provides the complex UI for creating JSON configs.
|
||||
* **`config_builder.js`** (entry point, loads `conf_builder/` modules)
|
||||
* **Role:** Registers the Builder node (`UltimateConfigBuilder`). Entry point for the modular Config Builder UI.
|
||||
* **Key Logic:** `onNodeCreated` **MUST be synchronous** (async init runs in fire-and-forget IIFE). Uses `Date.now()` cache-busting on module `import()` calls. Hooks `app.refreshComboInNodes()` to detect model/LoRA changes.
|
||||
* **Node Methods:** `node.saveState()`, `node.renderUI()`, `node.loadSession(sessionName)`, `node.saveConfigToBackend()`, `node.loadConfigFromBackend(filename)`, `node.triggerAutoSave()` (2s debounce)
|
||||
|
||||
|
||||
* **`smart_json_text.js`**
|
||||
* **Role:** Registers the JSON node. Adds syntax highlighting and validation.
|
||||
* **Role:** Registers the JSON node (`SmartJSONText`). Adds syntax highlighting and validation.
|
||||
|
||||
|
||||
* **`conf_builder/conf-builder-utilities.js`** (The Data Layer)
|
||||
* **Key Exports:** `convertStateToConfigs()`, `getIterationCount()`, `parseLoraString()`, `buildLoraString()`, `refreshAllConfigBuilders()`, `clearAllCaches()`, `normalizePath()`, `getAvailableLoras()`, `getModelLists()`
|
||||
* **Global Caches:** `availableLoras`, `availableModels`, `availableVAEs`, `availableSamplers`, `availableSchedulers`, `availableSessions`, `availableConfigs`
|
||||
|
||||
|
||||
* **`conf_builder/conf-builder-config-management.js`** (The UI Builder)
|
||||
* **Key Exports:** `renderUI()`, `updatePreview()`, `renderConfigSection()`, `renderSessionSection()`, `createConfigArrayElement()`, `createModelElement()`
|
||||
|
||||
|
||||
* **`conf_builder/conf-builder-ui-components.js`** (The Widget Library)
|
||||
* **Key Exports:** `createSearchableSelect()`, `createSlider()`, `createInputGroup()`, `getStyles()`
|
||||
|
||||
|
||||
|
||||
@@ -149,7 +366,7 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
* **`logic_events.js`** (The Handler)
|
||||
* **Role:** Interaction & API Bridge.
|
||||
* **Key Logic:**
|
||||
* **Keyboard Shortcuts:** Maps `Arrow Keys` (Pan), `Space` (Scroll), `+/-` (Zoom), `F` (Fit).
|
||||
* **Keyboard Shortcuts:** Maps `Arrow Keys` (Pan), `Space` (Scroll), `+/-` (Zoom), `F` (Fit), `Escape` (close modals with priority chain).
|
||||
* **API Calls:** Uses `fetch` to hit endpoints like `/config_tester/save_manifest` (when you star/reject an image) and `/config_tester/delete_session`.
|
||||
* **Live Updates:** Listens for the `executed` event from ComfyUI to trigger a soft-refresh of the image list.
|
||||
|
||||
@@ -168,15 +385,49 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
|
||||
* **`logic_ui.js`** (The Renderer)
|
||||
* **Role:** DOM Manipulation.
|
||||
* **Key Logic:** Generates the HTML for individual cards and the "Revise" modal.
|
||||
* **Key Logic:** Generates the HTML for individual cards, the "Revise" modal, and the unified Settings panel (gear icon). Manages modal open/close with overlay and body overflow lock.
|
||||
|
||||
|
||||
* **`logic_utils.js`** (The Helpers)
|
||||
* **Role:** Utilities.
|
||||
* **Key Logic:** Debouncers, formatters, and helper functions.
|
||||
* **Role:** Utilities and server communication.
|
||||
* **Key Functions:** `loadSession()`, `saveState()`, `exportFavorites()`, `scanDirectory()`, `triggerGen()`, `toggleFullscreen()`, `rejectItem()`
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **Key Functions Quick Reference**
|
||||
|
||||
**Python (by file):**
|
||||
|
||||
| File | Key Functions |
|
||||
|---|---|
|
||||
| `sampler_node.py` | `run_tests()`, `find_existing_match()`, `get_latent_channels()`, `IS_CHANGED()` |
|
||||
| `config_builder_node.py` | `generate_config()`, `process_lora_array()`, `get_available_sessions()`, `expand_lora_folders()` |
|
||||
| `generation_orchestrator.py` | `run_generation_loop()`, `check_if_job_completed()`, `setup_session_directories()`, `load_model_by_type()` |
|
||||
| `batch_encoding.py` | `encode_prompt_with_combinators()`, `batch_encode_prompts()` |
|
||||
| `config_utils.py` | `expand_configs()`, `parse_prompt_input_nested()`, `prepare_input_jobs()`, `parse_lora_definition()` |
|
||||
| `model_loader.py` | `load_checkpoint()`, `load_loras()`, `load_vae_by_name()`, `load_diffusion_model_and_clip()` |
|
||||
| `model_cache.py` | `ModelCache.get_base_model()`, `.put_base_model()`, `.preload_lora_model()`, `.register_schedule()` |
|
||||
| `trigger_words.py` | `collect_unique_prompts_with_triggers()`, `build_prompt_with_triggers()`, `get_filtered_lora_triggers()` |
|
||||
| `manifest_utils.py` | `load_existing_manifest()`, `save_manifest()`, `merge_manifest_user_changes()` |
|
||||
| `directory_scanner.py` | `scan_directory_for_images()`, `parse_a1111_parameters()` |
|
||||
| `metadata_packer.py` | `pack_metadata_into_image()`, `extract_metadata_from_image()`, `calculate_file_hash()` |
|
||||
|
||||
**JavaScript (by file):**
|
||||
|
||||
| File | Key Functions |
|
||||
|---|---|
|
||||
| `conf-builder-utilities.js` | `convertStateToConfigs()`, `getIterationCount()`, `parseLoraString()`, `buildLoraString()`, `refreshAllConfigBuilders()` |
|
||||
| `conf-builder-config-management.js` | `renderUI()`, `updatePreview()`, `createConfigArrayElement()`, `createModelElement()` |
|
||||
| `conf-builder-ui-components.js` | `createSearchableSelect()`, `createSlider()`, `getStyles()` |
|
||||
| `conf-builder-main.js` | `ensureModulesLoaded()`, `node.saveState()`, `node.renderUI()`, `node.loadSession()` |
|
||||
| `logic_ui.js` | `toggleCogMenu()`, `toggleFiltersPopup()`, `initFilters()` |
|
||||
| `logic_utils.js` | `loadSession()`, `exportFavorites()`, `scanDirectory()`, `triggerGen()`, `toggleFullscreen()` |
|
||||
| `logic_virtual.js` | `renderDOM()`, `updateVisibleItems()`, `calculateVisibleRange()`, `autoFitZoom()`, `goToImage()` |
|
||||
| `logic_pipeline.js` | `updateDataPipeline()`, `executePipeline()`, `processNewData()`, `incrementalFilter()` |
|
||||
| `logic_events.js` | Message handler for `progress_update` and `update_data` types |
|
||||
|
||||
---
|
||||
|
||||
### **Summary of Data Flow**
|
||||
@@ -188,4 +439,32 @@ ComfyUI-Ultimate-Sampler-Grid/
|
||||
5. **Compilation:** `html_generator.py` reads the `manifest.json` and all `.js` files in `/resources/`, baking them into a single `dashboard_html` string.
|
||||
6. **Display:** The **Dashboard Node** (JS) renders this HTML in an iframe.
|
||||
7. **Boot:** `logic_init.js` wakes up, loads the data into `logic_state.js`, and `logic_virtual.js` starts rendering cards.
|
||||
8. **Interaction:** You click "Favorite" in the UI -> `logic_events.js` sends an API request to `__init__.py` -> Python updates `manifest.json` on disk.
|
||||
8. **Interaction:** You click "Favorite" in the UI -> `logic_events.js` sends an API request to `__init__.py` -> Python updates `manifest.json` on disk.
|
||||
|
||||
---
|
||||
|
||||
### **Critical Development Gotchas**
|
||||
|
||||
1. **Dual Data Path** — Frontend `convertStateToConfigs()` (in `conf-builder-utilities.js`) and Python `generate_config()` (in `config_builder_node.py`) are **independent implementations** that both transform `node.state` into config JSON. Both must stay in sync when adding new config fields or the UI preview will mismatch the actual output.
|
||||
|
||||
2. **Widget Bridge** — The `lora_config` widget (STRING type) holds the **entire** node state as serialized JSON. Python `generate_config()` ignores all other widget params and reads only `lora_config`. The `samplers`, `schedulers`, `steps`, `cfg` widgets in `INPUT_TYPES` are vestigial (kept for ComfyUI schema compliance only).
|
||||
|
||||
3. **onNodeCreated Must Be Synchronous** — In `conf-builder-main.js`, `onNodeCreated` MUST remain synchronous. Async initialization runs inside a fire-and-forget `(async () => { ... })()` IIFE. Making `onNodeCreated` itself async breaks widget registration.
|
||||
|
||||
4. **Resources Are Inlined** — `html_generator.py` reads all `/resources/` files and bakes them into one HTML string. Zero external HTTP requests. Changes to dashboard JS/CSS require re-generating the HTML (re-run the sampler node or call `/config_tester/get_session_html`).
|
||||
|
||||
5. **Manifest Merge on Save** — `manifest_utils.py` `save_manifest()` calls `merge_manifest_user_changes()` which reloads from disk before saving. This preserves user favorites/rejected/notes that may have been changed concurrently while generation was running.
|
||||
|
||||
6. **Optional Inputs Disable Caches** — `sampler_node.py` disables conditioning cache when `optional_model`/`optional_clip`/`optional_positive`/`optional_negative` are connected. `IS_CHANGED()` returns `NaN` (forces re-execution). `check_if_job_completed()` skips model/lora/prompt matching.
|
||||
|
||||
7. **Folder Expansion Syntax** — `"folder/"` = expand to individual entries (one combination each). `"folder/*"` = stack ALL files in folder together as a single combined entry. Weight array syntax `"lora:[0.5, 0.8]:1.0"` creates grid search over strengths. No trailing slash = single file match.
|
||||
|
||||
8. **LoRA String Format** — `"name:model_str:clip_str"`, stacked with `" + "` separator. `parseLoraString()` in JS, `parse_lora_definition()` in Python. Default strengths: 1.0 for both if colon-separated parts are missing.
|
||||
|
||||
9. **Cache Busting** — `conf-builder-main.js` uses `Date.now()` timestamp on `import()` calls for module loading. Also intercepts `window.fetch` to detect `/object_info` calls and trigger cache refresh. Static JS served via ComfyUI's `/extensions/` path.
|
||||
|
||||
10. **Config Presets Path** — Saved to `output/ultimate-configs/` (defined in `__init__.py`), NOT `output/benchmarks/configs/`. Session data goes under `output/benchmarks/{session_name}/`.
|
||||
|
||||
11. **Field Name Inconsistency: `favorite` vs `favorited`** — `manifest_utils.py` `merge_manifest_user_changes()` uses the key `"favorite"`. But `__init__.py` routes like `save_manifest` and `export_favorites` check `"favorited"`. When touching favorite/unfavorite logic, check which key name the specific code path uses.
|
||||
|
||||
12. **Prompt Expansion is Recursive** — `parse_prompt_input_nested()` in `config_utils.py` supports arbitrarily deep nested arrays. Flat list = OR (options). List containing sub-lists = AND (Cartesian product). This is used by both the node text inputs and per-config prompt groups from the builder UI.
|
||||
@@ -49,6 +49,10 @@ Stop guessing which Sampler, Scheduler, Prompt, Denoise, Model, Lora or CFG valu
|
||||
- [Strength Locking](#strength-locking)
|
||||
- [Folder Expansion (Models & LoRAs)](#folder-expansion-models--loras)
|
||||
- [LoRA Stacking](#lora-stacking)
|
||||
- [VAEs Section](#vaes-section)
|
||||
- [Attention Modes](#attention-modes)
|
||||
- [Model-Specific Prompts](#model-specific-prompts)
|
||||
- [Recursive Cartesian Prompt Builder](#recursive-cartesian-prompt-builder)
|
||||
- [Dashboard Interface](#-dashboard-interface)
|
||||
- [Header Bar](#header-bar)
|
||||
- [Toolbar](#toolbar)
|
||||
@@ -117,6 +121,12 @@ Stop guessing which Sampler, Scheduler, Prompt, Denoise, Model, Lora or CFG valu
|
||||
* **VAE Batching:** Includes a `vae_batch_size` input to batch decode images, significantly speeding up large grid runs.
|
||||
* **Live Dashboard Updates:** Configure `flush_batch_every` to update the dashboard incrementally (e.g., every 4 images) instead of waiting for the entire batch to complete.
|
||||
* **Remote VAE Support:** Offload VAE decoding to remote servers (HuggingFace endpoints or local) for 20-30% faster generation and lower VRAM usage.
|
||||
* **VAE Selection & Iteration:** Test multiple VAEs per config — add individual VAEs from a searchable dropdown or expand entire VAE folders. Each VAE becomes a dimension in the Cartesian grid.
|
||||
* **Attention Mode Testing:** Grid-test different attention implementations (`default`, `sageattn`, `xformers`, etc.) as a combinatorial dimension. Find the optimal attention backend for your hardware.
|
||||
* **CLIP Encoding Combinators:** Advanced prompt syntax with `AND`, `CAT`, `AVG(weight)`, and `BREAK` keywords for multi-segment conditioning. `AND` creates separate conditioning entries, `CAT` concatenates token contexts, `AVG(0.5)` blends prompts, and `BREAK` forces new 77-token chunks.
|
||||
* **Model-Specific Prompts:** Per-config prompt prefix and suffix fields for model-appropriate quality tags (e.g., prepend `"masterpiece, best quality"` for anime models, append `"4k, photorealistic"` for realistic models).
|
||||
* **Recursive Cartesian Prompt Builder:** Visual chip-based prompt editor with `{option1|option2|option3}` syntax that expands into all combinations. Nest multiple groups for exponential prompt variation.
|
||||
* **Model & LoRA On/Off Switches:** Toggle individual models and LoRAs on/off without removing them from the config. Bypassed entries are omitted from both the preview JSON and the generated config output.
|
||||
|
||||
### 🎨 Interactive Dashboard (The "IDE")
|
||||
* **Infinite Canvas with Pan/Zoom:** Google Maps-style navigation with mouse drag, mousewheel zoom, and keyboard shortcuts.
|
||||
@@ -130,6 +140,10 @@ Stop guessing which Sampler, Scheduler, Prompt, Denoise, Model, Lora or CFG valu
|
||||
* **Auto-Load Sessions:** Dashboard automatically loads when generation starts — no manual session name entry needed.
|
||||
* **Session Management:** Save and load previous testing sessions directly from the UI.
|
||||
* **Keyboard Navigation:** `Space` to scroll rows, `Arrow Keys` to pan, `+/-` to zoom, `0` to reset, `F` to auto-fit.
|
||||
* **Real-Time ETA Progress Bar:** Shows estimated time remaining during generation with a progress bar in the dashboard header.
|
||||
* **Unified Settings Panel:** Gear icon (⚙️) opens a consolidated settings panel with Session management, Grid Display controls, Export Favorites, and Scan External Directory — replacing the old separate SESSION button.
|
||||
* **Scan External Directory:** Load image sessions from any folder (including other ComfyUI instances) into the dashboard for viewing and filtering. Reads PNG metadata for full config display.
|
||||
* **Export Favorites:** Copy all favorited images to a dedicated export folder with optional CivitAI metadata packing for sharing.
|
||||
|
||||
### ⚡ The "Revise & Generate" Workflow
|
||||
* **One-Click Revision:** Click "REVISE" on any image to open a detail view.
|
||||
@@ -421,6 +435,9 @@ Config Arrays are the building blocks of your testing grid. Each array defines a
|
||||
* **Schedulers:** Comma-separated list of schedulers (e.g., `"normal, karras, exponential"`).
|
||||
* **Steps:** Comma-separated list of step counts (e.g., `"20, 30, 40"`).
|
||||
* **CFG:** Comma-separated list of CFG scale values (e.g., `"6.0, 7.0, 8.0"`).
|
||||
* **Samplers Dropdown:** Searchable dropdown auto-populated with all available samplers from the connected sampler node. Select multiple to add them as a comma-separated list.
|
||||
* **Schedulers Dropdown:** Searchable dropdown auto-populated with all available schedulers. Works the same as the samplers dropdown.
|
||||
* **Attention Mode:** Dropdown selector for attention implementations (`default`, `sageattn`, `xformers`, etc.). Select multiple to test as a grid dimension.
|
||||
|
||||
#### Iteration Count Display
|
||||
|
||||
@@ -439,6 +456,7 @@ Updates in real-time as you add/remove items.
|
||||
* **📋 Duplicate Config** — Creates an exact copy of this config array (useful for variations).
|
||||
* **🗑️ Delete Config** — Removes this entire config array.
|
||||
* **▼ Collapse/Expand** — Toggle visibility of Models and LoRAs sections.
|
||||
* **➕ Add VAE** — Adds a new VAE slot to the VAEs section for testing multiple VAE decoders.
|
||||
|
||||
### Models Section
|
||||
|
||||
@@ -450,6 +468,8 @@ Each model card includes:
|
||||
- **Folder (Separate):** Expand folder — each model becomes a separate test. Multiplies iteration count by number of models in folder.
|
||||
- **Folder (Combined):** *Reserved for future use.*
|
||||
* **🗑️ Remove Model:** Deletes this model from the config (at least one model is always required).
|
||||
* **🔘 On/Off (Bypass) Switch:** Toggle a model on or off without removing it. Bypassed models are grayed out and excluded from the generated config output.
|
||||
* **🔍 CivitAI Model Lookup:** Opens a metadata panel showing model info fetched from CivitAI (name, base model, tags, creator, description). Includes a direct link to the model page.
|
||||
|
||||
### LoRAs Section
|
||||
|
||||
@@ -466,6 +486,7 @@ Each LoRA card includes:
|
||||
* **🔍 Lookup Trigger Words:** Opens the Trigger Word Lookup Modal (fetches from CivitAI API).
|
||||
* **🚫 Bypass Trigger Fetch:** Toggle to prevent automatic trigger word lookup for this LoRA.
|
||||
* **🗑️ Remove LoRA:** Deletes this LoRA from the config (at least one is always required).
|
||||
* **🔘 On/Off (Bypass) Switch:** Toggle a LoRA on or off without removing it. Bypassed LoRAs are grayed out and excluded from both the preview JSON and the Python node's `config_json` output.
|
||||
|
||||
### LoRA Trigger Words
|
||||
|
||||
@@ -561,6 +582,47 @@ Combine multiple LoRAs into a single generation by adding multiple LoRA slots to
|
||||
|
||||
**Mixed Expansion + Manual:** You can combine folder expansion with manual stacking — for example, a combined quality folder plus a specific character LoRA stacked together.
|
||||
|
||||
### VAEs Section
|
||||
|
||||
Each VAE card includes:
|
||||
|
||||
* **VAE Name (Searchable):** Dropdown with fuzzy search through all available VAE models. Supports folder structure display.
|
||||
* **Type Selection:**
|
||||
- **Single File:** Test just this specific VAE.
|
||||
- **Folder (Separate):** Expand folder — each VAE becomes a separate iteration, multiplying the total grid size.
|
||||
* **🗑️ Remove VAE:** Deletes this VAE from the config.
|
||||
|
||||
VAEs are treated as a Cartesian dimension just like samplers or models. If you add 3 VAEs, your total iteration count is multiplied by 3.
|
||||
|
||||
### Attention Modes
|
||||
|
||||
Select which attention backend implementations to test. Available options depend on your system (e.g., `default`, `sageattn`, `xformers`). Like other parameters, you can select multiple modes and they become a grid dimension.
|
||||
|
||||
### Model-Specific Prompts
|
||||
|
||||
Per-config prompt prefix and suffix fields that are prepended/appended to all prompts generated by that config array:
|
||||
|
||||
* **Prompt Prefix:** Text prepended to the positive prompt (e.g., `"masterpiece, best quality, "` for anime models).
|
||||
* **Prompt Suffix:** Text appended to the positive prompt (e.g., `", 4k, photorealistic"` for realistic models).
|
||||
|
||||
These fields are especially useful when testing multiple models that require different quality tags or style descriptors.
|
||||
|
||||
### Recursive Cartesian Prompt Builder
|
||||
|
||||
A visual chip-based prompt editor that supports variation groups using `{option1|option2|option3}` syntax:
|
||||
|
||||
```
|
||||
a {photo|painting|sketch} of a {cat|dog} in a {forest|city}
|
||||
```
|
||||
|
||||
This expands into all combinations (3 × 2 × 2 = 12 prompts):
|
||||
- "a photo of a cat in a forest"
|
||||
- "a photo of a cat in a city"
|
||||
- "a painting of a dog in a forest"
|
||||
- ... etc.
|
||||
|
||||
The editor provides a visual chip interface for building and editing these variation groups. Each group is displayed as a clickable chip that can be expanded to add/remove options. Multiple groups can be nested for exponential prompt variation.
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ Dashboard Interface
|
||||
@@ -572,12 +634,14 @@ Combine multiple LoRAs into a single generation by adding multiple LoRA slots to
|
||||
* **Zoom Controls:** `⊙` (reset), `−` (zoom out), `+` (zoom in).
|
||||
|
||||
### Toolbar
|
||||
* **Session Controls:**
|
||||
- Dashboard auto-loads when connected to sampler and generation starts.
|
||||
- **LOAD** to view previous results by session name.
|
||||
- **SAVE** to persist current state to disk.
|
||||
- **DELETE** to remove session and all images.
|
||||
|
||||
* **⚙️ Settings Panel (Gear Icon):** Opens a unified settings panel with four sections:
|
||||
- **Session:** Name input + SAVE/LOAD buttons. Dashboard auto-loads when connected to sampler and generation starts.
|
||||
- **Grid Display:** Go to Image #, Column Count, Reset Zoom controls.
|
||||
- **Export Favorites:** Export starred images to a dedicated folder with optional CivitAI metadata packing.
|
||||
- **Scan External Directory:** Load image sessions from any folder path into the dashboard.
|
||||
|
||||
* **ETA Progress Bar:** During generation, a real-time progress bar appears in the header showing estimated time remaining and completion percentage.
|
||||
|
||||
* **Filter Groups:** Click colored buttons to toggle visibility:
|
||||
- **Model** (Purple): Filter by checkpoint
|
||||
- **Sampler** (Cyan): Filter by sampler type
|
||||
@@ -585,9 +649,9 @@ Combine multiple LoRAs into a single generation by adding multiple LoRA slots to
|
||||
- **Denoise** (Red): Filter by denoise value
|
||||
- **LoRA** (Orange): Filter by LoRA configs
|
||||
- **Shift+Click:** Isolate single filter (deselect all others)
|
||||
|
||||
|
||||
* **Sort Button:** Cycles between **Sort: Oldest** (default), **Sort: Newest**, and **Sort: Fastest** (by generation time). Preference is saved to localStorage.
|
||||
|
||||
|
||||
* **Fullscreen Button (⛶):** Expand dashboard to fill entire screen.
|
||||
|
||||
### Navigation & Controls
|
||||
@@ -972,6 +1036,10 @@ The Config Builder generates JSON in the exact format required by the Ultimate S
|
||||
"cfg": [6.0, 7.0, 8.0],
|
||||
"lora": "style.safetensors:0.8:0.8 + detail.safetensors:0.5:0.5",
|
||||
"model": "base_xl.safetensors",
|
||||
"vae": ["vae_v1.safetensors", "vae_v2.safetensors"],
|
||||
"attention_mode": ["default", "sageattn"],
|
||||
"prompt_prefix": "masterpiece, best quality, ",
|
||||
"prompt_suffix": ", 4k, detailed",
|
||||
"lora_omit_triggers": ["masterpiece", "best quality"],
|
||||
"lora_triggerwords_append_settings": {
|
||||
"style.safetensors": "positive_start",
|
||||
@@ -980,6 +1048,9 @@ The Config Builder generates JSON in the exact format required by the Ultimate S
|
||||
"lora_bypass_states": {
|
||||
"technique_lora.safetensors": true
|
||||
},
|
||||
"model_bypass_states": {
|
||||
"old_model.safetensors": true
|
||||
},
|
||||
"lora_strength_lock": {
|
||||
"style.safetensors": true
|
||||
}
|
||||
@@ -992,6 +1063,10 @@ The Config Builder generates JSON in the exact format required by the Ultimate S
|
||||
- **Array:** `["euler", "dpmpp_2m"]` or `[6.0, 7.0, 8.0]`
|
||||
- **LoRA string:** `"name:model_str:clip_str"` — stack with ` + `
|
||||
- **Folder:** `"path/"` (separate) or `"path/*"` (combined)
|
||||
- **VAE:** Single VAE name or array of VAE names (each becomes a grid dimension)
|
||||
- **Attention mode:** Single mode string or array of modes to test
|
||||
- **Prompt prefix/suffix:** Strings prepended/appended to all prompts in this config
|
||||
- **Bypass states:** Object mapping model/LoRA names to `true` (bypassed) or `false` (active)
|
||||
|
||||
---
|
||||
|
||||
@@ -1049,6 +1124,51 @@ Fetch complete metadata for a specific LoRA from CivitAI.
|
||||
### `/configbuilder/refresh_models` (POST)
|
||||
Signal to clear frontend caches (called by ComfyUI when refreshing node definitions).
|
||||
|
||||
### `/configbuilder/lookup_model_metadata` (POST)
|
||||
Fetch metadata for a checkpoint model from CivitAI (same as LoRA metadata but for models).
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"model_name": "realistic_vision_v5.safetensors"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"name": "Realistic Vision V5.1",
|
||||
"model_name": "Realistic Vision",
|
||||
"base_model": "SD 1.5",
|
||||
"description": "...",
|
||||
"tags": ["realistic", "photorealistic"],
|
||||
"url": "https://civitai.com/models/12345",
|
||||
"hash": "abc123...",
|
||||
"creator": "SG161222"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `/configbuilder/scan_directory` (POST)
|
||||
Scan an external directory for image sessions and load them into the dashboard.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"path": "D:/other_comfyui/output/benchmarks/my_session"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"images_found": 42,
|
||||
"manifest": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Locations
|
||||
@@ -1097,6 +1217,7 @@ ComfyUI/output/benchmarks/model-data/
|
||||
| `+/-` | Zoom in/out |
|
||||
| `0` | Reset zoom to 1:1 |
|
||||
| `F` | Auto-fit first row to viewport width |
|
||||
| `Escape` | Close active modal/popup (priority: Revision > Filters > Settings > Fullscreen) |
|
||||
|
||||
### Config Builder
|
||||
| Shortcut | Action |
|
||||
@@ -1110,6 +1231,24 @@ ComfyUI/output/benchmarks/model-data/
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
### Update 2/16/26 — Config Builder & Dashboard Overhaul
|
||||
* 🎛️ **VAE Selection & Iteration:** Test multiple VAEs per config with searchable dropdown and folder expansion. VAEs are a full Cartesian dimension.
|
||||
* 🧠 **Attention Mode Testing:** Grid-test different attention implementations (`default`, `sageattn`, `xformers`) as a combinatorial dimension.
|
||||
* 🔗 **CLIP Encoding Combinators:** `AND`, `CAT`, `AVG(weight)`, and `BREAK` keywords for multi-segment conditioning and prompt blending.
|
||||
* 📝 **Model-Specific Prompts:** Per-config prompt prefix and suffix fields for model-appropriate quality tags.
|
||||
* 🔄 **Recursive Cartesian Prompt Builder:** Visual chip-based editor with `{option1|option2}` syntax expanding into all combinations.
|
||||
* 🔘 **Model & LoRA On/Off Switches:** Toggle entries on/off without removing them. Bypassed entries excluded from preview and output.
|
||||
* 🔍 **CivitAI Model Metadata Lookup:** Fetch model info from CivitAI directly in the Config Builder (same as existing LoRA lookup).
|
||||
* 📋 **Sampler/Scheduler Dropdowns:** Searchable dropdowns auto-populated from the connected sampler node, replacing free-text CSV entry.
|
||||
* ⚙️ **Unified Settings Panel:** SESSION button merged into a gear icon settings panel with Session, Grid Display, Export Favorites, and Scan Directory sections.
|
||||
* 📊 **Real-Time ETA Progress Bar:** Shows estimated time remaining during generation in the dashboard header.
|
||||
* 📂 **Scan External Directory:** Load image sessions from any folder into the dashboard for viewing and filtering.
|
||||
* 📦 **Export Favorites:** Copy starred images to a dedicated folder with optional CivitAI metadata packing.
|
||||
* 🛑 **Cancel During Text Encoding:** Properly cancels the entire run when interrupted during the text encoding step.
|
||||
* 🔄 **Connected Node Change Detection:** Detects when upstream nodes have changed for smarter job resuming.
|
||||
* ⌨️ **Close Modal Hotkeys:** Escape key closes modals in priority order (Revision > Filters > Settings > Fullscreen).
|
||||
* 🧹 **Dashboard Topbar Consolidation:** Streamlined header with condensed controls.
|
||||
|
||||
### Update 2/5/26 — Code Refactoring & Performance Improvements
|
||||
* 🏗️ **Major Code Refactoring:** Reorganized codebase into 6 modular files for better maintainability (`trigger_words.py`, `batch_encoding.py`, `manifest_utils.py`, `model_loader.py`, `image_generation.py`, `generation_orchestrator.py`).
|
||||
* 🎯 **CLIP Skip Support:** Control CLIP layer usage with `clip_skip` parameter. Essential for anime models (-2) vs realistic models (0). Supports arrays for testing multiple values.
|
||||
|
||||
Reference in New Issue
Block a user