Merge branch 'claude/confident-bhaskara'

This commit is contained in:
JasonHoku
2026-02-16 14:56:37 -08:00
3 changed files with 452 additions and 33 deletions
+2 -1
View File
@@ -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
View File
@@ -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.
+147 -8
View File
@@ -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.