Adds client-side concurrent queueing to style-sweep. -P N submits N
prompts to ComfyUI's HTTP queue concurrently via ThreadPoolExecutor.
The GPU still processes one prompt at a time (ComfyUI's queue is
single-worker), but the HTTP submission, websocket polling, image
download, and disk-write phases pipeline with the next prompt's
submission.
Expected speedup: 5-15% on a typical Flux sweep where per-image GPU
time is ~25-30s and overhead is ~3-5s. Real benefit grows with
slower networks or larger images.
Design choices:
- Default P=1 preserves the exact existing sequential behavior and
log output (no "(submit #N)" suffix in messages).
- P>1 uses ThreadPoolExecutor.as_completed for completion-order
reporting; the manifest is re-sorted to source-list order after.
- Skip-existing + dry-run cases are handled synchronously before the
executor even starts (no point pipelining no-ops).
- --abort-on-error is incompatible with parallelism (can't reliably
stop in-flight workers); we warn and continue.
- Per-task console output WILL interleave under -P>1 because
_run_generation prints its own progress; users are pointed at the
manifest for clean per-slug timing.
Why not full async multi-GPU-workflow parallelism:
- ComfyUI processes its queue strictly sequentially; we can't
actually run two Flux UNets concurrently without a second ComfyUI
instance, second port, second model dir, etc.
- Even with two instances on one GPU, the CUDA cores time-slice and
you get ~1.1x not 2x.
- Memory math is tighter than it looks even on Spark's 80GB unified
pool: two Flux dev instances = 64GB fixed before any activations.
- Maintenance burden is real; speed gain is marginal.
Client-side pipelining gets the practical wins (overhead hiding,
cleaner progress feedback for long sweeps) without the complexity
or OOM risk.
7 new tests covering: invalid P=0, P=1 equivalence with sequential,
multi-style execution, source-order manifest preservation under
chaotic completion, skip-existing in parallel mode, individual
failure containment, and abort-on-error warning.
267 -> 274 tests.
Catches mismatches between local intent and what's actually loaded on the
ComfyUI host. Replaces ComfyUI's generic 400 'prompt_outputs_failed_validation'
with a clear "model X not available on host — did you mean Y?" suggestion.
Why: when a user types `tsr generate -m getphatFLUXReality_v5Hardcore` but
only v11Softcore is installed, they got a 30-line raw API error buried in
node validation output. Now they get one red line plus three fuzzy-matched
candidates from the actual loader bucket.
Implementation:
- Extends get_loaded_models() in comfyui.py to include the diffusion_models
bucket (UNETLoader -> unet_name). Previously only checkpoints, loras, vae,
clip, controlnet, upscale_models were exposed.
- New _validate_model_available() helper in cli.py runs after family
detection, before prompt enhancement. Maps family -> loader bucket:
flux_unet / flux2_klein -> diffusion_models/, else checkpoints/. Uses
difflib.get_close_matches for the "did you mean" hint.
- Validates LoRA presence too when -l is passed.
- Special hint: if the requested file IS in checkpoints/ but the family
requires diffusion_models/, suggests the symlink command the user needs
to run on the host. Common case for newly-uploaded UNet-only checkpoints.
- Network failures are non-fatal — falls through to let ComfyUI surface
the error itself rather than blocking on a stale endpoint.
- Skipped in --json mode (machine callers) and --remote dispatches (the
server validates remotely).
8 new tests covering: unknown model in checkpoints bucket, unknown in
diffusion_models, flux2_klein routing, happy path, missing LoRA, network
failure, symlink hint, and a source-level check that the
diffusion_models bucket is wired into get_loaded_models.
259 -> 267 tests.
Adds a `flux2_klein` model family for Black Forest Labs' Flux.2 Klein 9B
release. Different architecture from Flux.1 D — required a separate
workflow rather than extending flux_unet.
Architecture differences from Flux.1 D:
- Single Qwen3-8B text encoder via CLIPLoader(type=flux2), producing
12288-dim conditioning (3 stacked hidden layers). NOT DualCLIPLoader.
- EmptyFlux2LatentImage instead of EmptySD3LatentImage (different
latent shape).
- Custom-sampling pipeline: Flux2Scheduler -> SIGMAS, fed into
SamplerCustomAdvanced together with BasicGuider + RandomNoise +
KSamplerSelect. No standalone KSampler node, so the caller's
scheduler argument is ignored.
- Dedicated VAE: flux2-vae.safetensors (not Flux.1's ae.safetensors).
Detection:
- New FLUX2_KLEIN_PATTERNS constant lists known Klein filenames
("lust_", "moodydesire"). _is_flux2_klein() checks base_model field
first ("flux.2 klein" / "flux2 klein") then filename pattern.
- detect_model_family() runs the Klein check BEFORE flux_unet, so
Klein checkpoints that also match UNet-only patterns (lust_v10,
moodyDesireMix) correctly route to flux2_klein.
Affected checkpoints reclassified from flux_unet -> flux2_klein:
- lust_v10.safetensors (Flux.2 Klein 9B-base per CivitAI DB)
- moodyDesireMix_v20PRO.safetensors (Flux.2 Klein 9B)
Still flux_unet (genuinely Flux.1 D UNet-only):
- cyberrealisticFlux_v25, fcFluxPonyPerfectBase,
getphatFLUXReality_v11Softcore.
Required ComfyUI host setup (one-time):
- /home/madcat/comfyui/models/text_encoders/qwen_3_8b_fp8mixed.safetensors
(8.1 GB, from Comfy-Org/vae-text-encorder-for-flux-klein-9b on HF)
- /home/madcat/comfyui/models/vae/flux2-vae.safetensors (321 MB)
Verified end-to-end on madcat: lust_v10 generated successfully through
the new flux2_klein workflow (~85s per image at 1024x1024, 20 steps).
7 new tests; 253 -> 259 total. Existing flux_unet tests retargeted to
genuine Flux.1 D checkpoints (getphat, fcFluxPony, cyberrealisticFlux).
Adds a `flux_unet` model family for Flux.1 D checkpoints that ship only the
UNet weights (no baked-in CLIP/T5/VAE) and require external encoder loading.
Affected checkpoints: lust_v10, cyberrealisticFlux_v25,
getphatFLUXReality_v11Softcore, moodyDesireMix_v20PRO,
fcFluxPonyPerfectBase. These live in `models/diffusion_models/` (or
`models/unet/`) on the ComfyUI host and are loaded via UNETLoader instead
of CheckpointLoaderSimple.
Detection:
- New `FLUX_UNET_ONLY_PATTERNS` constant in config.py lists known
UNet-only filename substrings.
- `detect_model_family()` returns `flux_unet` when any pattern matches,
taking precedence over base_model field (same architecture-override
pattern used for FluxPony hybrids).
Workflow:
- New flux_unet workflow in comfyui.py uses UNETLoader + DualCLIPLoader
(clip_l.safetensors + t5xxl_fp16.safetensors, type=flux) + VAELoader
(ae.safetensors) wired into the same Flux KSampler graph as the
monolithic flux family.
- Family defaults inherit from flux (sampler/scheduler/steps/cfg) with
external_clip flag set.
Smoke-tested end-to-end on madcat with getphatFLUXReality_v11Softcore
producing valid output. Flux.2 Klein checkpoints (lust_v10,
moodyDesireMix) still fail because they require a different text encoder
(qwen_3_8b) — see follow-up commit.
13 new tests; 240 -> 253 total.
--list/-L prints the resolved styles list as a two-column rich table
(slug + suffix truncated to ~80 chars) and exits without generating.
Template becomes optional when --list is paired with an explicit
--styles source, so you can inspect any styles file standalone.
--style/-S SLUG selects a single style by exact slug match; repeatable
for multiple. Unknown slugs error red with the available slug list.
Filter applies before --limit and preserves the source file's order.
Both flags compose with --limit and --dry-run; when filtering down to
a subset, the manifest is still written for the smaller run.
New `tsr style-sweep` command renders one image per style suffix from a
template JSON, composing prompt = template.prompt + ', ' + style.suffix
and writing to {output_dir}/{slug}.png.
- Template JSON mirrors `generate --input` keys plus output_dir + styles.
- Styles source can be a path or inline list/object on either CLI or
template. Relative styles paths in the template resolve against the
template's directory (so templates can ship with their styles file).
- Skips existing outputs by default (--no-skip-existing to force).
- --dry-run prints planned prompts/paths without invoking generate.
- --limit N caps the sweep for fast iteration.
- --continue-on-error keeps going on individual failures; final exit code
is non-zero if any style failed and failed slugs are reported.
- --remote propagates to the underlying generation, same as `generate`.
- Writes a manifest {output_dir}/_sweep.json with per-style results
(slug, prompt, output, seed, duration_sec, success, error).
Delegates to the `_run_generation` helper extracted from `generate`.
Move the post-merge body of generate() into a module-level _run_generation
helper so it can be invoked directly by other commands (next: style-sweep)
without going through Typer argv reconstruction.
No behaviour change. generate() still owns the --input JSON merge and CLI
parameter-source detection, then delegates to _run_generation.
Threads guidance value through CLI → remote payload → server schema →
generate_image() → FluxGuidance node. Ignored for non-Flux families.
Use cases:
- Lower guidance (2.0-3.0) for looser, more photorealistic Flux output
- Higher guidance (4.0-6.0) for tighter prompt adherence
Models like gonzalomoXLFluxPony are architecturally Flux but CivitAI
tags them as 'Pony', causing the SDXL workflow to be sent to ComfyUI
which fails validation. The filename now overrides base_model when it
contains 'flux'.
Also adds:
- Full Flux Dev/Schnell workflow template (ModelSamplingFlux,
FluxGuidance, ConditioningZeroOut, EmptySD3LatentImage); KSampler
cfg locked to 1.0, caller cfg routed to FluxGuidance
- --family/-F flag to manually override family detection
- queue_prompt now surfaces ComfyUI node_errors from 400 responses
- Tests for Flux workflow builder (8 cases) and updated family defaults
The CLI download flow only set civitai_model_id/version_id on local_files
without caching the full model payload, so 'tsr db list' joined against
empty models/versions/creators tables and showed every linked file as
'unlinked'. The server's _auto_link_file path had additional bugs:
resolved-vs-unresolved path comparison after rescan, redundant CivitAI
hash lookup, and silent failure swallowed by 'completed' status.
- New Database.register_downloaded_file() consolidates hashing, metadata
storage, FK linking, and cache_model() into a single idempotent call
shared by both CLI and server paths.
- Server _do_download now passes version_info straight through and
surfaces db_file_id/db_linked/db_cached/db_error onto _active_downloads.
- Drops the broken _auto_link_file rescan helper.
Family detection was force-injecting a default VAE (e.g. sdxl_vae.safetensors)
when a model was specified without an explicit --vae. If that VAE file didn't
exist on the ComfyUI server, the workflow was silently rejected. Now only
overrides VAE when the user explicitly passes --vae.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `tsr generate` and `tsr models` as top-level CLI commands that call
ComfyUI library functions directly or HTTP to a remote tensors server.
Add `--remote` flag to existing `tsr search` and `tsr dl` commands.
New file `tensors/remote.py` provides HTTP client functions for all four
operations against the remote tensors API (generate, models, search,
download with progress polling).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces hardcoded localhost origins with env-driven config.
Accepts comma-separated origins or wildcard (*). Defaults to
["*"] for backward compatibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add sampler/scheduler/steps/vae to MODEL_FAMILY_DEFAULTS for all families
- Add zimage family detection for ZImageTurbo models
- Flux and zimage families use ae.safetensors VAE
- SD 1.5 families use checkpoint built-in VAE
- SDXL families use sdxl_vae.safetensors
- API auto-applies family defaults when request uses default values
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Log prompt, model, size, steps on generate request
- Log completion with prompt_id and image count
- Log warnings/errors on failures
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use sdxl_vae.safetensors by default instead of checkpoint VAE
- Add vae parameter to generate_image and API endpoint
- Better quality for modern SDXL-based models
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace polling with WebSocket connection for real-time progress
- Show step-by-step progress during sampling (Step 1/20, etc.)
- Display progress bar with actual completion percentage
- Fall back to polling if WebSocket connection fails
- Import websocket-client for sync WebSocket support
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>