Skip to content

feat(anima): Anima Controlnets + Inpainting Adapter#9284

Open
kappacommit wants to merge 11 commits into
invoke-ai:mainfrom
kappacommit:anima-inpainting
Open

feat(anima): Anima Controlnets + Inpainting Adapter#9284
kappacommit wants to merge 11 commits into
invoke-ai:mainfrom
kappacommit:anima-inpainting

Conversation

@kappacommit

@kappacommit kappacommit commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for kohya-ss's ControlNet-LLLite adapters for Anima — small add-on models (8–66 MB) that bring two user-facing improvements:

  • Better inpainting and outpainting. With the new optional Inpaint Adapter (Advanced settings), the model is conditioned on the surrounding image and the results blend in cleanly. In fixed-seed side-by-side tests, seam artifacts dropped by ~60% and the composition breaks disappeared.
  • Control layers for Anima. Sketch/scribble, lineart, depth, and pose adapters can now guide Anima generations from a drawing or reference image — the same Control Layer workflow as other model families.

All variants are available as one-click starter model installs, work with existing Anima setups, and add no measurable generation time. The inpaint adapter applies automatically when inpainting/outpainting on Canvas; control adapters are picked per control layer.

Under the hood: a new backend module (ported with attribution from kohya's Apache-2.0 reference implementation), model-manager registration that routes inpaint vs. control variants to the right pickers, an anima_lllite node, and canvas graph wiring for both surfaces.

Related Issues / Discussions

N/A — integrates https://huggingface.co/kohya-ss/Anima-LLLite (reference implementations: kohya-ss/ComfyUI-Anima-LLLite, kohya-ss/sd-scripts).

QA Instructions

Requires the Anima starter models (Base 1.0, Qwen3 0.6B encoder, QwenImage VAE).

  1. Inpainting Adapter
  • Install it via starter models
  • In the advanced section below the VAE/Encoder, select the inpainting adapter
  • Do inpainting just as you always would, apply an inpaint mask and go
  • Inpainting quality should appear marginally more coherent and logical
  1. Controlnets
  • Install via starter models
  • Use just as you would any other controlnet

Merge Plan

No DB schema changes. Redux changes are additive with defaults (no slice migration needed). The 7 commits are two logical stacks (inpainting adapter, then control layers) and can be reviewed in order.

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 (not required — additive fields with defaults, per existing precedent)
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

🤖 Generated with Claude Code

Your Name and others added 7 commits June 9, 2026 20:50
Port of kohya-ss ControlNet-LLLite v2 for the Anima DiT (from the
Apache-2.0 ComfyUI-Anima-LLLite reference), restructured to build from
a safetensors state dict + metadata without a transformer instance so
it can live in the model cache. Binds to transformer Linears by path
regex with guaranteed restore, supports the 4-channel inpainting
conditioning (masked RGB + binary mask), and includes the
patch-padding-aware cond sizing helpers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New ControlNet_Checkpoint_Anima_Config probed on the lllite_conditioning1./
lllite_dit_blocks_ key namespace (no overlap with SDXL LLLite or Z-Image
control), a loader that builds the adapter from metadata at the Anima
inference dtype, and an Anima LLLite Inpainting starter model entry for
kohya-ss/Anima-LLLite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New anima_lllite invocation (image + optional mask + model + weight +
step range) and an optional control_lllite input on anima_denoise
(v1.7.0). The conditioning image is built once per generation from the
actual latent dims, the adapter binds after LoRA patching, the
multiplier is step-range gated in both the Euler and scheduler-driver
paths, and restore/clear always run on exit since the adapter instance
is shared via the model cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds an Inpaint Adapter model picker + weight control to the Anima
advanced settings, and wires anima_lllite into the canvas inpaint and
outpaint graphs (all scaled/unscaled branches). The canvas denoise-limit
mask is white = keep, while LLLite expects white = inpaint, so the mask
is inverted via img_lerp before it reaches the adapter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ControlNet_Checkpoint_Anima_Config records cond_in_channels at probe time
(metadata with conv1-shape fallback) so the UI can route inpaint (4ch) and
general control (3ch) variants to different surfaces; null means installed
before this field existed. Adds starter entries for the remaining LLLite
variants: Sketch (any-test-like-v2) plus the Preview3-era depth, scribble,
lineart and pose adapters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
control_lllite now takes a single field or a list (v1.8.0). Adapters are
sorted by model key for deterministic composition, applied in order after
LoRA patching, gated per-step by their own weight and step range, and
restored in reverse order with per-adapter exception isolation since the
bind chain is only safe to unwind LIFO. Duplicate adapter models are
rejected because the model cache shares one instance per key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the anima_lllite control-adapter type to control layers, lifts the
hard unsupported-base block for Anima (t2i/control-lora still warn), and
routes models by conditioning channels: 3ch adapters appear in control
layers, 4ch and legacy installs in the Inpaint Adapter picker. Control
layers and the inpaint adapter feed a shared collect node into the
denoise list input across all generation modes, and duplicate adapter
models block Invoke with a per-layer warning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added python PRs that change python files invocations PRs that change invocations backend PRs that change backend files frontend PRs that change frontend files python-tests PRs that change python tests labels Jun 10, 2026
@kappacommit kappacommit changed the title feat(anima): ControlNet-LLLite support (inpainting adapter + control layers) feat(anima): Anima Controlnets + Inpainting Adapter Jun 10, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lstein lstein added the 6.14.x label Jun 17, 2026
@lstein lstein moved this to 7.0 Theme: Tabbed Layout UI in Invoke - Community Roadmap Jun 17, 2026
@lstein lstein moved this from 7.0 Theme: Tabbed Layout UI to 6.14.x Theme: USER EXPERIENCE in Invoke - Community Roadmap Jun 17, 2026
@Pfannkuchensack

Copy link
Copy Markdown
Collaborator

Findings

  • Medium: invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.ts:271-289
    The new control_lllite collect-node lifecycle is logic-heavy and entirely untested. The accounting
    addedLLLiteAdapters = hasInpaintAdapter ? 1 : 0 (line 272) is then incremented by
    addAnimaLLLiteControl's return, and the collect node is wired to denoise only if the total is > 0,
    otherwise deleted (lines 284-288). A regression here has two concrete failure modes: (a) an empty
    collect node left wired into denoise.control_lllite -> backend graph/validation error, or (b)
    hasInpaintAdapter counted as 1 while addInpaint/addOutpaint did not actually wire the node ->
    collect kept but under-fed. There is already a graph-builder test file at
    invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.test.ts, and it
    contains ZERO references to lllite/control_lllite (verified: grep count 0). The inpaint-adapter
    wiring in addInpaint.ts:203-223 / addInpaint.ts:297-317 and addOutpaint.ts:170-190 /
    addOutpaint.ts:319-339 (including the white=inpaint mask inversion via img_lerp min=255/max=0) is
    likewise unexercised.
    To expose this issue, add a test that builds the Anima graph for generationMode 'inpaint' with and
    without params.animaLLLiteModel and asserts: the control_lllite collect node exists and has an edge
    to denoise.control_lllite exactly when an adapter is present, that it is absent/deleted when no
    adapter and no control layers exist, and that the anima_lllite node receives both an image edge and
    an inverted-mask edge.

  • Medium: invokeai/app/invocations/anima_denoise.py:304-344 (_build_lllite_cond_image) and
    invokeai/app/invocations/anima_denoise.py:346-359 (_get_lllite_multiplier)
    Both contain branch logic with no automated coverage. _build_lllite_cond_image raises ValueError for
    a 4-channel (inpaint) adapter when mask_name is None (lines 324-328), logs a warning and drops the
    mask for a 3-channel adapter (lines 339-343), and raises for any other channel count (lines 334-338).
    _get_lllite_multiplier gates the weight to 0.0 outside [floor(begintotal), ceil(endtotal)].
    The Anima LLLite backend test module covers from_state_dict, passthrough, bind/restore, multi-adapter
    composition, and _normalize_control_lllite, but neither of these two methods. A regression that, e.g.,
    inverts the channel test or the step-range comparison would silently mis-condition inpainting or
    apply an adapter outside its intended step window with no failing test.
    To expose this issue, add a test that calls _build_lllite_cond_image with a stub model of
    cond_in_channels=4 and mask_name=None and asserts ValueError; with cond_in_channels=3 and a mask
    asserts the returned tensor has 3 channels and a warning was logged; and a test that asserts
    _get_lllite_multiplier returns 0.0 for a step below first_step and above last_step and the field
    weight in between.

The Inpainting is so much better with that. I tested the others Controlnet and had only the problem that the 1.0 Weight is a bit low to show a good effect on the control. I had to use atleast 1.35~1.55.

…node wiring

Backend: add tests for AnimaDenoiseInvocation._get_lllite_multiplier covering the
floor/ceil step-window boundaries and inclusive endpoints.

Frontend: add tests for the buildAnimaGraph control_lllite collect-node lifecycle
— wired into denoise.control_lllite when an inpaint adapter is present, and not
created when there is neither an adapter nor control layers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kappacommit

Copy link
Copy Markdown
Contributor Author

Findings

* Medium: invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.ts:271-289
  The new control_lllite collect-node lifecycle is logic-heavy and entirely untested. The accounting
  `addedLLLiteAdapters = hasInpaintAdapter ? 1 : 0` (line 272) is then incremented by
  addAnimaLLLiteControl's return, and the collect node is wired to denoise only if the total is > 0,
  otherwise deleted (lines 284-288). A regression here has two concrete failure modes: (a) an empty
  collect node left wired into denoise.control_lllite -> backend graph/validation error, or (b)
  hasInpaintAdapter counted as 1 while addInpaint/addOutpaint did not actually wire the node ->
  collect kept but under-fed. There is already a graph-builder test file at
  invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.test.ts, and it
  contains ZERO references to lllite/control_lllite (verified: grep count 0). The inpaint-adapter
  wiring in addInpaint.ts:203-223 / addInpaint.ts:297-317 and addOutpaint.ts:170-190 /
  addOutpaint.ts:319-339 (including the white=inpaint mask inversion via img_lerp min=255/max=0) is
  likewise unexercised.
  To expose this issue, add a test that builds the Anima graph for generationMode 'inpaint' with and
  without params.animaLLLiteModel and asserts: the control_lllite collect node exists and has an edge
  to denoise.control_lllite exactly when an adapter is present, that it is absent/deleted when no
  adapter and no control layers exist, and that the anima_lllite node receives both an image edge and
  an inverted-mask edge.

* Medium: invokeai/app/invocations/anima_denoise.py:304-344 (_build_lllite_cond_image) and
  invokeai/app/invocations/anima_denoise.py:346-359 (_get_lllite_multiplier)
  Both contain branch logic with no automated coverage. _build_lllite_cond_image raises ValueError for
  a 4-channel (inpaint) adapter when mask_name is None (lines 324-328), logs a warning and drops the
  mask for a 3-channel adapter (lines 339-343), and raises for any other channel count (lines 334-338).
  _get_lllite_multiplier gates the weight to 0.0 outside [floor(begin_total), ceil(end_total)].
  The Anima LLLite backend test module covers from_state_dict, passthrough, bind/restore, multi-adapter
  composition, and _normalize_control_lllite, but neither of these two methods. A regression that, e.g.,
  inverts the channel test or the step-range comparison would silently mis-condition inpainting or
  apply an adapter outside its intended step window with no failing test.
  To expose this issue, add a test that calls _build_lllite_cond_image with a stub model of
  cond_in_channels=4 and mask_name=None and asserts ValueError; with cond_in_channels=3 and a mask
  asserts the returned tensor has 3 channels and a warning was logged; and a test that asserts
  _get_lllite_multiplier returns 0.0 for a step below first_step and above last_step and the field
  weight in between.

The Inpainting is so much better with that. I tested the others Controlnet and had only the problem that the 1.0 Weight is a bit low to show a good effect on the control. I had to use atleast 1.35~1.55.

updated with new tests based on feedback.

regarding the weak controlnets, unfortunately the model creator themselves calls out that theyre kind of weak due to being trained on the previous version of the Anima (Preview 3). But to my knowledge there are no other controlnets out there, these are still the best available.

The `as never` cast on the whole mock implementation erased the contextual type
of its parameter, tripping no-implicit-any under `tsc --noEmit`. Type the arg via
the real addInpaint signature and cast only the returned stand-in node; return a
resolved Promise to avoid require-await.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Pfannkuchensack Pfannkuchensack left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.14.x backend PRs that change backend files frontend PRs that change frontend files invocations PRs that change invocations python PRs that change python files python-tests PRs that change python tests

Projects

Status: 6.14.x Theme: USER EXPERIENCE

Development

Successfully merging this pull request may close these issues.

3 participants