27 KiB
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 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 (
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.