Register the subclass with generic.set_pipeline and build it with
from_pretrained, passing the SDNQ transformers and shared text encoder
while the vae, scheduler, and tokenizer load from the repo.
The dynamic-prompt brace processor in apply_styles_to_prompts strips the
{} and [] out of a JSON caption, leaving non-JSON that trips the model's
weight-baked safety placeholder. Let a model opt out of style and wildcard
processing via keep_prompts and set it for Ideogram4, then normalize the
prompt in encode_prompt: valid JSON to the compact training form, plain
text wrapped into a minimal caption so basic prompts still generate.
diffusers shipped first-party Ideogram 4 (transformer + pipeline) in
9b0818cf, so drop the in-tree port and keep only SD.Next integration glue.
Bump the diffusers pin to 9b0818cf and build diffusers' Ideogram4Pipeline
from a thin loader with per-transformer SDNQ. A small subclass keeps the
text encoder resident for the Qwen3-VL tap under balanced offload, and the
step callback denormalizes the preview latent from vae.bn before unpatchify.
Deletes the ported transformer, pipeline, scheduler, text encoder, and
latent-norm constants.
- move the text encoder on-device for the tapped forward (bypasses the offload hook)
- wrap the denoise loop in the diffusers progress bar
- live preview through the shared TAE FLUX.2 decoder (Flux.2 VAE)
Diffusers-native port of the 9.3B flow-matching DiT: dual-transformer asymmetric CFG, a 13-layer Qwen3-VL tap encoder deduped with VQA and prompt-enhance, the Flux.2 VAE, and a logit-normal schedule. Loads a published bf16 repo with SDNQ at load.
Surface YoloRestorer.restore() as a standalone operation: a Detailer
postprocessing script in the Process tab and a thin /sdapi/v1/detail
endpoint, neither requiring a base generation pass.
- modules/postprocess/yolo.py: YoloRestorer.make_processing() builds the
synthetic Img2Img processing object both entry points feed to restore(),
resolving the seed so the inpaint passes are reproducible
- modules/api/process.py: post_detail handler exposes the full detailer
parameter set and returns the detailed image plus optional annotations
as base64
- scripts/postprocessing_detailer.py: reuses shared.yolo.ui('extras') and
runs through make_processing()
- modules/postprocessing.py: run_extras takes a per-script script_args
dict, also letting the extras API drive other scripts such as Remove
background; omitting it leaves existing callers unchanged
- modules/api/models.py: ReqDetail / ResDetail
- modules/processing_info.py: guard create_infotext's Image/Hires CFG
reporting against an unset (None) cfg_image, matching the is-not-None
checks the other cfg_image readers use; the detailer inpaint pass runs
with it unset
- test/test-detailer-api.py: covers both paths; effect tests measure the
diff inside the detected region with extreme isolated parameter values,
and the suite disables model quantization for the run and restores the
original settings afterward
create_paths() pre-created every per-type output folder via fix_path(), which resolves relative values against data_path (the repo root). With a base images/grids folder configured, those bare dirs are never written to (generation uses the base-joined path), so they accumulate as empty stray folders at the repo root on every launch. Guard the bare creations behind an unset base; the resolved base+specific block already creates the real targets.
taesd_layers < 3 drops spatial upsample blocks in the TAESD/TAEHV
decoders, shrinking preview output 2x/4x. Both UIs size the live preview
from the image's intrinsic pixel dimensions (modern via object-fit:
scale-down, standard via max(naturalWidth, 512px)), so lower layer counts
rendered the preview physically small.
Rescale the decoded preview spatially by 2^(3-layers) in
sd_vae_taesd.decode. Gated to TAESD and TAEHV, the only decoders that
honor taesd_layers; TAEM1 and Hybrid VAEs decode at full size and are left
untouched. Rank-agnostic so it covers both image (CHW) and video (TCHW)
previews, including single-frame video models used for txt2img.
The custom-transformer apply (DiT branch) and the revert-to-default path now call reload_model_weights(force=True) instead of load_diffuser, so both go through the same managed reload: unload-before-rebuild for a lower VRAM peak and consistent job/checkpoint bookkeeping.
Gate the cross-arch unet reset on an actual checkpoint change so a same-checkpoint reload skips the pipeline-class comparison, which could false-positive and clear the override when the model was left in an img2img/inpaint variant after an interrupted generation.
safetensors.torch.load_file opens the file by path internally, so the progress.open wrapper around it never advanced from 0/N - it just left a stuck '0.0/18.2 GB' bar on screen for the duration of the load. Skip the wrapper on that path; .ckpt and stream_load paths still get the working bar.
When reload_model_weights resets shared.opts.sd_unet to Default on a cross-arch swap, the backend value changed but the dropdown component kept showing the previous selection because sd_unet was not in the change_checkpoint button's outputs.
Add the sd_unet component to the outputs and return its post-reload value via gr.update.
A custom UNET selected via the UNET dropdown carried over silently when the user swapped to a base model of a different arch, then crashed inside the per-arch loader because shared.opts.sd_unet was still pointing at the previous arch's file.
reload_model_weights now runs sd_detect.detect_pipeline on the new checkpoint before unloading the old model, compares the detected pipeline class against the loaded model's class, and resets shared.opts.sd_unet to Default when they differ.
load_unet returned early when sd_unet was set to 'Default'/'None',
leaving any previously-loaded custom UNET in memory. The model stayed
on the override and the dropdown looked stuck.
Now detects the transition from a custom selection to Default and
triggers sd_models.load_diffuser() to reload via the canonical
from_pretrained path.
Frees the name for pipelines/native_transformer. Module covers the full
LyCORIS adapter family (LoRA/LoKR/LoHA/OFT/IA3/GLoRA/Norm/Full), not
just LoRA.
Styles in nested subfolders that shared a base JSON name were being
overwritten because load_style keyed self.styles by the raw style["name"]
while the Style object stored the prefixed name. The extra networks page
iterates dict keys, so only one card per name surfaced.
Key the dict by the same prefixed name, and warn when a true duplicate
(same prefix and same name) is still detected.
NetworkOnDisk.fullname stripped only "/" from the post-lora_dir slice,
leaving a leading "\" on Windows. Every prompt typing the file's
natural dot-form name missed both registered aliases. On Linux,
dot-form lookup already missed when the file lived in any subfolder
because only the subfolder-prefixed form was registered.
- network.py: lstrip both separators and normalize backslashes to
forward slashes so fullname has one canonical shape per OS.
- lora_load.py: register a bare-basename-with-dots alias so typing
<lora:my.lora:1> resolves regardless of subfolder placement.
setdefault preserves explicit primary registrations on cross-subfolder
basename collisions.
Existing prompts using the legacy dots-to-underscores form continue
to resolve via entry.name unchanged.