Wires the existing add_random_seeds_to_gens parameter (already in
generation_orchestrator.py:824) through the full Run Settings pipeline:
1. Builder UI: state default + _addRunSetting widget (int, 0-100, default 0)
2. config_builder_node.py: emits into session_settings in configs_json
3. sampler_node.py: reads from session_settings, overrides the local variable
The generation loop already handles it — generates N extra images per config
with random seeds when > 0. Previously hardcoded to 0 with no UI path.
Based on PR #21 by external contributor, rebased onto current main with
full pipeline wiring.
Tests: 118/118 pass.
Phase 3 smoke-test feedback from Jason:
- Notice is too subtle — switch from italic gray to dark-red
prominent style (border-left, dark-red tinted bg, bold link)
- Add notice in 3 more places:
1. Run Settings 'LoRA Triggerwords Mode' (when != None and companion
missing — toggles dynamically on select change)
2. 'LoRA Metadata Lookup' modal (showLoraMetadataModal)
3. '🔎 LoRA Trigger Words Lookup' modal (Omit list lookup)
DOM methods only - no innerHTML. Placeholder pattern in modals so the
notice appears in correct visual order despite async availability check.
Adds small italic notice in 3 sections when ComfyUI-USCG-CivitAI is
not installed:
1. Model 'Lookup Metadata from CivitAI' button section
2. LoRA 'Auto Append Trigger Words' section
3. LoRA 'Lookup Metadata from CivitAI' button section
Notice: 'Plugin feature — install [ComfyUI-USCG-CivitAI](github) for
auto-trigger detection + metadata lookup.'
DOM methods only - no innerHTML. Async-checks via /uscg-civitai/health.
Distribution panel sections now async-check for the companion via
/uscg-distribution/health. 200 → renders normal panel; 404 → renders
install card (Comfy Manager + GitHub link + Re-check button).
DOM methods only - no innerHTML. Saved Distribution settings preserved
in node.state when companion isn't installed - no data loss.
The 'Remote URL' VAE type now asynchronously checks for the
ComfyUI-USCG-RemoteVAE companion plugin and renders either:
- An install card (Comfy Manager / GitHub instructions + Re-check button)
when companion is missing.
- A working preset dropdown + URL input populated from the companion's
/uscg-remote-vae/endpoints route when companion is installed.
DOM methods only - no innerHTML. Saved configs preserve their 'remote:<url>'
value either way; no data loss when companion isn't installed.
Deletes the hardcoded REMOTE_VAE_PRESETS object. Adds cached detection
helpers isRemoteVaeAvailable() and loadRemoteVaeEndpoints() backed by
the companion's /uscg-remote-vae/health and /endpoints routes.
The if (isRemote) render block still references REMOTE_VAE_PRESETS as
of this commit; that branch is rewritten to use the new helpers in the
next commit (B8).
User insight: Florence2's vision encoder activations scale with input image
dimensions, not with the polygon complexity it outputs. Down-scaling the
source image BEFORE detection saves the bulk of Florence2-stage VRAM
(encoder is dominant over the decoder's KV cache for moderate sequence
lengths). Detection accuracy at 0.5 MP is essentially identical to full
resolution for face/region tasks because the polygon is coarse and gets
smoothed by GrowMask + feather + crop+paste-back downstream anyway.
Implementation in florence2_hires.run_florence2_step:
- Read step_config['florence2_input_mp'] (default 0.5; set 0 to disable)
- If source MP > target, common_upscale source down to target dims with
lanczos before passing to Florence2Run
- After detection, common_upscale the returned mask back up to source dims
with bilinear so the crop / grow / feather / inpaint / paste-back chain
operates at full source resolution as before
- Only down-scales — never up-scales (skip when source already smaller)
- Manifest entry records florence2_input_mp + florence2_detect_resized
for forensics
- Console log shows both src and detect dimensions per call
UI: new 'Florence2 Input MP' numeric field (range 0-4.0, step 0.25)
in BOTH the Builder UI's Florence2 sub-panel and the Dashboard's Upscale
Image modal Florence2 panel. Default 0.5. Tooltip explains the VRAM
tradeoff and that it only takes effect when source > this MP.
Mirrors createDefaultFlorence2Options() + render block in both
conf-builder-config-management.js (Builder) and logic_upscale_modal.js
(Dashboard), keeping the two UI surfaces in sync.
User report: 'half a face' upscaled, the other half not — classic polygon
truncation symptom. Plus 'beam search runs to 100% and fills VRAM' — Florence2
not hitting EOS within the cap because it can't generate a complete polygon
in 256 tokens, so it spends the whole budget on garbage continuations.
My earlier 256 default was a panic fix for the wrong problem. The actual
root cause of the OOM was the duplicate session-model load (fixed via
same-model short-circuit in _get_or_load_checkpoint_lora) plus stale prior-
gen models in VRAM (fixed via soft_empty_cache at upscale_runner start).
With those fixes in place, 1024 fits fine.
The model emits EOS when the polygon is complete (typically token 50-100
for a face). 1024 is the CEILING, not the typical generation length. The
KV cache is pre-allocated for the cap but doesn't actually use all of it
unless the model fails to EOS — which is exactly what happens when the
cap is too tight.
Reverted defaults across florence2_hires.py + Builder UI + Dashboard
upscale modal. Updated tooltip to explain the trade-off correctly. Bumped
UI min from 32 to 64 (32 was nonsensically low and could be hit
accidentally by step=32 spinner clicks).
Default 256 (was hardcoded). Range 32-2048 step 32. Bump higher if Florence2
polygons get truncated on complex multi-face scenes; lower for VRAM savings.
Wired:
- florence2_hires.py: reads step_config.get('max_new_tokens', 256) and stores
it on manifest_extras for forensics
- web/conf_builder/conf-builder-config-management.js: added to
createDefaultFlorence2Options() + a numeric input next to 'Output Mask
Select' in the Builder UI sub-panel
- resources/logic_upscale_modal.js: same in the dashboard's Upscale Image
modal default + UI field
Tooltip explains the VRAM trade-off (~50 tokens needed for a face polygon;
256 is 5x headroom; KV cache scales linearly).
Upscale Ratios is meaningless for florence2_hires (it inpaints a region, not the
whole image — Target MP controls the internal pass size). Tiled Sampling is also
irrelevant since Florence2 crops are too small to need tiling.
Diagnostic log line: '[Florence2HiResFix] src=(WxH) crop=(WxH) requested_MP=X
-> internal_pass=(WxH)=Y MP'. This will reveal whether target_megapixels is
actually being applied to the resize step.
User report: lowering Max Crop Res caused the crop window to miss the
detected object. Root cause: the contract-around-center logic in
_crop_image_by_mask works fine for centered bboxes but pushes the crop
window away from the bbox when the bbox is near an image edge (e.g., a face
touching the right edge of a 512-wide image with max=200 produces a crop
centered at x=296 which only partially overlaps the actual face at x>=300).
Fix per user suggestion:
- Remove min_crop_resolution + max_crop_resolution from Builder UI
- Defaults are now no-op (min=0, max=99999)
- Florence2's polygon + grow_expand + crop_padding fully determine crop region
- Python helper keeps the params for backwards compat with existing tests
Adds the Florence2 Hi-Res Fix option to the upscale mode dropdown and a
sub-panel that renders when selected: 4 detection fields, 4 crop/resize fields,
5 mask-shape fields (grow_expand + 4 feather sides), model/lora source dropdown,
and the no-detection dropdown. Hires sampling fields (denoise/steps/cfg/sampler
/scheduler) are reused from the existing hires_only mode rendering — Florence2
sets showHires=true so they appear naturally. Combos counter treats Florence2
as 1-output-per-input (no cartesian multiplier).
Adds a new SUB-GROUP 4 in the Builder UI's "Extra Model & Sampling
Options" section with all 7 params from ComfyUI's built-in
PatchModelAddDownscale node:
- block_number (int, default 3)
- downscale_factor (float, default 2.0)
- start_percent / end_percent (float 0-1, default 0.0 / 0.35)
- downscale_after_skip (bool, default true)
- downscale_method / upscale_method (combo: bicubic/nearest-exact/
bilinear/area/bislerp, default bicubic)
Toggle checkbox controls whether the patch is applied. State plumbs:
config_array.use_deep_shrink/* (JS) -> state_to_configs_json -> per-
config entries -> expand_configs base_combos -> generate_image -> looks
up PatchModelAddDownscale via NODE_CLASS_MAPPINGS and calls .patch() to
patch the model before sampling. Logs an "Applied
PatchModelAddDownscale" line on each gen for visibility.
Mirrors the existing model_sampling_override / use_advanced_sampling
patterns for consistency. Hidden on LTX video config arrays since the
patch targets the SDXL/SD UNet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
User feedback: 1 is a more sensible default for typical generation —
smaller VAE batches keep VRAM lower, and flushing every gen makes the
dashboard show progress immediately.
Updated everywhere:
- sampler_node run_tests fallback defaults (post-widget-removal)
- conf-builder-main.js initial state + migration backfill
- conf-builder-config-management.js renderRunSettingsSection
state-init defaults + _addRunSetting defaultValue
- config_builder_node.py state_to_configs_json emit fallback +
except-clause fallback for both fields
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the widget cleanup (30e3aa9), the SamplerGridTester is nearly
empty — just one required input (configs_json), six optional sockets,
and a single output. New users won't know how to wire it.
Adds a small footnote (~44px tall) at the bottom of the node via a
non-serialized DOM widget:
"Connect the configs_json from the Builder node to this node, and
connect this node to the Dashboard node"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two UX defaults users hit on first drop:
1. Default node size was ~200x100 (Comfy's default), making the HTML
overlay invisible. Bumped to 720x900 on fresh create. Existing
workflows keep their saved size (only the max-with-current is
applied, gated by an _uscgSizeInitialized flag so the size override
doesn't fight user resizes).
2. Output Preview panel defaulted to 450px which dominated the UI on
smaller nodes. Reduced to 175px (~8 lines of JSON visible at the
11px/1.4-leading default font). The drag handle still lets users
grow it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues in 7a2c3c9:
1. start_at_job's onchange handler used setDirtyCanvas() instead of
saveState() — changes never reached the lora_config widget value.
Fixed to call saveState().
2. The 6 run-settings emissions were gated on "non-default" values
(e.g. lora_triggerwords_mode only emitted when != "None"). Result:
a user setting Builder UI to its default could not override the
Generator widget if the user had previously set that widget to
something non-default. Builder UI is now authoritative — emit all
values unconditionally. start_at_job and image_format also
switched to unconditional emission for consistency.
3. session_name had no plumbing path. Added emission in
state_to_configs_json and an override branch in sampler_node so
the Builder UI's session_name field overrides the generator widget.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User wanted Run Settings to be the FIRST section, above everything
including Distribution Settings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 of consolidating run-level config into the Builder UI. The
Builder UI's "Run Settings" section is now rendered at the TOP of the UI
(was previously below config arrays) and gains six new fields with help
tooltips:
- overwrite_existing (bool, default false)
- flush_batch_every (int, default 4)
- lora_triggerwords_mode (enum: None / Append To End / Append To Start /
Read From Config, default None)
- save_conditioning_cache_to_file (bool, default false)
- enable_model_cache (bool, default false)
- vae_batch_size (int, default 4, range -1..64)
State plumbs through state_to_configs_json -> _session_settings ->
sampler_node.run_tests -> run_generation_loop. Builder UI values
override the corresponding widgets on UltimateSamplerGrid when set
(non-default). Widget values remain the fallback so existing workflows
keep working without changes.
Logs an "⚙️ <name> overridden by Builder UI: <value>" line whenever an
override fires, so it's obvious in the console.
Next: once the user has tested this thoroughly, the corresponding
widgets on the SamplerGridTester node can be removed/hidden to
simplify the generator UX (deferred — coordinate with user).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These changes belong with the earlier "preview panel uses /configbuilder/preview"
work (b6e2344) but were dropped from that commit. Folding in now:
- Adds .cb-preview-panel + resize-handle + header/content CSS used by the
renderPreviewSection function shipped in 991631a.
- Removes the obsolete addNavIcon("📄", "JSON Preview", ...) sidebar entry.
- Cleans the cb-sec-preview ID out of the scroll-spy section list.
No behavior change beyond what was already committed — fills in the
visual/CSS gap the committed JS depends on.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a dropdown to the Builder UI's Run Settings section letting users
pick the saved image file format. Default remains webp (current
behavior). State plumbed through state_to_configs_json ->
_session_settings -> orchestrator -> save_image_to_disk.
PNG users will see lossless saves; JPG users smaller files at quality 95.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the user types '0.5, 1.0' they want 2 outputs (lora at 0.5, lora
at 1.0 — both model and clip mirroring), not 4 outputs (Cartesian
m×c). The bug was that we wrote both _model AND _clip side-channel
arrays, so Python emitted 'name:[m]:[c]' which the orchestrator
correctly interpreted as Cartesian.
JS: collapse the two-input compare section to a single 'Strengths:'
input. Never write _clip to lora_weight_arrays.
Python: process_lora_array now emits 'name:[m_arr]' (no clip part)
when only _model array is present. The generator's existing
parse_lora_definition defaults clip=model when the third segment
is missing, giving N configs with locked semantics.
Drops the Cartesian path entirely. Backward compat retained: if a
saved workflow has both arrays (legacy), the dual-bracket form is
still emitted so the orchestrator can Cartesian-expand as before.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the two separate '+ Compare Strengths' / '+ Compare CLIP'
buttons with a single button that toggles a section showing two
labeled inputs ('Model Strengths:' and 'Clip Strengths:'). When
Strength Lock is ON, only the Model input shows (CLIP mirrors
automatically — same as today).
Also fixes button-text-not-updating bug: previously the button
required a full re-render to swap between '+ Compare Strengths' and
'✕ Array' text, so toggling the input visible left the button stuck
at '+ Compare Strengths' until page reload. Activation now persists
arrays as [1] (Python passes through unchanged) and uses array
existence rather than length>1 as the active indicator, so reloads
correctly restore the active state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
convertStateToConfigs() was deleted in the previous commit; the JS
preview now POSTs to the new endpoint and Python is the sole transformer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces synchronous JS convertStateToConfigs (~280 lines) with a
debounced (800ms) fetch to the new endpoint. Preview now renders
literally what generate_config produces — drift impossible by
construction.
Stale-state badge surfaces network failures without blocking editing.
Loading placeholder appears during the first-paint debounce window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds SeedVR2 as a new upscale mode alongside HiRes Fix, Model Upscale,
and Model+HiRes. Requires ComfyUI-SeedVR2_VideoUpscaler to be installed
as a dependency — the plugin calls its nodes programmatically.
Data Model:
- New seedvr2 options object on each upscale step with all SeedVR2
parameters: dit_model, resolution, max_resolution, seed,
color_correction, batch_size, noise scales, blocks_to_swap,
attention_mode, offload_device, cache_model, VAE tiling options
- _defaultSeedVR2Options() / createDefaultSeedVR2Options() helpers
- Migration: ensureStepFields adds seedvr2 defaults to existing configs
Builder UI (conf-builder-config-management.js):
- "SeedVR2 Upscale" added to mode dropdown
- When selected, shows full SeedVR2 settings: DiT model selection
(10 variants: 3B/7B x fp16/fp8/GGUF/sharp), resolution, seed,
color correction, noise scales, batch size, VRAM optimization
(blocks_to_swap, attention_mode, offload), VAE tiling with
conditional encode/decode tile size and overlap fields
Dashboard (logic_upscale_modal.js):
- Same SeedVR2 mode and fields in renderPipelineEditor via _renderStep
- All fields use existing _makeSelect/_makeNumber/_makeCheckbox helpers
Python Execution (image_generation.py):
- New seedvr2_upscale(pil_image, seedvr2_config) function
- Imports SeedVR2 nodes from NODE_CLASS_MAPPINGS at runtime
- Calls SeedVR2LoadDiTModel → SeedVR2LoadVAEModel → SeedVR2VideoUpscaler
- Converts PIL→tensor→SeedVR2→tensor→PIL
- Clear error if SeedVR2 not installed
Pipeline Integration (generation_orchestrator.py):
- Inline upscale path: mode=="seedvr2" branch decodes current latent
to PIL, calls seedvr2_upscale, saves result, updates manifest
- Supports chained steps: non-final SeedVR2 steps re-encode to latent
for the next step in the pipeline
Preset compatible: seedvr2 config serializes naturally as JSON, saved
and loaded with upscale presets without any special handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When toggling fullscreen, the iframe reload was reading the session name
from the node's widget (default "my_session") instead of the session
the user had actually selected in the Dashboard picker.
Now the Dashboard sends its current session name in the fullscreen
postMessage, and the handler prefers that over the widget default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add rerenderConfigSection(node, arrayIdx) that rebuilds only one config
array section instead of the entire UI. debouncedRenderUI now accepts
optional arrayIdx parameter — when provided, uses partial re-render.
This avoids the full DOM rebuild (7k+ nodes) when making changes within
a single config section (add/remove model/lora, toggle settings). Falls
back to full renderUI if the target section isn't found in the DOM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Performance improvements for Builder UI model/LoRA loading:
localStorage persistent cache:
- Model lists cached to localStorage on first fetch
- On page reload, cached data renders instantly (no network wait)
- Cache expires after 24 hours, cleared on explicit Refresh Models
- Saves all categories: checkpoints, diffusion, GGUF, text encoders,
VAEs, upscale models, samplers, schedulers, clip types
Background count polling:
- New /configbuilder/model_counts endpoint returns file counts per
category (fast — uses folder_paths internal stat-based cache)
- Frontend polls every 30s, compares counts to last known values
- On change: auto-refreshes model lists and re-renders UI
- No manual Refresh Models needed when adding new models
Deduplicated /object_info:
- Shared _getObjectInfo() cache ensures the expensive /object_info
endpoint is called at most once per session (was called 2-3x)
- getAvailableLoras() and getAvailableModels() share the cached response
Polling lifecycle:
- Starts after initial render, stops when node is removed
- Only active while ConfigBuilder node exists on canvas
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>