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>
examples/ ships one workflow per family (SDXL/Illustrious, Anima, Z-Image
Turbo, Krea 2 Turbo, Flux.2 Klein 9B, MOSS-Image), each a Builder -> Sampler
Grid -> Dashboard graph with the Builder state pre-filled (model, text
encoder, CLIP type, VAE, sampling axes, prompt). examples/README.md lists
the files and folders each needs and what the grid tests.
tests/test_examples.py validates graph wiring, Builder state, emitted
configs and the image counts promised in the README.
Closes#6
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
encode_prompts_batched right-pads eligible prompts, runs ONE real encode on the
batch via a replay shadow on the inner encoder, and replays per-prompt slices
through the unmodified _encode_single (template strip, mask pop, clip_skip).
Allowlisted LLM-style encoders only (MOSS-Image, Krea 2, Qwen-Image); weighted,
AND/CAT/AVG/BREAK prompts fall back. Token cap, OOM halving, first-batch
self-check and an optional per-prompt verify mode. The shadow is always removed
in a finally. _encode_single gains tokens=None and restores clip_layer on error.
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>
html_generator gains a standalone flag (USCG_STANDALONE) and escapes "</" in
the inlined manifest. New logic_standalone.js (loaded last) replaces every
parent-frame dependency: own ComfyUI websocket re-dispatching the bridge's
message shapes (session filter, id dedupe, backoff, resync on resume),
Fullscreen API, URL kept on the shown session, GENERATE NEW via
POST /config_tester/run, and an openStandalone() header button for the node
iframe. Lightbox gets modalNav/toggleModalFavorite/rejectModalItem helpers
and an action bar element (styled in the next commit).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds GET /config_tester/dashboard (SPA as a top-level page, optional
?session=), POST /config_tester/run (builds the Generator -> Dashboard prompt
and enqueues it in-process exactly like PromptServer.post_prompt; force
defaults to true) and GET /config_tester/run_status. Pure helpers live in
run_api.py (no server imports) with unit tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ComfyUI's CLIP.encode_from_tokens() calls load_model() -> model_management.load_models_gpu()
on every encode. With the dynamic VRAM loader (comfy_aimdo, 0.4x+) that re-runs
ModelPatcherDynamic.load() for a model that is already resident: a walk over every module plus
free_memory bookkeeping, visible as one
Model MOSSImageTEModel_ prepared for dynamic VRAM loading. 7002MB Staged. ...
log line per prompt (N prompts -> N+1 lines). USCG's pre-encode paid it once per prompt, and
again on the single-encode paths (model switch, distributed, upscale, Florence2 hires).
Measured on the 5060 Ti (ComfyUI 0.42.5), MOSS-Image TE = Qwen3.5-9B w4a8, same process state:
template path (CLIPTextEncode), per prompt ....... 0.92 s (re-prepare every encode)
USCG batch encode with pinned_clip, per prompt ... 0.84 s (one prepare per pass)
USCG job, 21 prompts + 21 one-step images ........ 36.8 s -> 33.0 s
for scale: SDXL 0.15 s, Krea 2 0.29 s, Qwen-Image 0.32 s per prompt on the template path
The rest is the encoder forward itself: ~0.55 s fixed + ~2 ms/token for Qwen3.5-9B (24 of its
32 layers are Gated DeltaNet run through the pure-torch chunked recurrence, fp32 compute), so
a 60-prompt MOSS grid still spends ~50 s encoding before the first image. That part is
identical to the official workflow.
batch_encoding.pinned_clip(clip, texts): load once through load_model() (sized by the longest
text so any memory estimate still runs), shadow clip.load_model with a no-op for the block,
restore on exit or exception, nesting safe. encode_pair_cached(): positive+negative with one
load, nothing when both are cached. The pre-encode summary line now prints total time and
ms/prompt.
Applied to batch_encode_with_cache, the model-switch pre-encode in the main loop, the
distributed group pre-encode and per-job encode, the deferred-upscale re-encode fallback, the
dashboard upscale runner and the Florence2 hires step.
Tests: tests/test_pinned_clip.py (10) against the real batch_encoding module; the conftest
stub gained pinned_clip/encode_pair_cached. 137 passed, same 15 pre-existing torch-stub failures.
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>
The CivitAI function moved to the ComfyUI-USCG-CivitAI companion in this
phase. After Phase 1 (HF VAE) and Phase 2 (Distribution), this was the
only function left. Now the file is empty and removable.
network_utils.py shrunk: 281 (original) -> 189 (Phase 1) -> 60 (Phase 2)
-> 0 (Phase 3, deleted).
Also tidies any dead network_utils references in tests/conftest.py.
Adds is_civitai_available() and civitai_fetch_by_hash() with SILENT
fallback (returns None) when companion missing. No RuntimeError because
CivitAI lookup is an optional convenience — the LoRA workflow works
without auto-trigger detection.
Pre-existing callers already handle None on network failure, so this
facade is drop-in compatible.
Also updates tests/conftest.py to importlib-load the real civitai.py
(matches Phase 1/2 patterns for remote_vae + distribution).
Callers (config_builder_node.py + lora_utils.py) still import from
.network_utils as of this commit; the swap lands in the next commit.
Adds is_distribution_available(), _require_companion(), create_manager(),
set_active_manager(), clear_active_manager(), notify_workers_to_start(),
stop_all_workers(), get_master_url() plus INSTALL_INSTRUCTIONS.
create_manager passes three callbacks through to the companion's
DistributionManager (job_completed_check required, build_prompt_with_triggers
and get_model_cache_key optional) — avoids bidirectional imports back into
main USCG that the original distribution_manager.py had at line 91 (and 419).
Lazy try-import of the companion returns a clear error if not installed.
Defensive minimum-version check (>= 0.1.0). clear_active_manager is silent
when companion missing so cleanup paths are safe even if companion gets
uninstalled mid-run.
Also updates tests/conftest.py to importlib-load the real distribution.py
so facade tests exercise actual code (matches Phase 1's remote_vae pattern).
Old code paths in generation_orchestrator/distribution_routes/etc still
active; the swap lands in the next commits.
The companion plugin (ComfyUI-USCG-RemoteVAE) resolves endpoint names to
URLs internally. Main USCG no longer needs the URL allowlist - it only
passes the endpoint NAME (e.g. 'SDXL') through the worker.
Adds is_remote_vae_available(), get_endpoint_names(), _companion_decode()
plus INSTALL_INSTRUCTIONS constant. Lazy try-import of the companion module
returns a clear error if not installed. Old code paths still active; the
remote_decode_hf -> _companion_decode swap lands in the next task.
Also updates tests/conftest.py to load the real remote_vae module (via
importlib.util) so facade tests exercise actual code rather than a stub.
Adds network_utils real-load and diffusers/diffusers.image_processor stubs
to satisfy the remote_vae.py import chain in the test environment.
Adds an elif block to _run_upscale_thread that handles mode='florence2_hires',
delegating per-image work to florence2_hires.run_florence2_step() and using
two new pure-Python helpers (build_florence2_manifest_entry,
build_florence2_no_detection_entry) to construct manifest entries.
The pure-Python helpers are unit-tested in test_florence2_manifest_entry.py;
the wiring itself relies on the user's smoke test (mirroring the LTX pattern).
Also: conftests now try to load real torch before installing the torch stub,
so tests that genuinely need real torch tensors (crop/paste, integration)
work when the Comfy embedded Python is used. Other tests are unaffected.
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>
Covers single-lora pass-through, multi-lora combine with ' + ',
folder ref un-expansion, folder+brackets excluded from combine,
bypassed lora exclusion, and ltx_video model_type propagation.
Also tightens the existing bracket-array test from substring match
to exact string equality and removes the unused pytest import.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eliminates the JS<->Python drift bug class by making one Python function
the source of truth for state -> configs_json. Both the runtime
(generate_config) and the new preview endpoint (next commit) call it.
Adds golden test for the lora-strength-arrays bracket case that just
broke (regression guard).
Also loads the real config_builder_node module in conftest.py (instead
of a bare stub) so that tests against UltimateConfigBuilder methods work
correctly. Stubs network_utils before loading to satisfy the relative
import.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>