Repository navigation
FLUX.3 prompt upsampler block - #15058
Conversation
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
Adds Flux3PromptUpsampleStep, a conditional block that runs the FLUX.3 prompt upsampler (black-forest-labs/flux-3-image-klein-pup) in front of the pipeline. It expands `prompt_to_upsample` into `prompt`, picks the task from the inputs (text-to-image or image-conditioned, each with optional layout boxes), and is skipped when `prompt` is passed directly. The reference preparation and the box handling follow the official inference code; the system prompts are inlined from the upsampler repository. Also fixes the Memory section of the FLUX.3 docs, which called enable_auto_cpu_offload on the pipeline instead of the ComponentsManager. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
eec9cce to
4777180
Compare
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
prompt-upsampling may be strongly recommended, but this pr makes it mandatory as pipeline FAILS if prompt upsampler is not loaded. and making 9b upsampling model mandatory for model that is intended to run on lower-end gpus doesn't make sense. also, loader does not load upsampler automatically which makes upsampling mandatory, but must-be-loaded-by-user-separately - also not in line with any other model. |
|
it's by default, but not mandatory you can pop out the block if yon't want it, the doc covers the use case https://huggingface.co/docs/diffusers/main/en/api/pipelines/flux3#prompt-upsampling blocks.sub_blocks.pop("prompt_upsampler")
pipe = blocks.init_pipeline("")and it's gone, the block and its components this is the user experience BFL team preferred. like we explained in the doc, the model needs to take full scene captions and gives noticeably worse results without the upsampler - that's something specific to how this model is trained and we don't think it make any sense to overlook that simply to be in line with other models |
also this, not sure what you're talking about |
…UF support (#13933) ## Problem FLUX.3 Image Klein (black-forest-labs/flux-3-image-klein and -base) cannot be loaded in Studio. Diffusers ships it only as Modular Diffusers blocks (`Flux3DistilledAutoBlocks`, `Flux3AutoBlocks`), the repos carry `modular_model_index.json` instead of `model_index.json`, and the support is not in any Diffusers release yet (huggingface/diffusers#15046). ## Change - New `core/inference/diffusion_flux3.py`: loads the transformer, the Qwen3-VL-4B text encoder and processor, and the FLUX.2 VAE per subfolder, and puts a `DiffusionPipeline` face over the modular blocks. Placement, model and group offload, the step callback (progress, cancel), the decode phase hook and the scheduler swap all work through the same component objects the blocks run. - Two families: `flux.3-klein` (distilled, 16 steps, CFG 1, Euler) and `flux.3-klein-base` (50 steps, CFG 3.5, negative prompt). Defaults follow ComfyUI's current FLUX.3 templates (workflow templates 0.11.87): the base text to image and edit templates run 50 steps at CFG 3.5 (euler / simple), raised from the launch templates' 25, which is also the BFL reference setting (diffusers' FLUX.3 defaults: 50 steps, guidance 3.5); both distilled templates run 16 steps at CFG 1. On hand-heavy prompts (6 prompts, short and long, 2 seeds, same reference noise) 50 steps fixed a malformed handshake the 25-step render had and otherwise left the image nearly unchanged. Both take up to 10 images through the existing reference workflow. The `-pup` prompt upsampler repo is not matched as an image model. - Reference images are scaled to 1 megapixel (Lanczos, aspect kept) before encoding, as ComfyUI's `ReferenceMultiLatent ["resize", 1]` does in the FLUX.3 templates. Diffusers alone only downsizes above 16 megapixels. - A seeded request starts from the BFL reference noise layout, so a Studio seed renders what the BFL reference CLI renders for that seed. The VAE stays in bf16, as ComfyUI runs the FLUX.2 VAE on NVIDIA (the BFL reference decodes in fp32). - Download plan: the modular index is the manifest and only `transformer/`, `text_encoder/` and `vae/` are fetched, for full pipelines and as the companions of a GGUF or single file (which take only `transformer/config.json` from `transformer/`); the root BFL single file, `ae.safetensors`, `variants/` and the prompt upsampler repo are not. The plan also reports a pick of the other variant before anything is staged. - BFL added the optional prompt upsampler (`prompt_upsampler` / `prompt_upsampler_processor`, from the separate ~19 GB `black-forest-labs/flux-3-image-klein-pup` repo) to both official manifests on 2026-10-16. Image loads ignore it: validation and the locality check require only the components FLUX.3 loads (text encoder and its processor, transformer, VAE). Checked on the current revision through the real load path for a bf16 pipeline, a GGUF with staged companions and the INT8 prequant: renders are pixel-identical to before and the pup repo is never downloaded. - Memory plan: bf16 size table entries, and the load-dtype sizing now prices only `<component>/<file>` weights. The cached `variants/fp8r` and `variants/nvfp4` packages were inflating the plan from 19.8 GB to 31.7 GB. - Version gate: minimum 0.42.0, marked unreleased. Diffusers main and the FLUX.3 branch both report `0.41.0.dev0`, which reads the same as the 0.41.0 release without FLUX.3, so the picker checks for the FLUX.3 module file in the installed package instead (no import). - Quantisation: a new `FamilyProfile.quant_exclude_tokens` leaves named layers dense for every transformer-quant scheme (int8, fp8, mxfp8, nvfp4). It is empty by default, so other families are unchanged. FLUX.3 keeps the per-token modulations, timestep embedders, `norm_out`, the text-stream per-mode blocks and the 20480-wide text projection in bf16, which leaves 228 of 273 Linears quantised. Without it, mxfp8 and nvfp4 crash at the first step: the modulation inputs are a stride-0 `expand`, and those kernels need contiguous inputs. int8 and fp8 also lose more quality without it. - nvfp4 is denied for FLUX.3: every keep-set measured 0.09 to 0.12 LPIPS, and so did BFL's own nvfp4 package. - MX and NVFP4 Linears in every family now get a contiguous activation through a forward pre-hook, installed by the shared post-quantisation hook. torchao's activation quantisers assert contiguity, and a stride-0 broadcast such as `temb.expand(B, S, D)` otherwise crashes at the first step; for contiguous inputs the hook does nothing. This is a cross-family change. FLUX.2 klein 4B with mxfp8 (106 MX Linears, compiled with CUDA graphs, the only way Studio runs a quantised denoiser), checked in one process: 2 prompts at 1024x1024 rendered without the hook, then again with it, give `torch.equal` decoded images, and so does the denoiser output on a captured step. Two renders without the hook are also `torch.equal` in that process. Across separate processes the same renders differ by up to 2 of 255 with or without the hook (before vs after this commit, and between two runs of the same commit), so that comparison cannot show a difference either way. The hook returns the arguments unchanged for any contiguous input. The scheme smoke probe is unchanged from main, so no family's `auto` choice moves. - Regional compile and CUDA graphs are on for FLUX.3. With compile available, `auto` resolves to int8 on sm80 and newer, as it does for the other families. - Single file and GGUF denoisers assemble around the base repo's components, and the block graph follows the loaded denoiser. A GGUF FLUX.3 reports `uint8` as `transformer.dtype`, because its only float tensors are the fp32-kept QK norms. The denoise block casts the latents to that dtype, so the loader pins the compute dtype with an empty buffer. A checkpoint or pipeline of the other variant (distilled vs base) is refused with the family to use, before the loaded model is unloaded. BFL's own `variants/fp8r` and `variants/nvfp4` packages are refused too: the nvfp4 one has the same tensor sizes as a ComfyUI NVFP4 file with a different scale layout, so it would decode silently wrong. - Memory: measured activation rows for the distilled checkpoint. Each reference is charged at 0.11 of the flat 8192 MiB per megapixel (about 900 MiB per 1 MP reference, the int8 / fp8 figure; bf16 measures about 700). Before this, FLUX.3 references were not charged at all. The placement planner uses those rows too (denoise 1081, decode 2924 MiB at bf16 instead of the flat 8192 headroom), so a 32 GiB card keeps a bf16 FLUX.3 resident instead of streaming the text encoder: measured peaks 21.2 GiB text to image and 21.9 GiB with 4 references, 5.7 s vs 7.0 s per 1024x1024 text to image. 20 and 24 GiB still pick group offload. The base checkpoint keeps the flat estimate (no measured rows). - Auto-switch: a FLUX.3 cache snapshot downloaded around the hosted INT8/FP8 file (no dense transformer shards) counts as downloaded when that file is cached, and the switch loads it by repo id from the cache. - LoRAs: the pipeline face uses the pinned build's `Flux3LoraLoaderMixin` (transformer only). Checked on real weights with a random rank-4 adapter on 215 attention and feed-forward Linears: bf16 eager, the adapter changes the render, weight 0 and removing it give the base render bit for bit; int8 with the adapter baked in at load changes the render too. - Hosted artifacts: `flux.3-klein` lists `unsloth/flux-3-image-klein-FP8` for int8 and fp8 (files `flux-3-image-klein-INT8.safetensors` / `-FP8.safetensors`), the same layout as `flux.2-klein`. `flux.3-klein-base` lists `unsloth/flux-3-image-klein-base-FP8` the same way: its INT8 file is BFL's own ComfyUI `int8_convrot` denoiser from black-forest-labs/flux-3-image-klein-comfy, mirrored byte for byte and read through the ComfyUI quant loader, and its FP8 file is ours. Checked on real weights through the API load (50 steps, CFG 3.5, 2 prompts): the seeded INT8 renders match BFL's single file loaded directly (LPIPS 0.0001 and 0.004), the seeded FP8 renders match our FP8 file loaded by path (LPIPS 0.0001 and 0.0000). Without access to the repo the load logs `prequant_repo_unavailable` and quantises on the fly. - Ungated base files: `black-forest-labs/flux-3-image-klein` and `-base` are gated, so a GGUF or FP8 pick (which carries only the denoiser) fetched its text encoder and VAE through the gate. The upstream Diffusers folders Studio loads (`modular_model_index.json`, `text_encoder/`, `transformer/`, `vae/`) are now copied byte for byte (21 files each, sha256 checked against the pinned upstream revision) into `unsloth/flux-3-image-klein-FP8` and `unsloth/flux-3-image-klein-base-FP8` beside the pre-quantized files, and both are in the gated mirror table, so no separate mirror repo is needed. Companion listings and the manifest check read those repos too, so no request reaches the gated upstream. The picker and status still show the BFL id. - Hosted text encoders: both FP8 repos also carry the Qwen3-VL-4B text encoder, the same bytes in each (the distilled and base `text_encoder/` shards are identical), as `flux-3-image-klein[-base]-text_encoder-INT8-ConvRot.safetensors` (5.25 GB, int8 ConvRot weight-only, built by `scripts/build_te_int8_convrot_checkpoint.py`) and `...-text_encoder-FP8.safetensors` (5.61 GB on disk, 4.83 GB resident: layerwise fp8, bit-identical to the runtime cast), the same pattern as Qwen-Image-2.1. An int8 request that cannot load the int8 file falls back to the fp8 file, then to the dense encoder with the runtime fp8 cast, as on Qwen-Image-2.1. Unset requests get the int8 ConvRot encoder (`te_quant_auto = "int8"`), as BFL's ComfyUI templates load `qwen3vl_4b_int8_convrot` (4 of 5 workflows in black-forest-labs/flux-3-image-klein-comfy); `none` still pins the bf16 encoder. Checked through Studio's load path: an unset request loads the int8 file on both families and renders pixel-identical to the explicit int8 render. Real weights, bf16 DiT, 19 cases against the bf16 encoder on one B200 (A/A repeat bit-identical): int8 LPIPS-alex 0.017 mean / 0.098 max, SSIM 0.974 at 16 steps; 0.023 / 0.121, SSIM 0.973 for the base at 25 steps, CFG 3.5. fp8 scores 0.038 / 0.220 and 0.035 / 0.132. The encoder goes from 8.27 to 4.89 GiB resident and from 18.4 to 1.8 s to load (warm page cache). Peak memory drops from 21.2 to 17.8 GiB. INT8 DiT with the int8 encoder: 0.033 / 0.134 (distilled) and 0.034 / 0.142 (base), 13.9 GiB peak. BFL's `qwen3vl_4b_int8_convrot` has the same int8 projections byte for byte but also quantises `embed_tokens`, which Studio's loader cannot read. Repacked with that table dequantised, it scored 0.018 / 0.150 and 0.027 / 0.113, a tie within per-image spread, so the hosted file is our own build. Planner estimate for the encoder: 8487 MiB for bf16, 5517 MiB for fp8 or int8. Studio's own load of the mirrored files (int8 distilled and base, fp8 distilled) renders bit for bit what the harness renders. - A GGUF pick with a transformer quant builds through `_assemble_pipe`, which called `from_pretrained` on the modular pipeline class and returned a pipeline with no transformer. It now assembles FLUX.3 through the same per-component loader. - Base checkpoint, checked on real weights (19 cases, 50 steps, CFG 3.5): Studio vs the BFL reference code LPIPS 0.038 mean, the diffusers harness 0.037 against the same reference; 25 steps, text to image, single and multi-reference edits and negative prompts render; every distilled / base mismatch is refused before loading and in the loader. Fixes this needed: a base single file on distilled companions takes `flow_map` from the family instead of the companion config (it crashed on meta tensors); BFL's own ComfyUI `int8_convrot` and `nvfp4` files (black-forest-labs/flux-3-image-klein-comfy) now go to the ComfyUI quant loader instead of being refused as `variants/` packages, with the fused q/k/v split for FLUX.3; and the family, not the file name, picks the base vs distilled recipe (BFL's base ComfyUI files have no "base" in their names, so they were seeded 16 steps / CFG 1). - Diffusers pin: `diffusers-main.txt` is re-enabled at Diffusers main d42ebf9c (archive digest included), which carries the FLUX.3 merge (huggingface/diffusers#15046) and the prompt upsampler block (huggingface/diffusers#15058), the same route Qwen-Image-2.1 used before 0.41.0. Studio never loads the upsampler, so `flux3_blocks()` drops that step from both FLUX.3 block graphs; a render at d42ebf9c matches the merge commit pixel for pixel. A host that cannot fetch the pin keeps 0.41.0 and only FLUX.3 is refused. The pin tests check release mode on a commented-out copy, so they pass with the pin on or off. - Diffusers main now requires Python 3.11 (huggingface/diffusers#15030), so the installer and the startup repair skip the pinned build below 3.11 instead of cloning something that cannot install. Python 3.10 keeps 0.41.0 and gets a FLUX.3 refusal that names the Python requirement; 3.9 keeps 0.36.0 as before. ## Results Real weights, distilled, 16 steps, B200 shared lease. The 11 cases are the BFL example prompts and seeds (8 text to image at 832x1248 to 1248x832, 3 edits with 1 or 2 references), scored with LPIPS (alex) and SSIM. | Comparison | LPIPS mean | LPIPS max | SSIM mean | Identical images | |---|---|---|---|---| | Studio vs Diffusers ModularPipeline given the same noise and references | 0.0073 | 0.080 (edit_figurine) | 0.986 | 10 of 11 bitwise | | Studio vs the BFL reference implementation | 0.0115 | 0.083 (edit_figurine) | 0.979 | 0 | | Diffusers vs the BFL reference (same inputs, for scale) | 0.0046 | 0.026 | 0.991 | 0 | Text to image cases against the BFL reference: LPIPS 0.0008 to 0.0018, except riso_lido at 0.026 (the most chaotic prompt, which also moves between two runs of the reference on different hosts). The one edit that differs is edit_figurine: its 896x1120 input is just under 1 megapixel, so the ComfyUI sizing scales it up to 912x1136 where the reference keeps it as is. Same composition, different detail (LPIPS 0.08). Tiny random models, distilled and base, text to image and edit, resident, model offload and group offload: bitwise identical to the Diffusers pipeline given the same inputs. Speed features, the same 11 cases, against Studio bf16 eager: | Arm | Quantised Linears | LPIPS mean | LPIPS max | SSIM mean | |---|---|---|---|---| | bf16, regional compile + CUDA graphs | 0 | 0.004 | 0.027 | 0.992 | | auto (resolves to int8, compiled) | 228 | 0.024 | 0.056 | 0.958 | | fp8, compiled | 228 | 0.039 | 0.215 (riso_lido) | 0.946 | | mxfp8, compiled | 228 | 0.042 | 0.185 (riso_lido) | 0.939 | Every case except riso_lido is at or below 0.048 for all three schemes. For scale, compiled vs eager bf16 measured 0.0035 in the separate quantisation survey. Dependency check for the pinned build (`uv pip compile`, the installer's requirement line): | Python | Stack | Result | |---|---|---| | 3.11, 3.12, 3.13 | Studio pins: transformers 5.5.0, huggingface-hub >=1.32,<2 | resolves; huggingface-hub 1.33.0, transformers 5.5.0 and tokenizers 0.22.2 unchanged | | 3.10 | same | no solution (the build needs Python 3.11), so the installer skips it and keeps 0.41.0 | | 3.11 | transformers 4.57.6 | no solution (it needs huggingface-hub <1.0); the released 0.41.0 conflicts the same way, and Studio pins 4.57.6 only on Python 3.9, where it installs Diffusers 0.36.0 | A dry run of the installer's install against an existing Python 3.12 stack swaps only the diffusers package. Edits at 1024x1024, 16 steps, with 1 and 4 references (B200, 160 GB free): | Load | Placement | Peak allocated, t2i / 1 ref / 4 refs | |---|---|---| | bf16 | resident, no offload | 21.2 / 21.2 / 21.9 GiB | | auto (int8) | resident, no offload | 16.8 / 15.9 / 18.0 GiB | | auto (int8), capped at 16 GB | group offload, int8 denoiser and encoders streamed | 3.2 / 3.6 / 5.7 GiB | The 16 GB cap is simulated: the allocator fraction and `mem_get_info` are both capped at 16 GiB. All three loads render all three requests. GGUF and pre-quantised files, real weights, loaded through Studio's load API (`begin_load`) from local copies of the hosted files. The same 11 cases, 16 steps, against Studio bf16 eager: | Artifact | Quantised Linears | LPIPS mean | LPIPS max | SSIM mean | Builder's gate (19 cases) | |---|---|---|---|---|---| | GGUF Q8_0 | 273 (GGUF) | 0.010 | 0.035 (edit_figurine) | 0.983 | 0.006 | | GGUF UD-Q4_K_XL | 273 (GGUF) | 0.038 | 0.139 (riso_lido) | 0.947 | 0.038 | | INT8 safetensors (seeded) | 228 | 0.024 | 0.056 (riso_lido) | 0.958 | 0.035 | | FP8 safetensors (seeded) | 228 | 0.039 | 0.215 (riso_lido) | 0.946 | 0.036 | A 3-case spot check (fox, man_dog, edit_coffee) also ran Q4_K_M: LPIPS 0.033 mean against 0.021 for UD-Q4_K_XL. - All 273 Linears of the GGUF denoisers stay GGUF weights; the 1-D QK norms are dequantized like other GGUF families. The real GGUF files hit the `uint8` compute dtype problem described above, and render with the pin. - The INT8 and FP8 files quantise exactly the 228 Linears that on-the-fly quantisation picks under the keep-list, and the seeded renders are pixel identical to the on-the-fly int8 and fp8 renders. - A GGUF pick that asks for int8 seeds the INT8 file and renders pixel identical to the pipeline pick. Before the `_assemble_pipe` change the same load came back with no transformer. - CUDA graph capture fails on the K-quant GGUFs (a host-to-device copy inside the dequant). The load logs the reason as a warning and runs eager; every render completed and no error reaches the request. Tiny Q8_0 GGUFs with BFL tensor names load for both variants and render text to image and edit. Pairing the base GGUF with the distilled family is refused. Timing at 1024x1024, resident bf16, no compile: 1.3 to 2.5 s per text to image, 2.8 to 3.9 s per edit. ## Tests - `studio/backend/tests/test_diffusion_flux3.py`: 84 CPU tests covering the download plan scope and variant verdict, the prompt upsampler entries in the current official manifests, the modular manifest in the gated-base probe, shard restore and staging, the pre-eviction variant check (single files and pipelines), the local folder validation, the FLUX.3 LoRA loader, per-image generation chunks, per-image negative prompts, a local modular base for GGUF loads, the refusal of separate encoder / VAE files, the hosted artifact rows and filenames, the operator mirror, the quantized GGUF pick assembly, the nvfp4 deny, the contiguous-input guard, the per-scheme keep-list (and that other families are unchanged), the variant and BFL-package refusals, the GGUF compute dtype pin, memory rows, detection, the `-pup` exclusion, modular index detection, defaults, size table, the unreleased gate and module-file probe, the modular download plan, plan sizing, reference sizing, the reference noise layout, and the pipeline face (prompt and generator splitting, step callback, interrupt). - Frontend `model-catalog.check.ts`: passes with the new GGUF row. - Family data freeze and autoopt parity fixtures updated for the two new families (new rows only). - Diffusion, image, inventory, quantisation and Diffusers-pin suites (10,470 passed): no new failing test IDs against the base commit. - Whole backend suite after the hosted-artifact commit: 74,977 passed, and the failing test IDs (all present before it, environment related) are the same set as on the previous commit. - `tests/sh/test_clean_machine_notools_pinned_git.sh`: 44 passed. ## Not covered - A dense bf16 cold load (`transformer_quant=none`). Cold anonymous loads (fresh cache, no token) of a GGUF UD-Q4_K_XL pick, a base FP8 pick and a bf16 pipeline pick (which auto-selects int8) each requested only `unsloth/flux-3-image-klein[-base]-FP8` / `-GGUF` and rendered at 1024x1024; the GGUF pick downloads 9.53 GB with the int8 encoder. - fp8r and nvfp4 variant files: they use Diffusers' new BFL quantizer format, which no Studio prequant path loads yet. - Upstream: Diffusers reports `uint8` as the dtype of a GGUF FLUX.3 transformer, and a plain ModularPipeline fails the same way. Studio pins the compute dtype; the upstream fix is still to be filed. - A script that exits while Studio's deferred background compile is still running dies at interpreter shutdown (exit 245 or 250). This reproduced on a bf16 FLUX.3 run after all renders had finished, and the same way on FLUX.2 klein 4B (bf16, 3 renders, exit 245), so it is not in the FLUX.3 code. Calling `unload()` first exits cleanly. The background compile is shared by every family and is not changed here. I did not check whether a Studio server shut down mid-compile hits the same thing. - Follow-up: a smoke probe that also feeds a stride-0 activation, so a scheme missing the contiguous guard fails the probe instead of the first render. Left out of this PR. - MLX, Windows and AMD paths for FLUX.3 were not run. The native sd.cpp route is #14144, stacked on this PR. - Hosted repos (public, ungated): `unsloth/flux-3-image-klein-FP8`, `unsloth/flux-3-image-klein-base-FP8`, `unsloth/flux-3-image-klein-GGUF` and `unsloth/flux-3-image-klein-base-GGUF`. The two GGUF repos are picker rows (shown under their repo names); `model-catalog.check.ts --network` checks every declared file against the Hub. If a FP8 repo cannot be reached, an int8 or auto load logs `prequant_repo_unavailable` and quantises on the fly (checked on real weights, same output as the file). - GGUF CUDA graphs on K-quants (falls back to eager). - nvfp4 quality (excluded from `auto`) and step caching. - Python 3.10 hosts cannot run FLUX.3 until a Diffusers release carries it (the refusal says so). - Timings: the runs above were on shared GPUs, so they are accuracy checks, not benchmarks. - A real 16 GB card (the cap was simulated in-process). - Measured memory rows for the base checkpoint (it uses the flat estimate). - Trained FLUX.3 LoRAs (and their key formats); only a random adapter was tried. - Separate text encoder / VAE files beside a FLUX.3 GGUF or single file: refused with a clear message for now (the supplied-component loader reads a model_index.json, which FLUX.3 does not ship). - Prompt upsampler integration: #14137, stacked on this PR. - ComfyUI seeds do not reproduce (ComfyUI draws its own noise layout); BFL seeds do. - The single reference path differs from the BFL reference by design (1 megapixel resize instead of pad to 4 megapixels), following ComfyUI. - Text encoder length cap: 8192 tokens (Diffusers) against 4096 in the BFL reference; only prompts past 4096 tokens differ. ## Another GPU generation On a Colab RTX PRO 6000 Blackwell (sm120, torch 2.14.1+cu130), Studio's own `DiffusionBackend` from this branch loaded `unsloth/flux-3-image-klein-FP8` with every setting left at its default and rendered 1024x1024 images. It picked an int8 transformer (int8 GEMM engaged), the hosted int8 ConvRot text encoder, compile with CUDA graphs and no offload, and the load took 84 s. The merge base does not detect the repo as FLUX.3 at all. ## UI Images model picker searched for "FLUX", before (merge base 7382805) and after (42e4b8a). After this PR, `flux-3-image-klein-GGUF (Slow)` (5.5B) and `flux-3-image-klein-base-GGUF (Slow)` are curated rows in the FLUX group, right after FLUX.2-klein-9B. Before, both only show up as bare, unsized Hub search hits further down.  --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This PR prompt upsampler block into official pipeline
text to image

edit

layout

layout edit

text to image (from expanded prompt)
