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.
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.