fix(krea2-lora): validate kohya keys by un-flattening, not by prefix - #9518
Open
Pfannkuchensack wants to merge 2 commits into
Open
fix(krea2-lora): validate kohya keys by un-flattening, not by prefix#9518Pfannkuchensack wants to merge 2 commits into
Pfannkuchensack wants to merge 2 commits into
Conversation
`lora_unet_blocks_<idx>_` is not Krea-2's spelling alone — Wan writes it verbatim and Anima writes `lora_unet_[llm_adapter_]blocks_<idx>_`. Matching it as a prefix accepted a mislabeled Wan or Anima LoRA under an explicit Krea-2 override, where it installed and then silently no-op'd at generation time: the un-flattener rejects `self_attn`/`cross_attn`/`mlp_layer0`, so every layer warn-skipped. Install-time validation exists to prevent exactly that failure mode. Ask the converter's own un-flattener instead: a kohya key is a Krea-2 key only if its flattened path reconstructs to a leaf of the native module vocabulary. That also fixes the converse — the doubled-separator spelling (`lora_unet__blocks_...`) that the converter deliberately tolerates but no prefix spelled out, so a transformer-only adapter written that way could not be installed at all. Both come from one change: `split_kohya_krea2_key()` is now the only place that splits a kohya key, so the converter's per-module gate, the rewrite it guards, and identification cannot disagree about which module a key is in. The reconstruction helpers move to `krea2_lora_constants` because `configs/lora.py` cannot import the converter — it pulls in the patch layers, which import `model_manager.load`, closing a cycle back into `model_manager.configs`. Same reason `anima_lora_constants` exists. Side effect, tested: an adapter on an `nn.Sequential` position that holds no Linear (`lora_unet_tmlp_1`, `tproj_0`, `txtmlp_2`) no longer installs. It previously matched by prefix and then warn-skipped at apply time. Follow-up to invoke-ai#9449, addressing the two non-blocking notes from review 4888833569.
Pfannkuchensack
requested review from
JPPhoto,
blessedcoolant,
dunkeroni and
lstein
as code owners
August 19, 2026 00:54
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
Follow-up to #9449, addressing the two non-blocking notes from review 4888833569.
1.
lora_unet_blocks_swept Wan and Anima kohya LoRAs into the Krea-2 override.#9449 admitted the kohya/LyCORIS layout by matching per-module prefixes such as
lora_unet_blocks_. That spelling is not Krea-2's alone — Wan writes it verbatim(
wan_lora_conversion_utils._KOHYA_KEY_REGEX) and Anima writeslora_unet_[llm_adapter_]blocks_<idx>_(anima_lora_constants._KOHYA_ANIMA_RE). A mislabeledWan or Anima file with an explicit Krea-2 base override was therefore accepted, installed, and
then silently no-op'd at generation time: the un-flattener rejects
self_attn/cross_attn/mlp_layer0, so every layer warn-skipped. "Installs, then does nothing" is exactly the failuremode install-time validation exists to prevent.
2. The doubled-separator spelling could not be installed.
The converter deliberately tolerates
lora_unet__blocks_...(thelstrip, plus a dedicatedtest), but no prefix in the list spelled out the doubled underscore, so a transformer-only
adapter written that way was rejected by the override — the very class of file that part of
#9449 was meant to make installable.
The fix replaces prefix matching for the kohya layout with the converter's own
reconstruction: a kohya key counts as Krea-2 only if its flattened path un-flattens to a leaf of
the native Krea-2 module vocabulary. Wan/Anima keys do not reach a leaf and are rejected; the
doubled-separator spelling goes through the identical
lstripthe converter uses and isaccepted. The dotted layouts (
transformer.,diffusion_model.,text_encoder.) still matchby prefix as before.
Both notes fall out of one change:
split_kohya_krea2_key()is now the single place that splitsa kohya key into (flat module path, separator, weight suffix). The converter's per-module gate,
the rewrite it guards, and identification all call it, so they cannot disagree about which module
a key belongs to — the invariant round 3 of the review checked by hand is now structural.
Where the code lives.
configs/lora.pycannot importkrea2_lora_conversion_utils: it pullsin the patch layers, which import
model_manager.load, closing an import cycle back intomodel_manager.configs. The reconstruction helpers therefore moved tokrea2_lora_constants,the same reason
anima_lora_constantsexists. The move is verbatim apart from three namesbecoming public; no logic changed.
One intended behavior change beyond the two notes. An adapter targeting an
nn.Sequentialposition that holds no Linear (
lora_unet_tmlp_1,lora_unet_tproj_0,lora_unet_txtmlp_2)previously matched by prefix, installed, and then warn-skipped at apply time. The parsing tree
lists the Linear-bearing positions literally, so these no longer install. Same rationale as
note 1, covered by its own test.
No user-facing behavior changes for correctly-labeled Krea-2 LoRAs: every layout #9449 accepted
is still accepted.
Related Issues / Discussions
Follow-up to #9449 (merged as 77f00f1) — non-blocking notes 2 and 3 of review
#9449 (review), left as optional
follow-ups in the round-3 approval
#9449 (review).
QA Instructions
Automated:
818 passed, 3 skipped locally.
The 22 new parametrized cases in
tests/backend/model_manager/configs/test_krea2_lora_config.pyare load-bearing: checked out against #9449 as merged, 11 of them fail — 5 foreign
lora_unet_blocks_modules that were wrongly accepted, 3 doubled-separator modules that werewrongly rejected, and the 3 non-Linear Sequential indices. (The
llm_adapter_blocks_anddoubled-separator
txtfusioncases pass on both sides — the first matched no prefix, the secondreaches auto-detection via its
txtfusionsubstring — they pin the behavior rather than theregression.)
Manual, if you have the files:
Before: installs, then produces no visible effect and logs
Failed to find module for LoRA layer key:per layer. After: install is refused.lora_unet__blocks_...),install with an explicit Krea-2 base. Before: rejected. After: installs and applies.
unchanged in both install and generation.
Merge Plan
Nothing special. Backend only, no DB schema, no redux slice, no API surface change.
Checklist
What's Newcopy (if doing a release after this PR) — n/a