Commit eb15a4e
feat(recall): support direct model reference images in recall API (#9045)
* feat(recall): support model-free reference images in recall API
The recall parameters API previously exposed only `loras`, `control_layers`,
and `ip_adapters`. This meant reference images used by architectures that
feed images directly into the main model — FLUX.2 Klein, FLUX Kontext, and
Qwen Image Edit — could not be sent through the recall endpoint at all:
they have no adapter model to resolve, so they could not ride in the
`ip_adapters` list.
This change adds a new `reference_images` field on RecallParameter that
carries only an `image_name`. The backend validates the file exists in
outputs/images and forwards the resolved metadata (width/height) in the
broadcast event. The frontend's recall handler picks the right config type
(`flux2_reference_image` / `flux_kontext_reference_image` / `ip_adapter`
fallback) via getDefaultRefImageConfig() based on the currently-selected
main model, matching the behavior of a manual drag-and-drop, and dispatches
`refImagesRecalled` with replace:false so these append rather than clobber
any adapters already applied in the same event.
Also consolidates the two existing docs under docs/contributing/RECALL_PARAMETERS/
(RECALL_PARAMETERS_API.md and RECALL_API_LORAS_CONTROLNETS_IMAGES.md) into
a single RECALL_PARAMETERS_API.md that documents the full request schema
including the new field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(recall): cover loras, control layers, and ip_adapters paths
The original recall_parameters router (PR #8758) shipped without any
unit tests for its three collection fields. This commit backfills that
coverage alongside the reference_images tests added in the previous
commit.
The resolver helpers (resolve_model_name_to_key, load_image_file,
process_controlnet_image) are monkey-patched via module-level attribute
replacement so each test can pin down a specific resolution outcome
without spinning up the model manager or an image-files service. Two
small factory helpers (make_name_to_key_stub / make_load_image_file_stub)
make that ergonomic.
New coverage:
* LoRAs — multi-entry resolution + weight/is_enabled pass-through,
silent drop on unresolvable names, is_enabled default of True.
* Control layers — ControlNet resolution precedence, fall-through to
T2I Adapter and Control LoRA in order, missing image gracefully
warned-and-continued, processed_image attached when the processor
returns data, unresolvable entries dropped.
* IP Adapters — IPAdapter-before-FluxRedux lookup order, method /
image_influence pass-through, missing image gracefully warned-and-
continued, unresolvable entries dropped.
* Combined happy path — full request with prompts + model + all four
collection fields, verifying every resolved value reaches the
broadcast payload.
* Main-model drop — an unresolvable main model is scrubbed from the
broadcast so the frontend never receives a stale model name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(frontend): typegen
* chore: fix lint errors and typegen
* fix(test): patch ApiDependencies in auth_dependencies to fix recall tests
The patched_dependencies fixture only monkeypatched ApiDependencies in
the recall_parameters module, but the endpoint also resolves
CurrentUserOrDefault via auth_dependencies, which accesses
ApiDependencies.invoker independently. Patch both import sites.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(frontend): typegen
* feat(recall): add strict mode to clear unset parameters on recall
Add a `strict` query parameter (default false) to POST recall endpoint.
When true, parameters not in the request body are reset: list fields
(loras, control_layers, ip_adapters, reference_images) become [] and
scalar fields become null, so the frontend clears stale state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(frontend): eliminate ref image doubling from race between Promise.all chains
IP adapters and model-free reference images were dispatched via two
independent Promise.all chains — one with replace:true, the other with
replace:false. When a previous recall's promises were still in-flight
they could resolve after the clear and re-append stale entries, doubling
the list. Combine both into a single Promise.all with one replace:true
dispatch so the race is impossible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Alexander Eichhorn <alex@eichhorn.dev>1 parent 0a09452 commit eb15a4e
7 files changed
Lines changed: 18706 additions & 10960 deletions
File tree
- docs/src/content/docs/development/Guides
- invokeai
- app/api/routers
- frontend/web
- src/services
- api
- events
- tests/app/routers
Lines changed: 0 additions & 377 deletions
This file was deleted.
0 commit comments