29 KiB
ComfyUI Registry Security Requirements
This node is published to the ComfyUI Registry. Every version is scanned by an automated
security scanner. Versions that fail the scan receive NodeVersionStatusFlagged status and
are hidden from ComfyUI Manager. All contributors must follow these rules:
- No
import requests— Useurllib.request(stdlib) instead. The scanner flags outbound HTTP libraries as potential data exfiltration vectors. - No
subprocess,os.system,eval(),exec()— These are blocked as arbitrary code execution risks. - No runtime
pip install— Package installation must go throughrequirements.txtonly, never called at runtime. - No code obfuscation — No encoded strings, multi-statement tricks, or undefined variable patterns.
- No custom file-serving endpoints — Use ComfyUI's built-in
WEB_DIRECTORYfor JS and the built-in/viewendpoint for images. Never useweb.FileResponseor create custom routes that serve files from disk. For external images, create symlinks into the output directory and use/view?filename=X&type=output&subfolder=Y. - Path containment on all filesystem operations — Every endpoint that touches the
filesystem must validate that resolved paths stay within the expected base directory
(e.g.,
output/benchmarks/). Useos.path.realpath()+startswith()checks. - Sanitize all user-supplied path components — Session names, filenames, etc. must be
sanitized with
re.sub(r'[^\w\-]', '', name)oros.path.basename()before use.
Reference: https://blog.comfy.org/p/comfyui-2025-jan-security-update
Updated Project File Structure
ComfyUI-Ultimate-Sampler-Grid/
├── __init__.py # Server Routes & Node Mappings
├── sampler_node.py # Main "Sampler Grid" Node
├── dashboard_node.py # "Dashboard Viewer" Node
├── config_builder_node.py # "Config Builder" Node
├── json_text_node.py # "Smart JSON" Node
│
├── generation_orchestrator.py # The "Conductor" (Loop Logic)
├── image_generation.py # KSampler & VAE Wrappers
├── model_loader.py # Checkpoint/LoRA Loading Logic
├── 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 & 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
│ └── 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
├── report.css # Styling (Infinite Canvas, Cards, Modals)
├── logic_init.js # Bootstrapper (Load JSON, Init State)
├── logic_state.js # State Store (Redux-style: filters, favorites)
├── logic_pipeline.js # Data Processing (Filter -> Sort -> Layout)
├── logic_virtual.js # Virtual DOM / Infinite Scroller Engine
├── logic_ui.js # UI Renderer (DOM creation, Modal handling)
├── logic_events.js # Interaction Handler (Hotkeys, API calls)
└── logic_utils.js # Helpers (Debounce, Formatters)
Output Directory Structure (Generated at Runtime):
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
Project Architecture Overview
ComfyUI-Ultimate-Sampler-Grid is a hybrid application.
- The Backend (Python): Runs inside ComfyUI. It orchestrates the grid generation, manages resources (caching/loading), and serves the API.
- The Frontend Bridge (JS): Runs in the ComfyUI browser tab. It registers the custom nodes and widgets.
- The Dashboard SPA (HTML/JS/CSS): A standalone, virtualized "IDE" for viewing results. It is generated by the backend but runs entirely in the client's browser, communicating back to the server only when saving changes.
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, create symlinks, use built-in /view {directory_path, session_name} |
1. The Backend: Core Logic & Orchestration
Located in the root directory.
-
sampler_node.py(The Interface) -
Role: The primary ComfyUI node (
SamplerGridTester). Validates inputs and passes control to the orchestrator. -
Key Functions:
run_tests()(entry point → delegates togeneration_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()returnsNaNwhen 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 — 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:
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
InterruptProcessingExceptionduring text encoding AND image generation. Flushes pending VAE batch and saves manifest before stopping. -
ETA Tracking: Sends
ultimate_grid.progressevents to dashboard viaPromptServer.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_vaefor reverting to "Default". -
image_generation.py(The Worker) -
Role: Wraps KSampler and VAE Decode.
-
Key Logic: Implements
flush_batch_with_vaeto save images and update the manifest in chunks (preventing VRAM overflow). -
config_builder_node.py(The Builder UI Backend) -
Role:
UltimateConfigBuildernode. All state stored in singlelora_configwidget 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_configwidget — 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 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 intotemplate.html, embedsmanifest.jsonasfullManifestJS 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
2. The Backend: Resource Management
Located in the root directory.
-
model_cache.py(The 3-Tier Cache) -
Role: Thread-safe resource manager. Single
ModelCacheclass. -
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 viaconcurrent.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. Auto-disabled when optional inputs connected. -
batch_encoding.py(The Accelerator) -
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 vialora_triggerwords_append_settings. Also appliesmodel_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()callsmerge_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):
{
"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]):
{
"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
3. The Frontend: ComfyUI Integration
Located in /web/.
-
dashboard.js -
Role: Registers the Dashboard node (
UltimateGridDashboard). Listens forultimate_grid.updateandultimate_grid.progressserver events. -
Key Logic: Forwards events to matching iframe via
postMessage(). Auto-loads session when update received for unloaded session. Handles fullscreen toggle via.dashboard-fullscreenCSS class. -
Widgets: "RELOAD / SHOW SESSION" button →
forceLoadSession(), "DELETE SESSION" button → POST/config_tester/delete_session -
config_builder.js(entry point, loadsconf_builder/modules) -
Role: Registers the Builder node (
UltimateConfigBuilder). Entry point for the modular Config Builder UI. -
Key Logic:
onNodeCreatedMUST be synchronous (async init runs in fire-and-forget IIFE). UsesDate.now()cache-busting on moduleimport()calls. Hooksapp.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 (
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()
4. The Frontend: The Dashboard SPA
Located in /resources/. This is the "App" users interact with.
-
logic_init.js(The Bootstrapper) -
Role: Main Entry Point.
-
Key Logic:
-
initDashboard(): The first function called. -
Data Injection: detects if
fullManifestexists (injected by Python). If not, it attempts to fetch it (View Mode). -
Lifecycle: Calls
initLogicState()->applyPipeline()->initVirtualScroller()->initGlobalEvents(). -
logic_state.js(The Brain) -
Role: Central State Store (Redux-style).
-
Key Logic:
-
StateObject: Holdsitems(all images),filteredItems(visible),selection(active card), andfavorites. -
Triple JSON Lists: Manages the logic for the "Accepted", "Favorites", and "Rejected" JSON bars at the bottom of the UI.
-
Persistence: Saves user preferences (Sort Order, Column Count) to
localStorageso they persist across reloads. -
logic_events.js(The Handler) -
Role: Interaction & API Bridge.
-
Key Logic:
-
Keyboard Shortcuts: Maps
Arrow Keys(Pan),Space(Scroll),+/-(Zoom),F(Fit),Escape(close modals with priority chain). -
API Calls: Uses
fetchto hit endpoints like/config_tester/save_manifest(when you star/reject an image) and/config_tester/delete_session. -
Live Updates: Listens for the
executedevent from ComfyUI to trigger a soft-refresh of the image list. -
logic_virtual.js(The Engine) -
Role: Virtual Scroller.
-
Key Logic: Calculates viewport geometry to render only the ~20 visible cards out of thousands, enabling high performance.
-
logic_pipeline.js(The Processor) -
Role: Filtering & Sorting.
-
Key Logic: Transforms the raw item list based on active filters (Model, Sampler, etc.) before passing it to the virtual scroller.
-
logic_ui.js(The Renderer) -
Role: DOM Manipulation.
-
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 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
- User Config: You set parameters in the Sampler Node (Python).
- Expansion:
generation_orchestrator.pyexpands this into a list of jobs. - Generation: The backend loops through jobs, using ModelCache and ConditioningCache for speed.
- Storage: Images are saved to disk; metadata is appended to
manifest.json. - Compilation:
html_generator.pyreads themanifest.jsonand all.jsfiles in/resources/, baking them into a singledashboard_htmlstring. - Display: The Dashboard Node (JS) renders this HTML in an iframe.
- Boot:
logic_init.jswakes up, loads the data intologic_state.js, andlogic_virtual.jsstarts rendering cards. - Interaction: You click "Favorite" in the UI ->
logic_events.jssends an API request to__init__.py-> Python updatesmanifest.jsonon disk.
Critical Development Gotchas
-
Dual Data Path — Frontend
convertStateToConfigs()(inconf-builder-utilities.js) and Pythongenerate_config()(inconfig_builder_node.py) are independent implementations that both transformnode.stateinto config JSON. Both must stay in sync when adding new config fields or the UI preview will mismatch the actual output. -
Widget Bridge — The
lora_configwidget (STRING type) holds the entire node state as serialized JSON. Pythongenerate_config()ignores all other widget params and reads onlylora_config. Thesamplers,schedulers,steps,cfgwidgets inINPUT_TYPESare vestigial (kept for ComfyUI schema compliance only). -
onNodeCreated Must Be Synchronous — In
conf-builder-main.js,onNodeCreatedMUST remain synchronous. Async initialization runs inside a fire-and-forget(async () => { ... })()IIFE. MakingonNodeCreateditself async breaks widget registration. -
Resources Are Inlined —
html_generator.pyreads 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). -
Manifest Merge on Save —
manifest_utils.pysave_manifest()callsmerge_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. -
Optional Inputs Disable Caches —
sampler_node.pydisables conditioning cache whenoptional_model/optional_clip/optional_positive/optional_negativeare connected.IS_CHANGED()returnsNaN(forces re-execution).check_if_job_completed()skips model/lora/prompt matching. -
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. -
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. -
Cache Busting —
conf-builder-main.jsusesDate.now()timestamp onimport()calls for module loading. Also interceptswindow.fetchto detect/object_infocalls and trigger cache refresh. Static JS served via ComfyUI's/extensions/path. -
Config Presets Path — Saved to
output/ultimate-configs/(defined in__init__.py), NOToutput/benchmarks/configs/. Session data goes underoutput/benchmarks/{session_name}/. -
Field Name Inconsistency:
favoritevsfavorited—manifest_utils.pymerge_manifest_user_changes()uses the key"favorite". But__init__.pyroutes likesave_manifestandexport_favoritescheck"favorited". When touching favorite/unfavorite logic, check which key name the specific code path uses. -
Prompt Expansion is Recursive —
parse_prompt_input_nested()inconfig_utils.pysupports 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.