transformer., bare-diffusers, and lora_transformer_ bases are already in
network-key form for every arch, yet each per-arch resolve_targets repeated the
same passthrough branch for them. Move that into a shared
PASSTHROUGH_PREFIXES_DEFAULT set consulted by resolve_group_targets, leaving each
arch's resolve_targets to only the prefixes it actually rewrites (kohya / BFL).
lycoris_ stays in flux2, the one arch that recognizes it.
Pure refactor: the same keys resolve to the same modules.
Loading a Chroma transformer override via UNET dropdown crashed at
inference with a shape mismatch in unpack_uint4: from_single_file does
not integrate quantization_config the way from_pretrained does, so the
dequantizer state was set up but the .weight tensor was never packed.
CHROMA_SPEC routes through native_transformer with the diffusers
Chroma converter, which loads bare and quantizes explicitly via
sdnq_quantize_model.
Replaces chroma's four family loaders with thin wrappers binding
native_loader's generics to chroma's prefix tuples and resolve_targets.
resolve_targets folds the Flux-to-diffusers rename table and the two
fused-weight splits into one path-resolution step:
- img_attn.qkv / txt_attn.qkv: ChunkSpec(idx, total=3) for equal Q/K/V
- single_blocks.linear1: ChunkSpec(start, end) for the unequal
Q/K/V/proj_mlp partition
QKV_DIMS and LINEAR1_DIMS stay as module-level constants (tests patch
them for the scaled-down mock).
Behavior changes:
- LoKR on equal-chunk QKV now dispatches to NetworkModuleLokrChunk
instead of NetworkModuleLokrSliceChunk (the slice variant was used
pre-migration for both forms since chroma had no equal-chunk path).
- LoHA on fused img_attn.qkv now binds via NetworkModuleHadaChunk
instead of being skipped; the shared HadaChunk added in the flux2 PR
is general for equal-chunks dispatch.
parse_key returns (prefix_used, base, suffix) instead of the old
(flat_key, suffix); rename happens in resolve_targets. test_static_rename_table
replaced with test_resolve_targets_static_renames driving the same
remappings through the new interface.
try_load_lokr renamed slice_info keys via static_rename but didn't add
the lora_transformer_ prefix that apply_static_rename adds to the groups
dict. slice_info.get(network_key) always returned None for fused targets,
so the loader fell back to NetworkModuleLokr instead of
NetworkModuleLokrSliceChunk on every fused-QKV / fused-linear1 adapter.
The full kron(w1, w2) was applied against split target modules, either
shape-mismatching at apply time or broadcasting wrong.
Caught by test_lokr_bfl_img_attn_qkv_slice_chunked. No real-world chroma
LoKR adapters on fused targets are known in the wild.
Lifts the slice variant from chroma_lora into network_lokr so the generic
LoKR loader can dispatch to either NetworkModuleLokrChunk (equal chunks)
or NetworkModuleLokrSliceChunk (unequal ranges) based on ChunkSpec shape.
chroma_lora keeps the same slice path through an updated import.
Add a per-model native LoRA path for Chroma that handles both PEFT-style
(diffusion_model.<dotted>.lora_A.weight, no alpha) and kohya-flat
(lora_unet_<flat> with .alpha) layouts, plus LoKR/LoHA/OFT/DoRA.
Resolves issue #4169 (ostris ai-toolkit Chroma LoRAs failed to load).
- new pipelines/chroma/chroma_lora.py with try_load_lora/lokr/loha/oft
- fused-QKV expansion: 3-way equal for img/txt qkv, 4-way unequal
[3072, 3072, 3072, 12288] for single linear1 (Q+K+V+proj_mlp)
- NetworkModuleLokrSliceChunk for unequal-row LoKR slicing
- path rewrite from Flux-source to ChromaTransformer2DModel naming
- lora_load.py: dispatch chroma to the native loader and narrow the
kohya-Flux convert gate to f1 only