- one Generator run may hold standard configs AND workflow-sweep entries: the sweep is validated
first (job 1 + busy check, nothing registered), the standard loop runs, then start_sweep launches
into the same session with one global job numbering (Start At Job counts standard jobs first).
An interrupt in the loop never starts the sweep; meta.configs_json keeps the full Builder list
(_standard_run_meta/_completion_payload helpers; next_phase on the completion event; the
dashboard keeps the ETA bar up with "Workflow sweep next…" and clears the 30 s hide timer on
any later progress event)
- frame sources: FRAME_SINKS table (VHS_VideoCombine, SaveWEBM, SaveAnimatedWEBP, SaveAnimatedPNG)
feeding the CreateVideo splice; fps read per sink, clamped to CreateVideo's 1-120 with a plan
warning; find_output_nodes fallback names; Builder label via framesFps
- tests: 33 python (mixed routing, Start At Job translation, interrupt, pre-flight busy/invalid,
post-loop busy, three sink fixtures, clamping, VHS golden unchanged), 2 node, 2 Playwright
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Builder VAE beats the sampler node's remote VAE (normalize_config_vae; the GGUF pre-check
now treats "None"/"" like Default instead of failing late)
- batch encoding interrupt: verified (no further prompt encoded after the interrupt)
- pre-encode only configs with work left: configs_needing_work() extracted and tested;
optional-input tracking stays open (fingerprints know nothing about upstream nodes)
- omit logic: "(masterpiece)"-style terms never matched (\b next to punctuation) and a blank
term omitted everything; fixed with lookarounds + blank skip; the Builder's omit-vs-lookup
warning fires only when LoRA Triggerwords Mode is None (was a false positive in every other
mode) and a console warning (omit_without_lookup_warning) prints at run start
- drag reorder: pure reorderTarget/moveItem extracted and tested; doubled handle glyph on
text-encoder rows and non-handle drops firing a move fixed; browser pass still open
- Builder slowness: section-preset GETs cached per section (one uncached fetch per row per
render before); three remaining hotspots recorded in Roadmap.md
Roadmap.md: items 77, 81, 104, 9, 10 marked DONE; 83 and 91 annotated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A "+" beside the model-strength slider adds another strength to compare (one slider when
Lock is on, model + CLIP when off; up to 8 per LoRA, duplicates greyed out). The state keeps
only the extra rows (lora_extra_strengths[name] = [{model, clip}]), row 0 stays the verbatim
base string so existing jobs keep their fingerprints. state_to_configs_json emits concrete
"name:m:c" strings on the lora axis (stacks = every combination, first LoRA slowest); the old
"Compare Strengths" comma box is removed and converted on first edit. The Iterations count
now mirrors process_lora_array (fixes the stack overcount). Dashboard: a LoRA whose name is on
every card but with several strengths keeps its label; filter buttons keep the strengths.
Tests: 33 python (shared fixture, byte-identical output without the key, expand_configs end to
end, xfail for the pre-existing folder-drop bug), 8 node, 10 Playwright (the Builder modules
load from the fake origin with the real state_to_configs_json answering the preview).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`base COUPLE left | right` (2-4 equal columns; COUPLE(rows) for bands; region ` :w` weight)
splits a prompt into an unmasked base cond plus one masked cond per region, blended by
ComfyUI's own mask/mask_strength path (latent-couple style, one extra UNet pass per region,
works for every model family). COUPLE is parsed first; each part still takes AND/CAT/AVG/BREAK.
- regional_prompt.py: parser, join helpers (byte-identical output without COUPLE, cache keys
depend on it), size-independent region tags, latent-size mask builder with tile/crop frames,
apply_regional_masks at sample time
- batch_encoding: COUPLE prompts take the combinator path (never the batched TE path)
- conditioning_cache: conditioning_v2 keeps every entry and extra key (the old format silently
dropped AND prompts); legacy entries are a one-time miss
- trigger words, model suffix and HiRes append_end land in the base, not the last region
- tiled HiRes and Florence2 crops pass their frame; invalid syntax stops the run early;
LTX video warns; Builder tooltip + README section + ProjectStructure/AI-CONTEXT
- 107 new tests (parser, masks, encoding, cache round trip, source guards)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- 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>