Skip to content

feat(sd3): support LoRAs - #9824

Open
Pfannkuchensack wants to merge 3 commits into
invoke-ai:mainfrom
Pfannkuchensack:feat/sd3-lora
Open

Pfannkuchensack wants to merge 3 commits into
invoke-ai:mainfrom
Pfannkuchensack:feat/sd3-lora

Conversation

@Pfannkuchensack

@Pfannkuchensack Pfannkuchensack commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Summary

SD 3 / 3.5 LoRAs could not be used, and most installed as another model:

  • diffusers and PEFT files went to the Qwen-Image probe, which shares the attention names.
  • kohya files that also train both text encoders became SDXL.
  • DreamBooth repos were "unknown".
  • SD 3 had no LoRA loader and its denoise node did not patch.

After this PR, SD 3 LoRAs are identified with their size and applied:

  • Formats:

    • diffusers, ai-toolkit and PEFT (transformer., base_model.model.), including attn2, the conv pos_embed.proj and top-level modules.
    • SimpleTuner LyCORIS (lycoris_ with flattened names, LoKR).
    • kohya sd-scripts with the original MMDiT names. The fused qkv is split into thirds, and the two adaLN projections stored with swapped halves are unswapped. Both match diffusers' single-file converter, which a drift test checks.
    • DreamBooth folders, which read the top-level weights, not checkpoint-* copies.
    • Text encoders: CLIP-L and CLIP-G parts apply to their encoders. T5 parts are dropped with a warning.
    • Layouts that cannot be applied are refused at install: DoRA, unknown layers, incomplete layers, and LoKR/LoHA on layers that must be split or swapped.
  • Recognition: SD 3 is identified by its widths (1536 Medium, 2432 Large), because Qwen-Image uses the same attention names. The Qwen-Image probe now rejects SD 3 widths and CogView4's attn1. The generic SD/SDXL probe rejects kohya joint_blocks.

  • Size: new optional variant (sd3_medium / sd3_large) on SD 3 Diffusers mains and SD 3 LoRAs, set at install. SD 3 Medium and 3.5 Medium share sd3_medium. The webv2 concept picker hides LoRAs of the other size when both sides record one.

  • Nodes: new sd3_lora_loader and sd3_lora_collection_loader with transformer, CLIP-L and CLIP-G fields. sd3_text_encoder patches CLIP-L and CLIP-G with separate prefixes. sd3_denoise (1.2.0 → 1.3.0) refuses, before it loads the transformer:

    • a LoRA of the other size, by recorded variant or by shape;
    • a LoRA with dual attention on SD 3 Medium.

    It patches in place, or through a sidecar with FP8 Storage, and then reserves the patches.

  • webv2 and docs: SD 3 joins LORA_SUPPORTED_BASES, the graph routes transformer and CLIP-L/G through the collection loader, and the size has labels and options in the model manager. The docs add a LoRA section to the SD 3 page and update the format matrix and the Generate panel page.

Related Issues / Discussions

Closes #9741

QA Instructions

Automated:

  • CUDA_VISIBLE_DEVICES="" uv run --no-sync pytest -n 4 tests/backend/model_manager tests/backend/patches tests/backend/architectures tests/model_identification tests/app: 10,903 passed. The only failures were the bitsandbytes nf4/int8 tests, which need CUDA; with the GPU visible, tests/backend/model_manager/load/model_cache/torch_module_autocast passes. Each new test file also passes on its own.
  • New coverage:
    • The kohya conversion matches convert_sd3_transformer_checkpoint_to_diffusers on random MMDiT weights, for Medium with attn2 and for Large.
    • The real key layouts land on the production-size transformer.
    • Probe exclusivity: each SD 3 fixture is accepted by exactly one config. The Qwen, CogView4, FLUX (incl. OneTrainer flat names), Anima and other fixtures are not taken as SD 3.
    • Stripped real files are in the identification suite: kohya Clybius/mspaint-style-sd3.5medium and a DreamBooth folder.
    • Records without a variant stay readable.
    • Denoise: the fit refusal comes before the transformer lock, and the FP8 Storage sidecar both computes the LoRA and reserves it.
    • invoke() hands CLIP-L and CLIP-G their own layers.
  • ruff check/format, pnpm -C invokeai/frontend/api lint and test, pnpm -C invokeai/frontend/webv2 lint, and the webv2 generation/model tests (incl. settings/taxonomy and the concepts browser test): passed. webv1 tsc has the same 16 errors as main.
  • Locally, the HF download tests fail only when HF_ENDPOINT points at a mirror that lacks the Xet endpoints; they pass without it.

Manual E2E on an RTX 4090 with SD 3.5 Medium (Diffusers), same seed with and without LoRA:

  • LoRAs:

    • AdamLucek/sd3.5M-jareklfoto-LoRA-DreamBooth (diffusers)
    • bghira/sd35m-sfwbooru-lycoris (SimpleTuner LoKR)
    • jieliu/SD3.5M-FlowGRPO-PickScore (PEFT)
    • Clybius/mspaint-style-sd3.5medium (kohya, with attn2 and the swapped adaLN)
    • linoyts/Yarn_art_SD3_LoRA (diffusers with CLIP-L/G)
    • tensorart/stable-diffusion-3.5-medium-turbo (4 steps)

    Each applies visibly. Yarn with the text-encoder part differs from yarn with the transformer part only. The turbo LoRA gives a clean image at 4 steps where the base model does not.

  • FP8 Storage: mspaint and yarn with CLIP.

  • Logs: the server log contains no Failed to find module for LoRA layer key or Skipping LoRA layer.

  • Browser (webv2): for SD 3.5 Medium, the concepts slot shows only Medium LoRAs, and a LoRA marked sd3_large is hidden. Sending a Large LoRA through a hand-built graph fails before loading with "LoRA '…' was trained for SD 3.5 Large and does not fit this SD 3 / 3.5 Medium model."

Review

The review findings are resolved:

  • The speculative lora_transformer_ prefix is gone. No SD 3 file uses it, and it tied with FLUX OneTrainer files.
  • Incomplete LyCORIS layers are refused at install instead of failing at conversion.
  • Whether a kohya adaLN is swapped is decided by name and shape, so a LoKR on an unswapped modulation converts.
  • The size refusal is readable and happens before the transformer is locked.
  • override_fields is no longer mutated.
  • One width-to-size helper serves the probe, the main config and the denoise.

Remaining limitations:

  • OneTrainer SD 3 files that use lora_transformer_ and also train both text encoders are still taken as SDXL, as before this PR.
  • A LoKR under FP8 Storage is applied in weight space and is slower than a plain LoRA.
  • SD 3.5 Large is covered by meta/ground-truth tests only; no Large checkpoint was run.
  • Separate default settings per SD 3 size are not part of this PR.
  • The legacy UI (webv1) lists these LoRAs but does not wire them into SD 3 graphs.

Compatibility / Rollout

  • Persisted configs: variant is optional and defaults to None, so stored SD 3 records stay valid and discriminator tags do not change. New installs record it. Existing mains and LoRAs get it, and misfiled LoRAs get the right base, through Re-identify in the Model Manager. Re-identify also resets a model's default settings, which the docs note. The picker and the denoise accept records without a size, and the denoise then checks the shapes.
  • API: SD3VariantType joins the variant unions; the OpenAPI schema and types are regenerated.
  • Merging with the ERNIE-Image ([enhancement]: LoRA support for ERNIE-Image #9739) and Ideogram 4 ([enhancement]: LoRA support for Ideogram 4 #9740) LoRA PRs: whichever merges later needs generateGraphNodeTypes.json regenerated.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Identify diffusers, PEFT, SimpleTuner LyCORIS and kohya SD 3/3.5 LoRAs (and DreamBooth folders) with their Medium/Large size instead of misfiling them as Qwen-Image or SDXL; refuse layouts that cannot be applied.
Add sd3_lora_loader/collection_loader; the denoise refuses LoRAs of another size before loading, patches transformer and CLIP-L/G (T5 skipped), and reserves sidecar patches under FP8 Storage.
Record the SD 3 size on mains and LoRAs, filter the webv2 concept picker by it, and document LoRA support.
@github-actions github-actions Bot added python PRs that change python files invocations PRs that change invocations backend PRs that change backend files services PRs that change app services frontend PRs that change frontend files python-tests PRs that change python tests docs PRs that change docs labels Oct 12, 2026
# Conflicts:
#	docs/src/content/docs/Users Guide/03.Models/03.model-format-support.mdx
#	docs/src/content/docs/Users Guide/04.Image Generation/generate-panel.mdx
#	invokeai/backend/model_manager/configs/lora.py
#	invokeai/frontend/api/schema.ts
#	tests/app/invocations/test_sd3_text_encoder.py

This branch has not been deployed

No deployments
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 docs PRs that change docs frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files python-tests PRs that change python tests services PRs that change app services

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[enhancement]: LoRA support for SD 3

1 participant