- append_items never replaces an unreadable manifest with a fresh one (raises, file untouched) and
writes through the retrying atomic helper; the sweep spec write retries PermissionError too
- breaker only pauses while jobs remain; a paused sweep with nothing in flight or pending completes
instead of blocking new sweeps with SweepBusy
- sampler IS_CHANGED returns NaN when configs_json is linked (absent from kwargs), so a re-queued
sweep is never served from ComfyUI's cache
- Start At Job # = N skips N-1 jobs like the orchestrator
- dry_run / api_start expand the sweep in an executor, off the event loop
- Builder sweep section re-reads configArray.workflow on every render (load/remove replaced it)
- favorites export and GENERATE NEW skip workflow-sweep items (no template)
- Pairwise Review excludes all wf_* per-job keys
- test: same-sweep attach waits for the feeder's first enqueue (pre-existing race)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- lazy shell: SAVE never persists __uscg_lazy__ (saveState guard + save_manifest pop); the manifest
fetch route heals files that already carry it
- dashboard node: a Builder session switch does a full reload (session_name from the payload, not
the stale widget) instead of re-fetching the old session
- dashboard node: window message handlers only accept a dashboard iframe as source
- scanned ids are namespaced by session (same relpath in two sessions no longer collides on merge);
rescan merge donates each old id once and only via unambiguous basenames
- deferred upscale resume is keyed on base_fp == config_fp (fixed-seed grids upscale every cell)
- legacy distributed companion ignores the preview pass: master keeps the upscale phase
- connected latent: recompute w/h before gen_w/gen_h so 16x models record real dimensions
- Start on an identical paused sweep resumes it
- outputFromKey keeps subgraph node ids ("57:35") whole
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
html_generator.get_html_template now emits a shell (empty fullManifest flagged
__uscg_lazy__, inline_manifest=True restores the old behaviour). New route
GET /config_tester/manifest?session=<name>: compact JSON, weak ETag, 304 on
If-None-Match, gzip, torn-read retry (logic in run_api.load_manifest_for_fetch).
/config_tester/dashboard gets gzip + content ETag. The SPA boots with one fetch
(no more double load), resync is conditional and non-destructive, and
web/dashboard.js asks the iframe to re-fetch (reload_manifest) instead of
parsing data out of the Generator's executed message. Also fixes the dead
session-name regex in dashboard_node.py.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
No new top-level Builder field: workflow_output gains optional media/frames_from keys, emitted only
for video, so the rule-5 checklist does not apply and image configs JSON is unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Record session_settings + has_optional_inputs in manifest meta (run_meta_extras) so the
Dashboard "Full pass for survivors" can queue POST /config_tester/run headless; the iframe
falls through to it when the parent has no Builder / no queuePrompt.
- Exclude the deliberately omitted "vae" key from _EXPECTED_CONFIG_KEYS; test_examples asserts zero drift.
- minimize_reloads: log VAE switches + a "changed nothing" note, accurate tooltip/README wording.
- examples/README: Klein base-variant note; fix stale LTX stub comment.
- Tests: run_meta_extras, job order, state_to_configs_json, e2e full pass (new file).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Rich dragstart (asset-info JSON, uri-list, plain, DownloadURL; reference not copy), a modal button that
fills the selected/only/new Load Image via the parent bridge, standalone Download image fallback.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Load Session restores Workflow Sweep arrays from meta.configs_json (workflowConfigToArray) and
tells the user when an older sweep session has no saved configs. Builder Start shows 'attached'.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Identity = session + ordered job fingerprints + start_at_job + overwrite_existing; anything
else gets a clear 409. GET /config_tester/sweep/job_prompt returns one job's API prompt.
Sweep manifests now save their workflow configs in meta.configs_json.
pause/resume emit a progress event.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
New ES module conf-builder-workflow-sweep.js (load API workflow, output picker,
suggested bindings, node table, axis editors, dry run modal, start/pause/resume/cancel
with status polling), wired additively into the config-array list, default state and
migrations. Dashboard ETA bar shows 'Sweep job' with an error count and hides REVISE
for workflow items. README, ProjectStructure and Roadmap updated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PREVIEW badge on cards and a modal note, a Pass filter row (hidden until previews exist),
previews superseded by a full image (config_fp/base_fp match) hidden by default, a preview bar
with 'Full pass for survivors (N)'. The button asks web/dashboard.js (postMessage) to switch
Preview pass off in the matching Config Builder and call app.queuePrompt; a marked stub
covers a future run API; fallback is an instructions toast.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Session-level setting (not a per-config key). state_to_configs_json emits
_session_settings.metrics only when enabled and non-empty; GET
/configbuilder/metrics_info reports built-ins and scorer-pack install status.
Faster numpy fallback; README and ProjectStructure updated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
State keys job_order, preview_pass{enabled,steps,scale,cfg_override}, skip_rejected_previews,
only_favorited_previews: new-state defaults + migration, Run Settings controls, emission with
coercion in state_to_configs_json, tests, README/ProjectStructure.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
batch_encode_with_cache/batch_encode_prompts take te_batch_size (default 1 =
off, DEFAULT_TE_BATCH_SIZE) and a hidden te_batch_verify; batched prompts are
cached, leftovers take the unchanged per-prompt loop, and the summary line
reports batched prompts/forwards. Builder state default + migration, Run
Settings control, state_to_configs_json emission, orchestrator read, README.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
From #21: _expand_ltx_entry ran positive/negative through to_list, so the
Builder's [["p1","p2"]] reached the LTX encoder as a list; it now uses
parse_prompt_input_nested like expand_configs. The text-encoder dropdown
falls back to ["None"] when the list is empty. convertConfigsToConfigArrays
keeps the per-entry model type for builder-schema models[].
Refs #21
Co-authored-by: DRuggeri <DRuggeri@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Builder Run Settings already emitted add_random_seeds_to_gens through
_session_settings; this adds the missing new-state default and the
saved-config migration, tests for the emit -> sampler_node unwrap ->
expand_configs path, and a README Run Settings entry.
The extra seeds are now derived from the base seed
(config_utils.derive_extra_seeds) instead of unseeded random.randint, as the
README always promised. Besides reproducibility this makes stop/resume work:
the skip check matches on seed, so an interrupted run can now skip the
extra-seed images it already rendered.
Ported from #21 (it conflicts after the repo restructure). Closes#21.
Co-authored-by: DRuggeri <DRuggeri@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
MOSS-Image (Flux.2 latent: 128ch, 16 px per cell) jobs were built with the
SD-era `h // 8`, so every image sampled a latent with 4x the cells and decoded
at 2x the requested size (832x1216 -> 1664x2432): ~10x slower than the official
ComfyUI template (~120 s vs ~10 s per image on the 5060 Ti).
- model_loader: get_latent_downscale_ratio() reads latent_format.spacial_downscale_ratio
(8 for SD/SDXL/Flux.1, 16 for the Flux.2 family); make_empty_latent() builds the
job latent and warns when WxH is not a multiple of the ratio. Both orchestrator
latent sites (main loop + distributed worker) use it; hires-fix latent upscale,
tiled hires sampling and VAE tiling divisors no longer assume 8.
- CLIP type: resolve_clip_type() warns on unknown types instead of silently using
stable_diffusion, and when clip_type is left at the default it follows the
detected model family (MOSSImage -> moss_image, Flux2 -> flux2, ...). MOSS loaded
as a generic Qwen3.5 TE before (wrong chat template). The Builder's CLIP Type
list is now read from ComfyUI's CLIPLoader/DualCLIPLoader (static list = fallback,
now including moss_image); LS_CACHE_VERSION 3 refreshes cached lists.
- Skip Layer Guidance: new Extra Model & Sampling sub-group wrapping
SkipLayerGuidanceDiTSimple (double/single layers, start/end %), plumbed like Deep
Shrink (builder emission, expand_configs, orchestrator, generate_image). Defaults
match the official MOSS-Image workflow (single block 22, 0-50%).
- tests: latent geometry + clip type resolution, dynamic clip type lists, SLG
config emission/expansion (24 new; the 15 pre-existing torch-stub failures unchanged).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>