Skip to content

fix(krea2-lora): validate kohya keys by un-flattening, not by prefix - #9518

Open
Pfannkuchensack wants to merge 2 commits into
invoke-ai:mainfrom
Pfannkuchensack:fix/krea2-kohya-foreign-lora-guard
Open

fix(krea2-lora): validate kohya keys by un-flattening, not by prefix#9518
Pfannkuchensack wants to merge 2 commits into
invoke-ai:mainfrom
Pfannkuchensack:fix/krea2-kohya-foreign-lora-guard

Conversation

@Pfannkuchensack

Copy link
Copy Markdown
Member

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 writes
lora_unet_[llm_adapter_]blocks_<idx>_ (anima_lora_constants._KOHYA_ANIMA_RE). A mislabeled
Wan 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 failure
mode install-time validation exists to prevent.

2. The doubled-separator spelling could not be installed.

The converter deliberately tolerates lora_unet__blocks_... (the lstrip, plus a dedicated
test), 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 lstrip the converter uses and is
accepted. The dotted layouts (transformer., diffusion_model., text_encoder.) still match
by prefix as before.

Both notes fall out of one change: split_kohya_krea2_key() is now the single place that splits
a 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.py cannot import krea2_lora_conversion_utils: it pulls
in the patch layers, which import model_manager.load, closing an import cycle back into
model_manager.configs. The reconstruction helpers therefore moved to krea2_lora_constants,
the same reason anima_lora_constants exists. The move is verbatim apart from three names
becoming public; no logic changed.

One intended behavior change beyond the two notes. An adapter targeting an nn.Sequential
position 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:

pytest tests/backend/patches tests/backend/model_manager/configs

818 passed, 3 skipped locally.

The 22 new parametrized cases in tests/backend/model_manager/configs/test_krea2_lora_config.py
are 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 were
wrongly rejected, and the 3 non-Linear Sequential indices. (The llm_adapter_blocks_ and
doubled-separator txtfusion cases pass on both sides — the first matched no prefix, the second
reaches auto-detection via its txtfusion substring — they pin the behavior rather than the
regression.)

Manual, if you have the files:

  1. Take a Wan or Anima kohya LoRA, set the base explicitly to Krea-2 during install.
    Before: installs, then produces no visible effect and logs
    Failed to find module for LoRA layer key: per layer. After: install is refused.
  2. Take a Krea-2 kohya LoRA whose keys use the doubled separator (lora_unet__blocks_...),
    install with an explicit Krea-2 base. Before: rejected. After: installs and applies.
  3. Any normal Krea-2 LoRA (diffusers PEFT, native/ComfyUI, or single-separator kohya) —
    unchanged in both install and generation.

Merge Plan

Nothing special. Backend only, no DB schema, no redux slice, no API surface change.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Tests added / updated (if applicable)
  • ❗Changes to a redux slice have a corresponding migration — n/a, backend only
  • Documentation added / updated (if applicable) — n/a
  • Updated What's New copy (if doing a release after this PR) — n/a

`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.
@github-actions github-actions Bot added python PRs that change python files backend PRs that change backend files python-tests PRs that change python tests labels Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend PRs that change backend files python PRs that change python files python-tests PRs that change python tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants