Skip to content

feat(nodes): Add a LoRA collection picker node that selects and applies an indefinite number of LoRAs#9259

Open
lstein wants to merge 8 commits into
mainfrom
lstein/feat/lora-collection-node
Open

feat(nodes): Add a LoRA collection picker node that selects and applies an indefinite number of LoRAs#9259
lstein wants to merge 8 commits into
mainfrom
lstein/feat/lora-collection-node

Conversation

@lstein
Copy link
Copy Markdown
Collaborator

@lstein lstein commented May 31, 2026

Summary

Using LoRAs in workflows currently requires a fixed number of Apply LoRA nodes, and every node must have a LoRA selected or the graph won't fire — making variable-LoRA workflows awkward. This PR extends the Apply LoRA Collection - * nodes to allow users to select and apply any number of LoRAs, or none at all.

  • A model picker adds a LoRA, filtered to the node's compatible base model(s).
  • Each row has a weight input and a trash icon to remove it.
  • The list can be empty and still valid, so the node fires with zero LoRAs.

Existing connections still work (Select LoRA → collection loader, Collect → collection loader), because the LoRAField type name is unchanged — the field simply became stateful/editable in addition to being connectable.

image

How it works

LoRAField is now a stateful field type whose value is a list of { lora, weight }. The collection loaders' loras input gains ui_model_base/ui_model_type metadata, which the new inline editor reads to filter the picker. The frontend reads ui_model_base from the live OpenAPI schema, so no schema.ts regen is needed.

Backend

  • Added ui_model_base + ui_model_type=LoRA to the loras field of every *LoRACollectionLoader (SD1.5, SDXL, FLUX, Anima, Z-Image, Qwen Image, Flux2 Klein); bumped node versions.
  • Fixed a pre-existing base-validation bug: the FLUX and FLUX.2 Klein collection loaders both carried an identical copy-pasted assert lora.lora.base in (Flux, Flux2), so each accepted the other architecture's LoRAs even though FLUX.1 and FLUX.2 Klein are separate pipelines with non-interchangeable LoRAs. Each now validates against its own base only, via an explicit ValueError (helpful message, not stripped under python -O) matching the Anima/Z-Image loaders.

Frontend

  • Registered the stateful LoRAField type (zod schemas, guards, unions) in field.ts.
  • Collection template builder + empty-list default; fieldLoRACollectionValueChanged reducer.
  • New LoRAFieldCollectionInputComponent (reuses ModelPicker, useLoRAModels, zModelIdentifierField.parse, DEFAULT_LORA_WEIGHT_CONFIG); wired into InputFieldRenderer.
  • Added unit tests for the new schemas/guards.

QA / Testing

  • Backend: ruff check + ruff format --check pass on all changed files.
  • Frontend: pnpm lint (tsc / eslint / prettier / dpdm / knip) all pass; 1144 unit tests pass, no type errors.
  • Manual: confirmed the inline +/weight/trash editor renders on the collection nodes, the picker is filtered per base (FLUX shows only Flux, not Flux.2 Klein), and an empty collection still lets the graph run.

Notes for reviewers

  • Reload the app after deploying so the frontend re-fetches the updated OpenAPI schema (the picker filter reads ui_model_base at runtime).

lstein and others added 5 commits May 30, 2026 22:59
Add ui_model_base/ui_model_type metadata to the loras field of every
*LoRACollectionLoader so the upcoming inline LoRA collection editor can
filter the model picker to compatible LoRAs. Bump node versions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the LoRAField type stateful so the loras input on the Apply LoRA
Collection nodes can be edited directly on the node: a model picker adds
a LoRA (filtered to the node's compatible base via ui_model_base), a
weight input per row, and a trash icon to remove. An empty collection is
valid, so the node fires with zero LoRAs — fixing the fixed-count /
must-select-a-LoRA pain points. Connections from Select LoRA still work
since the field type name is unchanged.

- field.ts: register stateful LoRAField (value = list of {lora, weight}),
  guards, unions
- buildFieldInputTemplate/Instance: collection builder + empty-list default
- nodesSlice: fieldLoRACollectionValueChanged reducer
- LoRAFieldCollectionInputComponent: the inline editor
- InputFieldRenderer: dispatch to the new component
- field.test.ts: schema/guard unit tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Annotate the test fixtures with their field types so tsc (lint:tsc) does
not widen literals like fieldKind/base; vitest's typecheck did not catch
this.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The FLUX collection loader showed both Flux and Flux.2 Klein LoRAs in the
picker. Flux.2 has its own Apply LoRA Collection - Flux2 Klein node, so
restrict the FLUX collector's ui_model_base to [Flux], matching the single
Apply LoRA - FLUX loader. The runtime assert still accepts Flux2 (a safe
superset), so connected inputs are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both the FLUX and FLUX.2 Klein collection loaders carried an identical
copy-pasted `assert lora.lora.base in (Flux, Flux2)`, so each accepted the
other architecture's LoRAs even though FLUX.1 and FLUX.2 Klein are separate
pipelines with non-interchangeable LoRAs. Validate each against its own base
only (FLUX->Flux, Flux2 Klein->Flux2) and replace the bare assert with an
explicit ValueError (helpful message; not stripped under python -O), matching
the Anima/Z-Image loaders.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added python PRs that change python files Root invocations PRs that change invocations frontend PRs that change frontend files labels May 31, 2026
@lstein lstein added the 6.14.x label May 31, 2026
@lstein lstein moved this to 6.14.x Theme: USER EXPERIENCE in Invoke - Community Roadmap May 31, 2026
@lstein lstein changed the title feat(nodes): inline-editable LoRA collection on Apply LoRA Collection nodes feat(nodes): Add a LoRA collection picker node that selects and applies an indefinite number of LoRAs May 31, 2026
lstein added a commit to lstein/InvokeAI that referenced this pull request May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.14.x frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files Root

Projects

Status: 6.14.x Theme: USER EXPERIENCE

Development

Successfully merging this pull request may close these issues.

2 participants