Feat: flux2 dev support#9234
Open
Pfannkuchensack wants to merge 3 commits into
Open
Conversation
Adds end-to-end support for FLUX.2 [dev] alongside the existing Klein implementation. Dev uses Mistral Small 3.1 (24B) as its sole text encoder instead of Klein's Qwen3, with joint_attention_dim=15360 and the guidance-distilled 32B transformer. Backend - taxonomy: Flux2VariantType.Dev, ModelType.MistralEncoder, ModelFormat.MistralEncoder, MistralVariantType - configs: probe dev via context_in_dim=15360 (main + LoRA); new mistral_encoder.py with Diffusers / Checkpoint / GGUF configs; Main_Diffusers_Flux2_Config accepts Flux2Pipeline class name - loaders: new mistral_encoder.py (AutoModel for Diffusers folder, MistralModel for single-file + GGUF with llama.cpp key conversion). Existing Klein transformer loaders are generic enough for dev - ModelRecordChanges.variant union extended with MistralVariantType Invocations - flux2_dev_model_loader, flux2_dev_text_encoder (Mistral chat-template with FLUX2_DEV_SYSTEM_MESSAGE and layer-stacking 10/20/30), flux2_dev_lora_loader (+ collection variant) - MistralEncoderField on model.py; flux2_denoise / flux2_vae_decode / flux2_vae_encode reused unchanged (already model-agnostic) Frontend - types/hooks/selectors for MistralEncoder, isFlux2DevMainModelConfig, selectFlux2DevDiffusersModels, useMistralEncoderModels - params slice fields flux2DevVaeModel / flux2DevMistralEncoderModel / flux2DevSourceModel + reducers, selectIsFlux2Dev / selectIsFlux2Klein - ParamFlux2DevModelSelect component, wired into AdvancedSettingsAccordion - buildFLUXGraph dev branch with full txt2img / img2img / inpaint / outpaint + multi-reference image editing (same flux_kontext + collect chain as Klein, since Flux2RefImageExtension is model-agnostic) - addFlux2DevLoRAs helper for dev LoRA wiring - zModelType / zModelFormat / zFlux2VariantType extended for mistral_encoder / mistral_small_3_1 / dev - OpenAPI schema regenerated, TS types updated Starter models - FLUX.2 [dev] Diffusers (bf16 + NF4), three GGUFs (Q4/Q6/Q8), Mistral encoder (bf16 + NF4)
Follow-up fixes after first end-to-end run with FLUX.2 [dev] GGUF +
Mistral 3.x GGUF + standalone FLUX.2 VAE.
Frontend
- buildFLUXGraph: wire dev model loader's vae into both flux2_denoise
(required for BN statistics / inpaint) and flux2_vae_decode; missing
edge was raising RequiredConnectionException at runtime
- readiness.ts: variant-aware FLUX.2 readiness check — dev requires
flux2DevVaeModel + flux2DevMistralEncoderModel (or a Dev diffusers
source); Klein keeps Qwen3/VAE check. Threads
hasFlux2DevDiffusersSource through generate + canvas tabs and updates
buildGenerateTabArg / buildCanvasTabArg test helpers
- en.json: noFlux2DevVaeModelSelected, noFlux2DevMistralEncoderModelSelected
Mistral encoder loader (GGUF / single-file)
- Fix "Cannot copy out of meta tensor": llama.cpp conversion produced
`model.*` keys but loader instantiated bare MistralModel (no `model.`
prefix). Add _convert_for_bare_mistral_model to strip the prefix and
drop lm_head before load_state_dict
- _materialize_remaining_meta_tensors: after load_state_dict, replace any
still-meta parameters (norms→ones, others→zeros) and buffers so the
cache→VRAM move can't fail on partial state dicts, with a warning
listing what was missing
- llama.cpp converter: map attn_q_norm/attn_k_norm (Mistral 3.x qk-norm
variants), with ordering before attn_q/attn_k to avoid bad rewrites
Tokenizer / processor fallback
- _load_processor_with_offline_fallback walks a list of sources
(black-forest-labs/FLUX.2-dev tokenizer subfolder, then
mistralai/Mistral-Small-3.1-… and 3.2-…), trying AutoProcessor then
AutoTokenizer for each, cache-first then online. Final error spells
out the three workarounds (install Diffusers folder, set HF_ENDPOINT,
pre-cache the tokenizer)
- flux2_dev_text_encoder: try multimodal `[{type, text}]` chat template
first (PixtralProcessor / Mistral3Processor), fall back to plain
string content (AutoTokenizer), then to manual [INST]…[/INST]
Qwen3 encoder probe strictness
- _get_qwen3_variant_from_state_dict and _get_variant_from_config now
return None / raise NotAMatchError for unknown hidden_size instead of
silently defaulting to qwen3_4b. The old fallback meant any llama.cpp
GGUF causal LM (Mistral, Llama, …) was wrongly classified as Qwen3 —
visible when the Mistral 3.x GGUF was identified as a Qwen3-4B encoder
- Checkpoint / GGUF / Diffusers loaders propagate the strictness
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds end-to-end support for FLUX.2 [dev] alongside the existing FLUX.2 Klein implementation. Dev is a 32B guidance-distilled rectified flow transformer that uses Mistral Small 3.1 (24B) as its sole text encoder instead of Klein's Qwen3, with
joint_attention_dim=15360and a guidance-active distillation. It shares the 32-channelAutoencoderKLFlux2VAE and the 4D-RoPE sampling backend with Klein, so most of the existing infrastructure is reused — only Mistral-specific loaders, configs, and graph wiring are new.Backend
Flux2VariantType.Dev,ModelType.MistralEncoder,ModelFormat.MistralEncoder, newMistralVariantType.Small3_1. Added toAnyVariantandvariant_type_adapter.ModelRecordChanges.variantunion extended.context_in_dim = 15360(main) /vec_in_dim = 5120/hidden_size = 6144(LoRA, all formats). Existing Klein and FLUX.1 probes preserved.Main_Diffusers_Flux2_Configalso accepts theFlux2Pipeline/Flux2Transformer2DModelclass names.configs/mistral_encoder.py: Diffusers folder, single-file safetensors, and GGUF configs underBaseModelType.Any / ModelType.MistralEncoder. Folder probe excludes full pipelines (those matchMain_Diffusers_Flux2_Config).load/model_loaders/mistral_encoder.py: three loaders (Diffusers viaAutoModel, single-file viaMistralModel, GGUF viaMistralModel+ llama.cpp key conversion). Includes:_convert_for_bare_mistral_model: strips themodel.prefix and dropslm_headso aMistralForCausalLMstate dict loads cleanly into bareMistralModel._materialize_remaining_meta_tensors: replaces any params/buffers still on the meta device afterload_state_dict(norms → ones, others → zeros) with a warning listing what was missing, so the model cache → VRAM move can't fail on partial state dicts.attn_q_norm/attn_k_norm(Mistral 3.x QK-norm),attn_q/k/v/output,attn_norm,ffn_*, plus the roottoken_embd/output_norm/outputkeys._load_processor_with_offline_fallbackwalks a list of sources (black-forest-labs/FLUX.2-dev:tokenizer, thenmistralai/Mistral-Small-3.1-…, then…-3.2-…), tryingAutoProcessorandAutoTokenizerfor each, cache-first then online. Final error spells out three workarounds.Flux2Transformer2DModeldiffusers defaults are already dev's (8+48 blocks,joint_attention_dim=15360,mlp_ratio=3.0, 4D RoPE). No new transformer loader code needed.flux2/denoise.py+sampling_utils.pyare model-agnostic — same 32-channel VAE, 4D RoPE, packing, guidance vector, scheduler support — no changes needed.Invocations
flux2_dev_model_loader,flux2_dev_text_encoder,flux2_dev_lora_loader(+ collection variant).MistralEncoderFieldadded tomodel.py.(B, seq, 15360)matching the transformer'sjoint_attention_dim. Tries multimodal[{type, text}]content first (PixtralProcessor / Mistral3Processor) and falls back to plain-string content, then to manual[INST]…[/INST]formatting.flux2_denoise/flux2_vae_decode/flux2_vae_encodeare reused unchanged — they're already model-agnostic.Qwen3 probe strictness (bugfix)
_get_qwen3_variant_from_state_dict/_get_variant_from_confignow returnNone/ raiseNotAMatchErrorfor unknownhidden_sizeinstead of silently defaulting toqwen3_4b. The old fallback meant any llama.cpp GGUF causal LM (Mistral, Llama, …) was misclassified as Qwen3 — caught when a Mistral 3.x GGUF was identified asqwen3_4b.Frontend
MistralEncoderModelConfigtype +isMistralEncoderModelConfig,isFlux2DevMainModelConfig,isFlux2DevDiffusersMainModelConfigguards.selectMistralEncoderModels,selectFlux2DevDiffusersModels,useMistralEncoderModels,useFlux2DevDiffusersModelshooks/selectors.paramsSlice:flux2DevVaeModel/flux2DevMistralEncoderModel/flux2DevSourceModelfields + reducers +selectIsFlux2Dev/selectIsFlux2Kleinselectors.ParamFlux2DevModelSelectcomponent (VAE + Mistral Encoder dropdowns), wired intoAdvancedSettingsAccordion(Dev shows the Mistral selector, Klein keeps the Qwen3 selector).buildFLUXGraph: dev branch forflux2_dev_model_loader/flux2_dev_text_encoder/ sharedflux2_denoisewith full txt2img / img2img / inpaint / outpaint support, plus multi-reference image editing viaflux_kontextcollect chain (Flux2RefImageExtensionis model-agnostic). Dev model loader'svaeis wired into bothflux2_denoise(required for BN statistics / inpaint) andflux2_vae_decode.addFlux2DevLoRAshelper, wires LoRAs throughflux2_dev_lora_collection_loader.readiness.ts: variant-aware FLUX.2 readiness — dev requiresflux2DevVaeModel+flux2DevMistralEncoderModel(or a Dev diffusers source), Klein keeps the Qwen3/VAE check.hasFlux2DevDiffusersSourcethreaded through both generate and canvas tabs.zModelType/zModelFormat/zFlux2VariantTypeextended formistral_encoder/mistral_small_3_1/dev. NewzMistralVariantTypeinAnyModelVariantunion. Display-name maps updated.noFlux2DevVaeModelSelected,noFlux2DevMistralEncoderModelSelected.Starter models (7 entries)
black-forest-labs/FLUX.2-devDiffusers (~80 GB, Non-Commercial)diffusers/FLUX.2-dev-bnb-4bitDiffusers (NF4, ~18 GB w/ offload)city96/FLUX.2-dev-ggufQ4 / Q6 / Q8 transformer-only entries depending on the FLUX.2 VAE + a Mistral encoderRelated Issues / Discussions
None yet. The upstream
feature/flux2-noncommercial-licensework is unrelated but worth coordinating with — FLUX.2 [dev] inherits the BFL Non-Commercial License and is flagged inisNonCommercialMainModelConfig.QA Instructions
Backend probing (no model load required, takes seconds):
Manual probe against any local FLUX.2 [dev] artifacts (folder, transformer subfolder, text_encoder subfolder, GGUF, single-file VAE, Mistral GGUF) — all should classify with the matching dev / mistral_encoder configs and existing FLUX.2 Klein fixtures should still classify identically.
Frontend checks (from
invokeai/frontend/web):End-to-end with a real model:
city96/FLUX.2-dev-ggufquants (Q4_K_M is a good balance at ~18 GB) or the fullblack-forest-labs/FLUX.2-devdiffusers folderflux2-vae.safetensors(or extract from a diffusers source)mistralai/Mistral-Small-3.1-24B-Instruct-2503GGUF (or the BFLtext_encodersubfolder, or thediffusers/FLUX.2-dev-bnb-4bittext_encoder for NF4)MainModelDefaultSettings.from_base(Flux2, Dev): 28 steps, guidance 3.5, CFG 1.0, 1024×1024.Tokenizer/processor caveat: the GGUF Mistral encoder has no bundled tokenizer; the loader pulls from a fallback chain (BFL FLUX.2-dev
tokenizer/, thenmistralai/Mistral-Small-3.1-…, then…-3.2-…). IfHF_ENDPOINTis unreachable and nothing is cached, the loader raises a clear error with three documented workarounds (install the Diffusers folder, set a reachable endpoint, or pre-cache withhuggingface-cli download …).Merge Plan
nulland the slice version did not change. Existing user states keep working — Dev-specific fields just stay null until the user picks a Dev model.flux2-dev-Q2_K.gguf+ Mistral 3.2 Q3_K_S GGUF + standalone FLUX.2 VAE setup; full-precision Diffusers pipeline path is structurally covered but I have not run inference on the 80 GB bf16 weights.isNonCommercialMainModelConfig.Checklist
feat(flux2): add FLUX.2 [dev] supportnull, slice version unchangedWhat's Newcopy (if doing a release after this PR)