diff --git a/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx b/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx
index 38c654f7d7c..d5fac328a73 100644
--- a/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx
+++ b/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx
@@ -219,10 +219,13 @@ If no arguments are provided, the script will run in interactive mode.
:::danger[Data Loss]
Deleting a user removes the user record and cascades to their sessions, board shares, sent
-invitations, and per-user client state. It does **not** delete the boards, images, workflows, queue
-items, or style presets they created — those rows remain in the database, owned by a user_id that no
-longer exists, and will not appear in any user's gallery. Physical image files in `outputs/images`
-are also left in place until a gallery maintenance script is run to remove orphan images.
+invitations, and per-user client state. It does **not** delete the boards, images, videos,
+workflows, queue items, or style presets they created — those rows remain in the database, owned by
+a user_id that no longer exists, and will not appear in other (non-admin) users' galleries.
+Administrators keep gallery visibility of the orphaned images and videos and can review or delete
+them from there. Physical media files in `outputs/images` and `outputs/videos` are also left in
+place: orphan image files can be removed with a gallery maintenance script, while video files should
+be deleted through the gallery (which removes the file along with the record) or cleaned up manually.
If you want their content gone as well, reassign or delete it before deleting the user. Back up the
database first if recovery might be needed.
diff --git a/docs/src/content/docs/features/gallery.mdx b/docs/src/content/docs/features/gallery.mdx
index 48dfedcc5f2..c0c4cebda99 100644
--- a/docs/src/content/docs/features/gallery.mdx
+++ b/docs/src/content/docs/features/gallery.mdx
@@ -1,6 +1,6 @@
---
title: Gallery Panel
-description: Learn how to manage, organize, and use your generated images and assets with the Gallery Panel in InvokeAI.
+description: Learn how to manage, organize, and use your generated images, videos, and assets with the Gallery Panel in InvokeAI.
lastUpdated: 2026-02-19
sidebar:
order: 1
@@ -8,7 +8,7 @@ sidebar:
import { Card, CardGrid, Steps } from '@astrojs/starlight/components';
-The Gallery Panel is a fast way to review, find, and make use of images you've generated and loaded. The Gallery is divided into **Boards**. The *Uncategorized* board is always present, but you can create your own for better organization.
+The Gallery Panel is a fast way to review, find, and make use of images and videos you've generated and loaded. The Gallery is divided into **Boards**. The *Uncategorized* board is always present, but you can create your own for better organization. Boards are polymorphic — images and videos coexist on the same board and appear together in the gallery, sorted by creation time.

@@ -51,10 +51,10 @@ Each board has a context menu accessible via right-click (or Ctrl+click).
- **Auto-add to this Board:** If *Auto-Assign Board on Click* is disabled in settings, use this option to quickly set the selected board as the default destination for new images.
- **Download Board:** Packages all images within the board into a `.zip` file. A notification link will be provided when the download is ready.
-- **Delete Board:** Permanently removes the board and all of its contents.
+- **Delete Board:** Permanently removes the board and all of its contents — both images **and** videos.
:::danger
-Deleting a board will **permanently delete all images** contained within it. Proceed with caution!
+Deleting a board will **permanently delete all images and videos** contained within it. Proceed with caution!
:::
### Image Storage Maintenance
@@ -134,6 +134,27 @@ Additionally, each image has a context menu (right-click or Ctrl+click) with pow
---
+## Videos in the Gallery
+
+Videos generated by InvokeAI (currently from the Wan 2.2 model family) appear alongside images in the same gallery view. Each video item displays a first-frame still as its thumbnail with a play badge in the corner; selecting it opens the video in the viewer where you can play it back inline.
+
+### Uploading Videos
+
+You can upload existing videos to a board via the standard drop-or-upload affordance. The upload pipeline accepts **MP4 files only**. Other containers (`.mov`, `.webm`, `.mkv`) are not transcoded on upload and are rejected at the API boundary — re-encode them to MP4 (for example with `ffmpeg -i input.mov -c:v libx264 output.mp4`) before uploading.
+
+### Video Context Menu
+
+Each video has a context menu with the same organization actions as images, plus video-appropriate variants:
+
+- **Open in New Tab / Download:** Opens or saves the raw MP4 file.
+- **Star Video:** Pins the video to the top of the gallery.
+- **Change Board:** Moves the video to a different board. *(Drag-and-drop onto board thumbnails also works.)*
+- **Delete Video:** Permanently deletes the video and its thumbnail.
+
+Videos count toward board contents: a board with two images and three videos shows five items in the polymorphic gallery list and reports both totals in its stats.
+
+---
+
## Summary
This walkthrough covers the Gallery interface and Boards. For guidance on prompting and generation workflows, please refer to the [Prompting Guide](/concepts/prompting-guide/) and [AI Image Generation](/concepts/image-generation/).
diff --git a/docs/src/content/docs/features/video-generation.mdx b/docs/src/content/docs/features/video-generation.mdx
new file mode 100644
index 00000000000..e8011108540
--- /dev/null
+++ b/docs/src/content/docs/features/video-generation.mdx
@@ -0,0 +1,267 @@
+---
+title: Video Generation
+description: Generate short videos with the Wan 2.2 model family — text-to-video, image-to-video, and the trick for stitching longer sequences.
+lastUpdated: 2026-05-13
+---
+
+import { Card, CardGrid, Steps } from '@astrojs/starlight/components';
+
+InvokeAI ships **experimental support for the Wan 2.2 model family**, which lets you generate short MP4 clips from a text prompt, an image, or both. Output ranges from a few-second loop (the model's training distribution) up to longer sequences assembled with the [concat trick](#making-longer-videos) below.
+
+:::caution[Experimental]
+Video generation is a prototype feature. Workflows, node fields, and starter-model packaging may change between releases. The underlying models are also new — expect rough edges in coherence and artifacts at longer durations.
+:::
+
+:::tip[Just want to make a video?]
+Start with the [**Video Workflows** guide](/features/video-workflows/) — a plain-language tour of the eight ready-made workflows that ship with InvokeAI and how to run them. This page is the deeper technical reference behind them.
+:::
+
+---
+
+## Models
+
+Wan 2.2 ships three transformer variants, plus a shared text encoder and two VAEs. All share the same diffusion-style sampling but differ in size, conditioning, and intended task.
+
+### The variants
+
+| Variant | Task | Params | VAE | Conditioning |
+|---|---|---|---|---|
+| **T2V-A14B** | Text → Video | 14B × 2 experts | A14B VAE (16-ch, 8× spatial) | Text only |
+| **I2V-A14B** | Image+Text → Video | 14B × 2 experts | A14B VAE (16-ch, 8× spatial) | Text + reference image (36-channel concat) |
+| **TI2V-5B** | Text → Video OR Image+Text → Video | 5B (single) | Wan 2.2-VAE (48-ch, 16× spatial) | Text, optionally with reference image (first-frame mask blend) |
+
+* **T2V-A14B** generates videos from a text prompt alone. Best motion coherence and prompt-following of the three.
+* **I2V-A14B** locks the first frame to a reference image you supply. Best subject-preservation; the image is concatenated to the noise latents at every step so the model "sees" the reference throughout denoising.
+* **TI2V-5B** is the small single-expert variant. It can do **both** text-to-video and image-to-video with the same checkpoint, at substantially lower VRAM, but with somewhat less stable long-range coherence than the A14B variants.
+
+### High-noise and low-noise transformers (A14B variants only)
+
+The A14B models are a **mixture-of-experts (MoE)** pair. There are actually *two* 14B transformers on disk per variant — a "high-noise" expert and a "low-noise" expert — and the denoise loop swaps between them at a model-defined boundary timestep:
+
+* **High-noise expert** runs early in denoising, when the latents are still mostly noise. It's responsible for composition, layout, and broad motion.
+* **Low-noise expert** runs later, when the latents are close to clean. It refines detail and texture.
+
+InvokeAI handles the swap automatically — both experts have to be installed (the starter bundle handles this), but the workflow only references the "high-noise" model as the main and the "low-noise" model is wired alongside it via the loader node. You don't manage the boundary yourself.
+
+**TI2V-5B is single-expert** — no swap, no boundary, just one model that runs every step. Workflows for TI2V-5B are correspondingly simpler.
+
+### Lightning LoRAs (4-step inference for A14B)
+
+The default A14B variants need ~40–50 denoise steps for clean output. The Wan team also released **Lightning distillation LoRAs** that collapse that to 4 steps with minimal quality loss — about a 10× speedup. There's a pair per variant (one LoRA for the high-noise expert, one for the low-noise), wired through the LoRA loader nodes in the starter workflows.
+
+:::tip
+**TI2V-5B doesn't have a Lightning LoRA.** Its smaller size means each step is cheap; you typically run it at 40–50 steps and end up in a similar wall-clock ballpark as A14B + Lightning.
+:::
+
+### Installing models
+
+The model manager ships two **starter bundles** for video work:
+
+* **Wan 2.2 Text-to-Video** (~36 GB) — UMT5-XXL text encoder, both VAEs, TI2V-5B Q4_K_M, T2V-A14B Q4_K_M (high + low), T2V Lightning (high + low).
+* **Wan 2.2 Image-to-Video** (~32 GB) — UMT5-XXL, A14B VAE, I2V-A14B Q4_K_M (high + low), I2V Lightning (high + low).
+
+The bundles are independent. Installing both ends up at ~56 GB total (shared components — UMT5-XXL and the A14B VAE — are deduplicated on the second install). A 12 GB VRAM card can install only the Text-to-Video bundle and have **TI2V-5B available for both T2V and image-to-video** without ever touching the I2V bundle.
+
+Higher-quality Q8_0 quantizations of every transformer, plus full Diffusers builds of all three variants, are available as a-la-carte installs in the starter models list.
+
+:::tip
+**On a 12 GB VRAM card**: install just the Text-to-Video bundle and use TI2V-5B. The A14B variants will technically run via aggressive offloading but are slow and prone to OOM. TI2V-5B Q4_K_M fits comfortably and is what we recommend for that tier.
+:::
+
+---
+
+## Workflow setup
+
+The shipped starter workflows ("Text to Video - Wan 2.2 Lightning", "Image to Video - Wan 2.2 Lightning") are the easiest starting point — load them from the workflow library, pick your models, set a prompt, and Invoke. The [Video Workflows guide](/features/video-workflows/) walks through all eight and how to choose between them; the sections below describe what's happening inside so you can build your own.
+
+### Constraints that apply to every video workflow
+
+**Frame count**: `num_frames - 1` must be divisible by **4**. This is dictated by the Wan VAE's temporal compression (4 pixel-frames → 1 latent-frame). Valid values: 5, 9, 13, … **81** (the training default, 5 seconds at 16 fps), 85, 89, etc.
+
+**Pixel dimensions**: must be a multiple of **16** for T2V-A14B and I2V-A14B, and a multiple of **32** for TI2V-5B. The constraint comes from the VAE's spatial downsample × the transformer's 2×2 patch size:
+
+| Variant | VAE spatial | Pixel multiple of |
+|---|---|---|
+| T2V-A14B, I2V-A14B | 8× | **16** |
+| TI2V-5B | 16× | **32** |
+
+Reference values that work: 832×480 (480p), 1280×720 (720p, A14B only — TI2V-5B needs 1280×704 instead since 720 isn't divisible by 32).
+
+**Encoder and denoise dimensions must match**: the `Reference Image - Wan 2.2` encoder and the `Denoise Video - Wan 2.2` node both have their own `width` and `height` fields. They have to be identical or the denoise loop will reject the condition tensor.
+
+:::tip[Use Wan 2.2 I2V Ideal Dimensions]
+The **Wan 2.2 I2V Ideal Dimensions** node takes a source image's W×H and a target preset (480p / 720p / 1080p) and outputs valid (width, height) for the encoder + denoise inputs. Wire it once and feed its outputs into both nodes. Saves the manual snap-to-16/snap-to-32 math.
+:::
+
+### Text-to-video workflow
+
+The minimum node chain for T2V:
+
+```
+Wan Main Model Loader ──┐
+ │
+Wan T5 Text Encoder ────┤
+ ▼
+Wan Compel Conditioning (positive)
+ │
+ ▼
+ Denoise Video - Wan 2.2 ──→ Latents to Video - Wan 2.2 ──→ MP4
+ ▲
+Wan Compel Conditioning (negative) ─┘
+```
+
+For **TI2V-5B T2V** this is the entire graph — load the TI2V-5B model and the TI2V-5B VAE, set width/height/num_frames, and run.
+
+For **T2V-A14B** the main model loader also exposes the low-noise expert slot, and you typically add the **Lightning LoRA pair** (one for each expert) to bring step count down to 4. Recommended:
+
+* Steps: **4** (with Lightning) or **40–50** (without)
+* CFG: **5.0** high-noise / **4.0** low-noise (the dedicated `Guidance Scale (Low Noise)` field on the denoise node)
+* Width × Height: 832×480 (faster, default) or 1280×720 (sharper, 4× the memory)
+
+### Image-to-video workflow
+
+I2V adds a **Reference Image** branch alongside the denoise. The reference image gets VAE-encoded into a conditioning tensor that the denoise loop uses to anchor the video's content:
+
+```
+Wan Main Model Loader ──┐
+Wan T5 Text Encoder ────┤
+Wan Compel Conditioning ┤
+ │
+Image Primitive ──→ Reference Image - Wan 2.2 ──┐
+ │ │
+ ▼ ▼
+ Denoise Video - Wan 2.2 ──→ Latents to Video - Wan 2.2 ──→ MP4
+```
+
+For **I2V-A14B**, both the reference encoder and the denoise node need to use the same width/height. The encoder also takes a `num_frames` parameter that must match the denoise's `num_frames` — set both to 81 by default.
+
+For **TI2V-5B image-to-video**, the conditioning math is different (the model uses a first-frame-mask blend rather than channel concatenation), but the workflow shape is the same. The encoder auto-detects TI2V-5B from the VAE's 48 latent channels and emits the right condition tensor.
+
+:::caution[TI2V-5B I2V dimensional constraint]
+TI2V-5B image-to-video requires **width and height divisible by 32** (not just 16). The encoder will refuse the workflow with a clear error if not. 832×480 works; 1280×720 does not (720 is not divisible by 32). Use 1280×704 for 720p-ish on TI2V-5B.
+:::
+
+### First-last-frame interpolation (FLF2V)
+
+The **Reference Image - Wan 2.2** node has an optional **End Image** input. Provide a start image on the regular `image` field and an end image on `End Image (FLF2V)`, and the model interpolates a clip that **begins on the first image and animates to the second** — handy for morphing between two stills or bridging two shots.
+
+```
+Image Primitive (start) ──→ image ────┐
+ ├─→ Reference Image - Wan 2.2 ──→ Denoise Video ──→ MP4
+Image Primitive (end) ──→ End Image ───┘
+```
+
+This is **I2V-A14B only** (`num_frames > 1`); it is not supported on TI2V-5B or single-frame I2V (the node raises a clear error). Wan 2.2 has no dedicated first-last-frame checkpoint — that was a Wan 2.1 model — so the stock I2V-A14B model accepts the end-frame anchor through its existing conditioning channels. Quality is good in practice but not guaranteed for every image pair, so eyeball your results. The shipped **"Interpolate 2 Images to Video - Wan 2.2 Lightning"** workflow wires this up end to end.
+
+### Recommended starting parameters
+
+| | T2V-A14B + Lightning | T2V-A14B | I2V-A14B + Lightning | TI2V-5B (T2V or I2V) |
+|---|---|---|---|---|
+| Steps | 4 | 40–50 | 4 | 40–50 |
+| CFG (high) | 1.0 | 5.0 | 1.0 | 5.0–5.5 |
+| CFG (low) | 1.0 | 4.0 | 1.0 | n/a (single expert) |
+| Num frames | 81 | 81 | 81 | 81 |
+| Width × Height | 832×480 | 832×480 | 832×480 | 832×480 |
+| Scheduler | Auto (FlowMatchEuler) | Auto | Auto | Auto (UniPC) |
+
+---
+
+## Making longer videos
+
+The Wan 2.2 models were trained on **81-frame** clips (5 seconds at 16 fps). Outputs much longer than that suffer rapidly degrading coherence — the temporal positional encoding goes out of distribution and the model loses track of scene content. So instead of asking for `num_frames=200`, the recommended pattern is **chaining**: render a sequence of 81-frame clips where each one's first frame matches the previous clip's last frame, then concatenate them with the `Concatenate Videos` node.
+
+### The basic chain
+
+
+
+1. **Render the first clip** with I2V or T2V, ending on whatever subject/scene you want to continue.
+
+2. **Extract the last frame** of clip 1 using the `Frame from Video` node. Use `frame_index = -1` for the literal last frame, or `-3` / `-5` to step back a few frames (last frames sometimes have boundary artifacts — see the [troubleshooting](#late-frame-artifacts-text-color-blobs) note).
+
+3. **Feed that frame as the reference image** for an I2V run that becomes clip 2. Adjust the prompt for whatever motion you want next.
+
+4. **Repeat** as many times as you want clips.
+
+5. **Concatenate** all the clips into a single MP4 with `Concatenate Videos`. Pick a transition mode based on whether you want a seamless join (`cut` if the bridge frame matches perfectly), a smooth blend (`crossfade`), or a punctuated scene change (`fade_through_black`).
+
+
+
+### Transition modes
+
+The `Concatenate Videos` node offers three:
+
+* **`cut`** — hard splice. Fastest. Total length = sum of inputs. Use this when the bridge frame is genuinely shared (clip 2's first frame = clip 1's last frame) — the seam is invisible.
+* **`crossfade`** — linear A→B dissolve over `transition_frames`. Consumes `transition_frames` from both sides of each boundary. Total length = `sum(inputs) - transition_frames × (n-1)`. Use this when bridge frames don't quite match.
+* **`fade_through_black`** — A fades to black, then B fades in from black. Total length is preserved. Use this for explicit scene changes.
+
+### Quality degradation across iterations
+
+A real failure mode of long chains: each iteration's reference image is itself a *generation output*, so artifacts compound. The model treats codec artifacts and VAE softness in the bridge frame as "style" and reproduces them in the next clip. By the 4th or 5th iteration you can see noticeable softening or color drift.
+
+**Mitigations**:
+
+1. **Pick a bridge frame a few back from the end** (e.g., `frame_index = -3` or `-5`). The very last frame is often the worst frame of a clip due to boundary effects in the temporal attention.
+2. **Refresh the bridge frame with a low-strength img2img pass** before feeding it into the next I2V. An SDXL or FLUX img2img at strength ~0.2 with a quality-focused negative prompt (`blurry, low quality, compression artifacts`) noticeably suppresses the cumulative drift.
+3. **Don't chain more than 4–5 clips** unless you're explicitly doing img2img refinement between each.
+
+---
+
+## Troubleshooting
+
+### OOM errors
+
+Video denoise is memory-intensive — attention scales roughly as `(T_lat × H/16 × W/16)²`, so resolution and frame count both quadratically affect peak VRAM.
+
+* **Drop resolution before frame count.** Going from 1280×720 to 832×480 is a ~2.4× memory drop and visually subtle in most content. Going from 81 frames to 65 only saves ~20%.
+* **TI2V-5B before A14B.** TI2V-5B Q4_K_M peaks around ~6–8 GB at 832×480, versus ~12–14 GB for A14B Q4_K_M. If you're at the OOM edge, switch model family.
+* **OOM at the *reference image encoder* step** is usually allocator fragmentation from a previous run rather than absolute memory pressure. Restart the dev server and try again; if it recurs reproducibly, file an issue.
+
+### Late-frame artifacts (text, color blobs)
+
+If your video looks great for most of its duration but the last ~20% develops Asian text, watermarks, or floating colored shapes, **that's the model's training-data prior leaking through** as temporal coherence weakens at long temporal distance. It's particularly common on TI2V-5B (smaller model, less capacity to hold scene).
+
+**Mitigations**:
+
+* Add to the negative prompt: `text, watermark, logo, subtitles, chinese characters, kanji, ticker, banner`
+* Use a more specific prompt — describe the action you want to *happen* through the clip, not just the static scene
+* Bump CFG to 5.5 (TI2V-5B tolerates this)
+* Stay at `num_frames=81`; values above push temporal RoPE out of distribution and artifacts accelerate
+
+### "Dimensions must be multiples of 16/32"
+
+The encoder and denoise nodes enforce these at runtime. Either:
+
+* Use the **Wan 2.2 I2V Ideal Dimensions** node to compute valid (W, H) automatically from a source image, or
+* Manually round to the right multiple (16 for A14B variants, 32 for TI2V-5B)
+
+### Reference image / denoise dimension mismatch
+
+If the denoise refuses with `Reference-image dimensions … must match denoise dimensions`, both nodes have their own width/height fields and they need to agree. Wire the same values (or the same Ideal Dimensions output) into both.
+
+### TI2V-5B VAE state-dict load error
+
+If `Latents to Video - Wan 2.2` fails with `Error(s) in loading state_dict for AutoencoderKLWan: ... size mismatch for ...`, you have the **wrong VAE installed** for the chosen transformer. TI2V-5B needs the **Wan 2.2 TI2V-5B VAE** (48-channel, Wan 2.2-VAE), not the A14B VAE (16-channel). Both are in the model manager — check the VAE field on the loader and the latents-to-video node.
+
+### Sampler drift on the standalone TI2V-5B GGUF
+
+Standalone GGUF installs don't ship a `scheduler/` config directory. InvokeAI now defaults to `UniPCMultistepScheduler` with the correct Wan-flow params when the model is TI2V-5B and there's no on-disk scheduler — but if you have an older install behaving oddly, the safer alternative is the **full Diffusers TI2V-5B** install (which includes the scheduler config).
+
+### Preview images don't appear
+
+Two known causes:
+
+1. **A video is already loaded in the viewer.** If the last-selected gallery item is a video, the viewer renders the video element. The progress preview overlays on top of it. If you don't see it at all, **hard-refresh the browser** (`Ctrl+Shift+R` / `Cmd+Shift+R`) — Vite's bundle cache occasionally serves a stale build.
+2. **`Show progress in viewer` is disabled.** Check the gallery settings (gear icon at the top of the gallery panel).
+
+### Pipeline runs but the final MP4 is glitchy
+
+This is almost always a **VAE mismatch** or a **scheduler mismatch** — both surface as garbage at the very end of the pipeline. Check that:
+
+* The VAE matches the transformer family (16-ch for A14B, 48-ch for TI2V-5B)
+* You're using the default (auto-selected) scheduler — manually overriding it is currently not supported
+
+---
+
+## Acknowledgements
+
+Wan 2.2 model family by the Alibaba Wan-AI team. Lightning distillation LoRAs by [lightx2v](https://huggingface.co/lightx2v/Wan2.2-Lightning). GGUF quantizations by [QuantStack](https://huggingface.co/QuantStack).
diff --git a/docs/src/content/docs/features/video-workflows.mdx b/docs/src/content/docs/features/video-workflows.mdx
new file mode 100644
index 00000000000..7bea07ab237
--- /dev/null
+++ b/docs/src/content/docs/features/video-workflows.mdx
@@ -0,0 +1,166 @@
+---
+title: Video Workflows
+description: A plain-language guide to the ready-made Wan 2.2 video workflows that ship with InvokeAI — which one to pick, and how to run it.
+lastUpdated: 2026-05-30
+---
+
+import { Card, CardGrid, Steps, LinkButton } from '@astrojs/starlight/components';
+
+InvokeAI ships **ten ready-made video workflows** so you can start generating video without wiring up nodes yourself. Each one is a complete recipe — open it from the **Workflows** library, pick your models, type a prompt (and/or drop in an image), and press **Invoke**.
+
+This page helps you choose the right workflow and run it. For how the models work under the hood, VRAM details, and troubleshooting, see the [Video Generation guide](/features/video-generation/).
+
+:::caution[Experimental]
+Video generation is a prototype feature. These workflows and the models behind them are new — expect rough edges, and check the [Video Generation guide](/features/video-generation/) if something misbehaves.
+:::
+
+---
+
+## Before you start
+
+**1. Install the video models.** Open the **Model Manager** and install a Wan 2.2 starter bundle (Text-to-Video and/or Image-to-Video). The [Video Generation guide explains the bundles and what fits your GPU](/features/video-generation/#installing-models).
+
+**2. You'll select models when you open a workflow.** The shipped workflows come with their model slots **left empty on purpose** — every InvokeAI install stores models a little differently, so a workflow can't point at yours automatically. When you open one, look at its **Notes** panel (the note icon in the workflow editor): it lists exactly which model to pick in each slot. Drop those in once and you're set.
+
+:::tip
+Whenever a workflow won't run because a model field is empty, open its **Notes** — the shopping list of models to select is right there.
+:::
+
+---
+
+## Which workflow should I use?
+
+The workflows come in three families — **Text to Video**, **Image to Video**, and **Extend Video** — each with a high-quality option, a "concept LoRA" variant, and a lighter low-VRAM option — plus an **Interpolate** workflow that bridges two images and an **Extend Video to Image** variant that steers a continuation toward a target frame.
+
+| I want to… | Use | Notes |
+|---|---|---|
+| Make a clip from a text description | **Text to Video - Wan 2.2 Lightning** | Best quality, fast. Start here. |
+| Animate an existing image | **Image to Video - Wan 2.2 Lightning** | Your image becomes the first frame. |
+| Make a video between two images | **Interpolate 2 Images to Video - Wan 2.2 Lightning** | Provide a start and end image; the model interpolates between them. |
+| Make a video longer than ~5 seconds | **Extend Video - Wan 2.2 Lightning** | Continues an existing video and stitches the pieces together. |
+| Extend a video toward a specific end frame | **Extend Video to Image - Wan 2.2 Lightning** | Continues a video and interpolates the new part to a target image. |
+| Add my own style/subject LoRAs | the **w/ Concept LoRAs** variant of any of the above | Same as the base workflow, plus slots for your LoRAs. |
+| Run on a smaller GPU (≈12–16 GB) | a **TI2V-5B (Low Quality)** workflow | Smaller model, lower memory, lower quality. |
+
+If you're not sure, start with **Text to Video - Wan 2.2 Lightning** to get a feel for it, then move to Image-to-Video and Extend.
+
+---
+
+## Bundled video workflows
+
+### Text to Video
+
+
+
+ Type a prompt, get a ~5-second clip. Uses the high-quality A14B model with the Lightning speed-up (just 4 steps). **The recommended starting point.**
+
+
+ The same workflow, plus extra slots for your own concept LoRAs (a style or character you've trained or downloaded). Every LoRA slot must be filled — if you don't have concept LoRAs to add, use the plain version instead.
+
+
+ Uses the smaller TI2V-5B model. Lower quality and slower per result, but fits comfortably on 12–16 GB GPUs. Good for quick drafts or smaller cards.
+
+
+
+### Image to Video
+
+
+
+ Drop in a starting image and a prompt; the model animates outward from your image as the first frame. Uses the A14B model + Lightning. **The recommended image-to-video starting point.**
+
+
+ Same as above, with slots for your own concept LoRAs.
+
+
+ Image-to-video on the smaller TI2V-5B model. Lower quality, lower VRAM — the low-memory option for animating an image.
+
+
+
+### Interpolate Between Two Images
+
+
+
+ Provide a **start image** and an **end image**; the model generates a clip that begins on the first and animates smoothly to the second (first-last-frame interpolation). Great for morphing between two stills or bridging two shots. Uses I2V-A14B + Lightning (4-step).
+
+
+
+:::caution[The "Concept LoRAs" variants need a LoRA in every slot]
+A **concept LoRA** is an add-on that teaches the model a specific style, character, or subject. The *w/ Concept LoRAs* workflows are the same as their plain counterparts but add extra LoRA slots. **Every LoRA slot is required** — if any slot, including the extra concept ones, is left empty, the workflow won't run. Only use these variants when you actually have concept LoRAs to apply; otherwise use the plain version.
+:::
+
+:::note[About the "TI2V-5B (Low Quality)" variants]
+"Low quality" here means *lower than the A14B Lightning workflows*, in exchange for running on much less video memory. On a 12–16 GB GPU they may be your best (or only) option, and they're handy everywhere for fast drafts.
+:::
+
+### Extend Video
+
+The Wan models are trained on short (~5 second) clips, so the way to make something longer is to **continue** a video and stitch the pieces together. These workflows do that for you.
+
+
+
+ Provide an existing video and use the sliders to choose where to continue from. The workflow generates a new clip starting from that point and joins it onto the original with a smooth transition. Run it repeatedly to keep growing the video.
+
+
+ Same as above, with slots for your own concept LoRAs so the continuation keeps your style or subject.
+
+
+ Continue a video **toward a target image**. Provide a starting video and a destination image; the new segment interpolates from the video's last frame to your image (FLF2V), then joins onto the original with a smooth transition. Combines the Extend and Interpolate ideas — useful for steering where a continuation ends up.
+
+
+
+:::tip[Using "Extend Video to Image"]
+This variant takes **two inputs** — the source video and a destination image:
+
+1. Drop your **video** into the video input and use the **start/end frame sliders** to pick the slice to continue from.
+2. Drop a **destination image** into the image input. The generated continuation interpolates from the video's last selected frame to this image, so the extended clip *ends on your image*.
+3. **Invoke.** The new segment is concatenated onto the source with a cross-fade — tune the join (transition type and frame count) on the **Concatenate Videos** node.
+
+Because the endpoint is pinned to your image, it's ideal for bridging a clip to a known next shot or landing on a specific final frame.
+:::
+
+---
+
+## Running a workflow
+
+
+
+1. **Open the Workflows tab** and load one of the workflows from the library.
+
+2. **Read the Notes** (note icon) and select the models it lists — the main model, VAE, and text encoder, plus the Lightning LoRA pair on the A14B workflows.
+
+3. **Set your inputs:**
+ - *Text to Video* — type a **positive prompt** describing the scene and motion.
+ - *Image to Video* — drop a starting **image** into the image field, and optionally add a prompt.
+ - *Extend Video* — provide a **starting video** and use the sliders to pick where the continuation begins.
+
+4. **(Optional) adjust resolution.** The defaults are chosen to balance speed and memory. If you hit an out-of-memory error, lower the resolution preset — see the [VRAM tips](#which-one-fits-my-gpu) below.
+
+5. **Press Invoke.** The finished MP4 appears in the gallery and plays inline in the viewer.
+
+
+
+:::tip
+Leave the **frame count** and **steps** at their defaults to start. They're already tuned for each workflow (for example, the Lightning workflows use only 4 steps, which is what makes them fast).
+:::
+
+---
+
+## Which one fits my GPU?
+
+A rough guide — exact numbers depend on resolution and your system. See [Video Generation → OOM errors](/features/video-generation/#oom-errors) for the full picture.
+
+| Your GPU | Best choice |
+|---|---|
+| 24 GB and up | Any workflow at 720p. |
+| 16–24 GB | A14B Lightning workflows at 480p; TI2V-5B at up to 720p. |
+| 12–16 GB | The **TI2V-5B (Low Quality)** workflows. |
+
+If you get an **out-of-memory** error, lower the resolution first (it helps far more than reducing the frame count), and switch to a TI2V-5B workflow if you're still over budget.
+
+---
+
+## Going further
+
+These workflows are starting points. Once you're comfortable, the [Video Generation guide](/features/video-generation/) covers what's happening inside them — the model variants, how image conditioning works, the trick for chaining clips into longer videos, recommended parameters, and troubleshooting.
+
+Read the Video Generation guide
diff --git a/docs/src/content/docs/start-here/system-requirements.mdx b/docs/src/content/docs/start-here/system-requirements.mdx
index 114698ce158..5eff2bc427a 100644
--- a/docs/src/content/docs/start-here/system-requirements.mdx
+++ b/docs/src/content/docs/start-here/system-requirements.mdx
@@ -2,7 +2,7 @@
title: Hardware Requirements
sidebar:
order: 1
-lastUpdated: 2026-02-18
+lastUpdated: 2026-05-11
---
import { Tabs, TabItem, Steps } from '@astrojs/starlight/components'
@@ -28,6 +28,8 @@ The requirements below are rough guidelines for best performance. GPUs with less
| FLUX.2 Klein 4B | 1024x1024 | Nvidia 30xx+ | 12GB | 16GB | FP8 works with 8GB+; Diffusers + encoder |
| FLUX.2 Klein 9B | 1024x1024 | Nvidia 40xx | 24GB | 32GB | FP8 works with 12GB+; Diffusers + encoder |
| Z-Image Turbo | 1024x1024 | Nvidia 20xx+ | 8GB | 16GB | Q4_K 8GB; Q8/BF16 16GB+ |
+| Wan 2.2 A14B (T2V/I2V) | 1280x720 | Nvidia 30xx+ | 12GB | 32GB | Dual-expert MoE; Q4_K_M 12GB; Q8 18GB+; Diffusers requires 32GB+ |
+| Wan 2.2 TI2V-5B | 1280x720 | Nvidia 20xx+ | 8GB | 16GB | Single transformer; Q4_K_M 6GB+; Q8 8GB+; Diffusers 12GB+ |
:::tip[`tmpfs` on Linux]
If your temporary directory is mounted as a `tmpfs`, ensure it has sufficient space.
diff --git a/invokeai/app/api/auth_dependencies.py b/invokeai/app/api/auth_dependencies.py
index 1df1ed6e250..01a22b771be 100644
--- a/invokeai/app/api/auth_dependencies.py
+++ b/invokeai/app/api/auth_dependencies.py
@@ -2,7 +2,7 @@
from typing import Annotated
-from fastapi import Depends, HTTPException, status
+from fastapi import Cookie, Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from invokeai.app.api.dependencies import ApiDependencies
@@ -13,6 +13,18 @@
# HTTP Bearer token security scheme
security = HTTPBearer(auto_error=False)
+MEDIA_TOKEN_COOKIE = "invokeai_media_token"
+
+
+def _validate_token(token: str, invalid_detail: str) -> TokenData:
+ token_data = verify_token(token)
+ if token_data is None:
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=invalid_detail)
+
+ user = ApiDependencies.invoker.services.users.get(token_data.user_id)
+ if user is None or not user.is_active:
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found or inactive")
+ return token_data
async def get_current_user(
@@ -116,6 +128,19 @@ async def get_current_user_or_default(
return token_data
+async def get_current_media_user_or_default(
+ credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(security)],
+ media_token: Annotated[str | None, Cookie(alias=MEDIA_TOKEN_COOKIE)] = None,
+) -> TokenData:
+ """Authenticate video media requests with a Bearer token or the path-scoped login cookie."""
+ if not ApiDependencies.invoker.services.configuration.multiuser:
+ return TokenData(user_id="system", email="system@system.invokeai", is_admin=True)
+ token = credentials.credentials if credentials is not None else media_token
+ if token is None:
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")
+ return _validate_token(token, "Invalid or expired token")
+
+
async def require_admin(
current_user: Annotated[TokenData, Depends(get_current_user)],
) -> TokenData:
@@ -162,5 +187,6 @@ async def require_admin_or_default(
# Type aliases for convenient use in route dependencies
CurrentUser = Annotated[TokenData, Depends(get_current_user)]
CurrentUserOrDefault = Annotated[TokenData, Depends(get_current_user_or_default)]
+CurrentMediaUserOrDefault = Annotated[TokenData, Depends(get_current_media_user_or_default)]
AdminUser = Annotated[TokenData, Depends(require_admin)]
AdminUserOrDefault = Annotated[TokenData, Depends(require_admin_or_default)]
diff --git a/invokeai/app/api/dependencies.py b/invokeai/app/api/dependencies.py
index 3092f5ab71a..dceda34ea93 100644
--- a/invokeai/app/api/dependencies.py
+++ b/invokeai/app/api/dependencies.py
@@ -10,6 +10,7 @@
from invokeai.app.services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
from invokeai.app.services.board_images.board_images_default import BoardImagesService
from invokeai.app.services.board_records.board_records_sqlite import SqliteBoardRecordStorage
+from invokeai.app.services.board_video_records.board_video_records_sqlite import SqliteBoardVideoRecordStorage
from invokeai.app.services.boards.boards_default import BoardService
from invokeai.app.services.bulk_download.bulk_download_default import BulkDownloadService
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import ClientStatePersistenceSqlite
@@ -24,6 +25,7 @@
SeedreamProvider,
)
from invokeai.app.services.external_generation.startup import sync_configured_external_starter_models
+from invokeai.app.services.gallery.gallery_default import SqliteGalleryService
from invokeai.app.services.image_files.image_files_disk import DiskImageFileStorage
from invokeai.app.services.image_moves.image_moves_default import ImageMoveService
from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
@@ -52,6 +54,9 @@
from invokeai.app.services.style_preset_records.style_preset_records_sqlite import SqliteStylePresetRecordsStorage
from invokeai.app.services.urls.urls_default import LocalUrlService
from invokeai.app.services.users.users_default import UserService
+from invokeai.app.services.video_files.video_files_disk import DiskVideoFileStorage
+from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
+from invokeai.app.services.videos.videos_default import VideoService
from invokeai.app.services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
from invokeai.app.services.workflow_thumbnails.workflow_thumbnails_disk import WorkflowThumbnailFileStorageDisk
from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
@@ -63,6 +68,7 @@
QwenImageConditioningInfo,
SD3ConditioningInfo,
SDXLConditioningInfo,
+ WanConditioningInfo,
ZImageConditioningInfo,
)
from invokeai.backend.util.logging import InvokeAILogger
@@ -108,6 +114,7 @@ def initialize(
raise ValueError("Output folder is not set")
image_files = DiskImageFileStorage(f"{output_folder}/images")
+ video_files = DiskVideoFileStorage(f"{output_folder}/videos")
model_images_folder = config.models_path
style_presets_folder = config.style_presets_path
@@ -133,6 +140,10 @@ def initialize(
image_records = SqliteImageRecordStorage(db=db)
image_moves = ImageMoveService(db=db, image_files=image_files, config=configuration, logger=logger)
images = ImageService()
+ video_records = SqliteVideoRecordStorage(db=db)
+ videos = VideoService()
+ board_video_records = SqliteBoardVideoRecordStorage(db=db)
+ gallery = SqliteGalleryService(db=db)
invocation_cache = MemoryInvocationCache(max_cache_size=config.node_cache_size)
tensors = ObjectSerializerForwardCache(
ObjectSerializerDisk[torch.Tensor](
@@ -154,6 +165,7 @@ def initialize(
ZImageConditioningInfo,
QwenImageConditioningInfo,
AnimaConditioningInfo,
+ WanConditioningInfo,
],
ephemeral=True,
),
@@ -224,6 +236,11 @@ def initialize(
workflow_thumbnails=workflow_thumbnails,
client_state_persistence=client_state_persistence,
users=users,
+ videos=videos,
+ video_files=video_files,
+ video_records=video_records,
+ board_video_records=board_video_records,
+ gallery=gallery,
)
ApiDependencies.invoker = Invoker(services)
diff --git a/invokeai/app/api/routers/auth.py b/invokeai/app/api/routers/auth.py
index e0b0c885cd2..f101d6dc828 100644
--- a/invokeai/app/api/routers/auth.py
+++ b/invokeai/app/api/routers/auth.py
@@ -5,10 +5,10 @@
from datetime import timedelta
from typing import Annotated
-from fastapi import APIRouter, Body, HTTPException, Path, status
+from fastapi import APIRouter, Body, HTTPException, Path, Request, Response, status
from pydantic import BaseModel, Field, field_validator
-from invokeai.app.api.auth_dependencies import AdminUser, CurrentUser
+from invokeai.app.api.auth_dependencies import MEDIA_TOKEN_COOKIE, AdminUser, CurrentUser
from invokeai.app.api.dependencies import ApiDependencies
from invokeai.app.services.auth.token_service import TokenData, create_access_token
from invokeai.app.services.users.users_common import (
@@ -119,7 +119,9 @@ async def get_setup_status() -> SetupStatusResponse:
@auth_router.post("/login", response_model=LoginResponse)
async def login(
- request: Annotated[LoginRequest, Body(description="Login credentials")],
+ login_request: Annotated[LoginRequest, Body(description="Login credentials")],
+ request: Request,
+ response: Response,
) -> LoginResponse:
"""Authenticate user and return access token.
@@ -143,7 +145,7 @@ async def login(
)
user_service = ApiDependencies.invoker.services.users
- user = user_service.authenticate(request.email, request.password)
+ user = user_service.authenticate(login_request.email, login_request.password)
if user is None:
raise HTTPException(
@@ -156,14 +158,25 @@ async def login(
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User account is disabled")
# Create token with appropriate expiration
- expires_delta = timedelta(days=TOKEN_EXPIRATION_REMEMBER_ME if request.remember_me else TOKEN_EXPIRATION_NORMAL)
+ expires_delta = timedelta(
+ days=TOKEN_EXPIRATION_REMEMBER_ME if login_request.remember_me else TOKEN_EXPIRATION_NORMAL
+ )
token_data = TokenData(
user_id=user.user_id,
email=user.email,
is_admin=user.is_admin,
- remember_me=request.remember_me,
+ remember_me=login_request.remember_me,
)
token = create_access_token(token_data, expires_delta)
+ response.set_cookie(
+ MEDIA_TOKEN_COOKIE,
+ token,
+ max_age=int(expires_delta.total_seconds()),
+ httponly=True,
+ secure=request.url.scheme == "https",
+ samesite="lax",
+ path="/api/v1/videos",
+ )
return LoginResponse(
token=token,
@@ -175,6 +188,7 @@ async def login(
@auth_router.post("/logout", response_model=LogoutResponse)
async def logout(
current_user: CurrentUser,
+ response: Response,
) -> LogoutResponse:
"""Logout current user.
@@ -193,6 +207,7 @@ async def logout(
"""
# TODO: Implement token invalidation when server-side session management is added
# For now, this is a no-op since we use stateless JWT tokens
+ response.delete_cookie(MEDIA_TOKEN_COOKIE, path="/api/v1/videos")
return LogoutResponse(success=True)
diff --git a/invokeai/app/api/routers/boards.py b/invokeai/app/api/routers/boards.py
index 067936834ee..c699941a92d 100644
--- a/invokeai/app/api/routers/boards.py
+++ b/invokeai/app/api/routers/boards.py
@@ -22,6 +22,14 @@ class DeleteBoardResult(BaseModel):
description="The image names of the board-images relationships that were deleted."
)
deleted_images: list[str] = Field(description="The names of the images that were deleted.")
+ deleted_board_videos: list[str] = Field(
+ default_factory=list,
+ description="The video names of the board-videos relationships that were deleted.",
+ )
+ deleted_videos: list[str] = Field(
+ default_factory=list,
+ description="The names of the videos that were deleted.",
+ )
@boards_router.post(
@@ -106,7 +114,9 @@ async def update_board(
async def delete_board(
current_user: CurrentUserOrDefault,
board_id: str = Path(description="The id of board to delete"),
- include_images: Optional[bool] = Query(description="Permanently delete all images on the board", default=False),
+ include_images: Optional[bool] = Query(
+ description="Permanently delete all images and videos on the board", default=False
+ ),
) -> DeleteBoardResult:
"""Deletes a board (user must have access to it)"""
try:
@@ -117,6 +127,11 @@ async def delete_board(
if not current_user.is_admin and board.user_id != current_user.user_id:
raise HTTPException(status_code=403, detail="Not authorized to delete this board")
+ # Admins delete everything on the board; regular owners only delete their own
+ # contributions so that contributions from other users to a public/shared board
+ # are preserved (they cascade to "uncategorized" via FK on board_videos / board_images).
+ cascade_user_id: Optional[str] = None if current_user.is_admin else current_user.user_id
+
try:
if include_images is True:
assert_image_move_maintenance_inactive()
@@ -124,13 +139,24 @@ async def delete_board(
board_id=board_id,
categories=None,
is_intermediate=None,
+ user_id=cascade_user_id,
+ )
+ ApiDependencies.invoker.services.images.delete_images_on_board(board_id=board_id, user_id=cascade_user_id)
+ # Use the service-returned list as the authoritative ``deleted_videos``.
+ # delete_videos_on_board now preserves DB records when the underlying file
+ # delete fails (so the API doesn't lie about a file that is still orphaned
+ # on disk), and the preserved records cascade to "uncategorized" via the
+ # board_videos FK when the board itself is deleted below.
+ deleted_videos = ApiDependencies.invoker.services.videos.delete_videos_on_board(
+ board_id=board_id, user_id=cascade_user_id
)
- ApiDependencies.invoker.services.images.delete_images_on_board(board_id=board_id)
ApiDependencies.invoker.services.boards.delete(board_id=board_id)
return DeleteBoardResult(
board_id=board_id,
deleted_board_images=[],
deleted_images=deleted_images,
+ deleted_board_videos=[],
+ deleted_videos=deleted_videos,
)
else:
deleted_board_images = ApiDependencies.invoker.services.board_images.get_all_board_image_names_for_board(
@@ -138,11 +164,20 @@ async def delete_board(
categories=None,
is_intermediate=None,
)
+ deleted_board_videos = (
+ ApiDependencies.invoker.services.board_video_records.get_all_board_video_names_for_board(
+ board_id=board_id,
+ categories=None,
+ is_intermediate=None,
+ )
+ )
ApiDependencies.invoker.services.boards.delete(board_id=board_id)
return DeleteBoardResult(
board_id=board_id,
deleted_board_images=deleted_board_images,
deleted_images=[],
+ deleted_board_videos=deleted_board_videos,
+ deleted_videos=[],
)
except HTTPException:
raise
diff --git a/invokeai/app/api/routers/gallery.py b/invokeai/app/api/routers/gallery.py
new file mode 100644
index 00000000000..4ffa5238802
--- /dev/null
+++ b/invokeai/app/api/routers/gallery.py
@@ -0,0 +1,97 @@
+from typing import Optional
+
+from fastapi import HTTPException, Query
+from fastapi.routing import APIRouter
+
+from invokeai.app.api.auth_dependencies import CurrentUserOrDefault
+from invokeai.app.api.dependencies import ApiDependencies
+from invokeai.app.api.routers.images import _assert_board_read_access
+from invokeai.app.services.gallery.gallery_common import GalleryItem, GalleryItemNamesResult
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+
+gallery_router = APIRouter(prefix="/v1/gallery", tags=["gallery"])
+
+
+@gallery_router.get(
+ "/items/",
+ operation_id="list_gallery_items",
+ response_model=OffsetPaginatedResults[GalleryItem],
+)
+async def list_gallery_items(
+ current_user: CurrentUserOrDefault,
+ origin: Optional[ResourceOrigin] = Query(default=None, description="The origin of items to list."),
+ categories: Optional[list[ImageCategory]] = Query(
+ default=None,
+ description="The categories to include. Shared between images and videos.",
+ ),
+ is_intermediate: Optional[bool] = Query(default=None, description="Whether to list intermediate items."),
+ board_id: Optional[str] = Query(
+ default=None,
+ description="The board id to filter by. Use 'none' to find items without a board.",
+ ),
+ offset: int = Query(default=0, description="The page offset"),
+ limit: int = Query(default=10, description="The number of items per page"),
+ order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
+ starred_first: bool = Query(default=True, description="Whether to sort by starred items first"),
+ search_term: Optional[str] = Query(default=None, description="The term to search for"),
+) -> OffsetPaginatedResults[GalleryItem]:
+ """Returns a paginated, time-sorted stream of polymorphic gallery items (images + videos)."""
+ if board_id is not None and board_id != "none":
+ _assert_board_read_access(board_id, current_user)
+
+ return ApiDependencies.invoker.services.gallery.list_items(
+ offset=offset,
+ limit=limit,
+ starred_first=starred_first,
+ order_dir=order_dir,
+ origin=origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=current_user.user_id,
+ is_admin=current_user.is_admin,
+ )
+
+
+@gallery_router.get(
+ "/items/names",
+ operation_id="get_gallery_item_names",
+ response_model=GalleryItemNamesResult,
+)
+async def get_gallery_item_names(
+ current_user: CurrentUserOrDefault,
+ origin: Optional[ResourceOrigin] = Query(default=None, description="The origin of items to list."),
+ categories: Optional[list[ImageCategory]] = Query(
+ default=None,
+ description="The categories to include. Shared between images and videos.",
+ ),
+ is_intermediate: Optional[bool] = Query(default=None, description="Whether to list intermediate items."),
+ board_id: Optional[str] = Query(
+ default=None,
+ description="The board id to filter by. Use 'none' to find items without a board.",
+ ),
+ order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
+ starred_first: bool = Query(default=True, description="Whether to sort by starred items first"),
+ search_term: Optional[str] = Query(default=None, description="The term to search for"),
+) -> GalleryItemNamesResult:
+ """Returns an ordered (kind, name) list — used to drive virtualized gallery selection."""
+ if board_id is not None and board_id != "none":
+ _assert_board_read_access(board_id, current_user)
+
+ try:
+ return ApiDependencies.invoker.services.gallery.list_item_names(
+ starred_first=starred_first,
+ order_dir=order_dir,
+ origin=origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=current_user.user_id,
+ is_admin=current_user.is_admin,
+ )
+ except Exception:
+ raise HTTPException(status_code=500, detail="Failed to get gallery item names")
diff --git a/invokeai/app/api/routers/images.py b/invokeai/app/api/routers/images.py
index fb756ab8812..ebec1889892 100644
--- a/invokeai/app/api/routers/images.py
+++ b/invokeai/app/api/routers/images.py
@@ -490,6 +490,10 @@ async def delete_images_from_list(
raise
try:
+ # Skip — but do not re-raise — auth failures so a foreign name mid-batch doesn't
+ # discard the response payload for items the caller had already legitimately deleted.
+ # Without this, the client cache never learns about the partial successes and the
+ # already-deleted records reappear in the UI until the next full refresh.
deleted_images: set[str] = set()
affected_boards: set[str] = set()
for image_name in image_names:
@@ -501,7 +505,7 @@ async def delete_images_from_list(
deleted_images.add(image_name)
affected_boards.add(board_id)
except HTTPException:
- raise
+ continue
except Exception:
pass
return DeleteImagesResult(
diff --git a/invokeai/app/api/routers/videos.py b/invokeai/app/api/routers/videos.py
new file mode 100644
index 00000000000..0221322d549
--- /dev/null
+++ b/invokeai/app/api/routers/videos.py
@@ -0,0 +1,736 @@
+import re
+import tempfile
+import traceback
+from pathlib import Path
+from typing import Optional
+
+from fastapi import Body, HTTPException, Query, Request, Response, UploadFile
+from fastapi import Path as PathParam
+from fastapi.responses import FileResponse
+from fastapi.routing import APIRouter
+from pydantic import BaseModel, Field
+
+from invokeai.app.api.auth_dependencies import CurrentMediaUserOrDefault, CurrentUserOrDefault
+from invokeai.app.api.dependencies import ApiDependencies
+from invokeai.app.api.routers.images import _assert_board_read_access
+from invokeai.app.invocations.fields import MetadataField
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.video_records.video_records_common import VideoNamesResult, VideoRecordChanges
+from invokeai.app.services.videos.videos_common import (
+ AddVideosToBoardResult,
+ DeleteVideosResult,
+ RemoveVideosFromBoardResult,
+ StarredVideosResult,
+ UnstarredVideosResult,
+ VideoDTO,
+ VideoUrlsDTO,
+)
+from invokeai.app.util.video_thumbnails import probe_video
+
+videos_router = APIRouter(prefix="/v1/videos", tags=["videos"])
+
+# Videos are immutable; set a high max-age (1 year)
+VIDEO_MAX_AGE = 31536000
+
+# MP4 only — the names service emits `{uuid}.mp4` unconditionally and we don't transcode on
+# upload. Accepting .mov/.webm/.mkv here previously caused those containers to be stored
+# under a .mp4 name and served with the .mp4 MIME type, which silently broke playback in
+# browsers when the container did not match.
+ACCEPTED_VIDEO_MIME_PREFIXES = ("video/mp4",)
+ACCEPTED_VIDEO_EXTENSIONS = (".mp4",)
+
+# Per-chunk size for HTTP Range responses (1 MB)
+RANGE_CHUNK_SIZE = 1024 * 1024
+
+# Upload streaming chunk size (1 MB) and a coarse per-upload size cap. The cap is generous
+# because Wan-generated MP4s for long sequences can run into the hundreds of megabytes;
+# the goal is to prevent a single client from exhausting RAM, not to be a content policy.
+UPLOAD_CHUNK_SIZE = 1024 * 1024
+MAX_UPLOAD_SIZE = 1024 * 1024 * 1024 # 1 GB
+
+
+def _get_video_cache_control() -> str:
+ if ApiDependencies.invoker.services.configuration.multiuser:
+ return "private, no-store"
+ return f"max-age={VIDEO_MAX_AGE}"
+
+
+def _assert_video_owner(video_name: str, current_user: CurrentUserOrDefault) -> None:
+ """Raise 403 if the current user does not own the video and is not an admin."""
+ from invokeai.app.services.board_records.board_records_common import BoardVisibility
+
+ if current_user.is_admin:
+ return
+ owner = ApiDependencies.invoker.services.video_records.get_user_id(video_name)
+ if owner is not None and owner == current_user.user_id:
+ return
+
+ board_id = ApiDependencies.invoker.services.board_video_records.get_board_for_video(video_name)
+ if board_id is not None:
+ try:
+ board = ApiDependencies.invoker.services.boards.get_dto(board_id=board_id)
+ if board.user_id == current_user.user_id:
+ return
+ if board.board_visibility == BoardVisibility.Public:
+ return
+ except Exception:
+ pass
+
+ raise HTTPException(status_code=403, detail="Not authorized to modify this video")
+
+
+def _assert_video_direct_owner(video_name: str, current_user: CurrentUserOrDefault) -> None:
+ """Raise 403 if the current user is not the direct owner of the video.
+
+ Intentionally stricter than _assert_video_owner: board-ownership and public-board
+ fallbacks are NOT honored. Mirrors _assert_image_direct_owner in board_images.py —
+ board-move operations need to verify the *original* owner, otherwise a user could
+ move someone else's video onto their own board via the board-owner branch.
+ """
+ if current_user.is_admin:
+ return
+ owner = ApiDependencies.invoker.services.video_records.get_user_id(video_name)
+ if owner is not None and owner == current_user.user_id:
+ return
+ raise HTTPException(status_code=403, detail="Not authorized to move this video")
+
+
+def _assert_board_write_access(board_id: str, current_user: CurrentUserOrDefault) -> None:
+ """Raise 403 if the current user may not mutate the given board.
+
+ Mirrors _assert_board_write_access in board_images.py: admins and the board owner
+ may write; public boards accept contributions from any user.
+ """
+ from invokeai.app.services.board_records.board_records_common import BoardVisibility
+
+ try:
+ board = ApiDependencies.invoker.services.boards.get_dto(board_id=board_id)
+ except Exception:
+ raise HTTPException(status_code=404, detail="Board not found")
+ if current_user.is_admin:
+ return
+ if board.user_id == current_user.user_id:
+ return
+ if board.board_visibility == BoardVisibility.Public:
+ return
+ raise HTTPException(status_code=403, detail="Not authorized to modify this board")
+
+
+def _assert_video_read_access(video_name: str, current_user: CurrentUserOrDefault) -> None:
+ """Raise 403 if the current user may not view the video."""
+ from invokeai.app.services.board_records.board_records_common import BoardVisibility
+
+ if current_user.is_admin:
+ return
+ owner = ApiDependencies.invoker.services.video_records.get_user_id(video_name)
+ if owner is not None and owner == current_user.user_id:
+ return
+
+ board_id = ApiDependencies.invoker.services.board_video_records.get_board_for_video(video_name)
+ if board_id is not None:
+ try:
+ board = ApiDependencies.invoker.services.boards.get_dto(board_id=board_id)
+ if board.board_visibility in (BoardVisibility.Shared, BoardVisibility.Public):
+ return
+ except Exception:
+ pass
+
+ raise HTTPException(status_code=403, detail="Not authorized to access this video")
+
+
+def _is_accepted_video_upload(file: UploadFile) -> bool:
+ if file.content_type and file.content_type.startswith(ACCEPTED_VIDEO_MIME_PREFIXES):
+ return True
+ if file.filename:
+ return file.filename.lower().endswith(ACCEPTED_VIDEO_EXTENSIONS)
+ return False
+
+
+def _is_mp4_file(path: Path) -> bool:
+ try:
+ with open(path, "rb") as video_file:
+ search_limit = min(path.stat().st_size, 64 * 1024)
+ position = 0
+ while position + 8 <= search_limit:
+ video_file.seek(position)
+ header = video_file.read(8)
+ box_size = int.from_bytes(header[:4], byteorder="big")
+ box_type = header[4:8]
+ header_size = 8
+ if box_size == 1:
+ extended_size = video_file.read(8)
+ if len(extended_size) != 8:
+ return False
+ box_size = int.from_bytes(extended_size, byteorder="big")
+ header_size = 16
+ if box_size < header_size:
+ return False
+ if box_type == b"ftyp":
+ major_brand = video_file.read(4)
+ return len(major_brand) == 4 and major_brand != b"qt "
+ position += box_size
+ except OSError:
+ return False
+ return False
+
+
+@videos_router.post(
+ "/upload",
+ operation_id="upload_video",
+ responses={
+ 201: {"description": "The video was uploaded successfully"},
+ 415: {"description": "Video upload failed"},
+ },
+ status_code=201,
+ response_model=VideoDTO,
+)
+async def upload_video(
+ current_user: CurrentUserOrDefault,
+ file: UploadFile,
+ request: Request,
+ response: Response,
+ video_category: ImageCategory = Query(description="The category of the video"),
+ is_intermediate: bool = Query(description="Whether this is an intermediate video"),
+ board_id: Optional[str] = Query(default=None, description="The board to add this video to, if any"),
+ session_id: Optional[str] = Query(default=None, description="The session ID associated with this upload, if any"),
+ metadata: Optional[str] = Body(
+ default=None,
+ description="The metadata to associate with the video, must be a stringified JSON dict",
+ embed=True,
+ ),
+) -> VideoDTO:
+ """Uploads a video for the current user."""
+ # Check board access for uploads to a specific board.
+ if board_id is not None:
+ from invokeai.app.services.board_records.board_records_common import BoardVisibility
+
+ try:
+ board = ApiDependencies.invoker.services.boards.get_dto(board_id=board_id)
+ except Exception:
+ raise HTTPException(status_code=404, detail="Board not found")
+ if (
+ not current_user.is_admin
+ and board.user_id != current_user.user_id
+ and board.board_visibility != BoardVisibility.Public
+ ):
+ raise HTTPException(status_code=403, detail="Not authorized to upload to this board")
+
+ if not _is_accepted_video_upload(file):
+ raise HTTPException(status_code=415, detail="Not a supported video file")
+
+ # Stream the upload to a tmp file so we can probe and then hand its path to the service.
+ # Reading the full body into memory first risked exhausting RAM on multi-GB uploads;
+ # chunk-stream instead and enforce a hard size cap.
+ tmp = tempfile.NamedTemporaryFile(prefix="invokeai_upload_", suffix=".mp4", delete=False)
+ tmp_path = Path(tmp.name)
+ try:
+ total = 0
+ while chunk := await file.read(UPLOAD_CHUNK_SIZE):
+ total += len(chunk)
+ if total > MAX_UPLOAD_SIZE:
+ tmp.close()
+ raise HTTPException(
+ status_code=413,
+ detail=f"Video upload exceeds maximum size ({MAX_UPLOAD_SIZE} bytes)",
+ )
+ tmp.write(chunk)
+ tmp.close()
+
+ if not _is_mp4_file(tmp_path):
+ raise HTTPException(status_code=415, detail="Not an MP4 video file")
+
+ try:
+ width, height, duration, fps = probe_video(tmp_path)
+ except Exception:
+ ApiDependencies.invoker.services.logger.error(traceback.format_exc())
+ raise HTTPException(status_code=415, detail="Failed to read video")
+
+ try:
+ video_dto = ApiDependencies.invoker.services.videos.create(
+ source_path=tmp_path,
+ width=width,
+ height=height,
+ duration=duration,
+ fps=fps,
+ video_origin=ResourceOrigin.EXTERNAL,
+ video_category=video_category,
+ session_id=session_id,
+ board_id=board_id,
+ metadata=metadata,
+ workflow=None,
+ graph=None,
+ is_intermediate=is_intermediate,
+ user_id=current_user.user_id,
+ )
+
+ response.status_code = 201
+ response.headers["Location"] = video_dto.video_url
+ return video_dto
+ except Exception:
+ ApiDependencies.invoker.services.logger.error(traceback.format_exc())
+ raise HTTPException(status_code=500, detail="Failed to create video")
+ finally:
+ # If create() succeeded the file was moved; this unlink is a no-op then.
+ try:
+ tmp_path.unlink(missing_ok=True)
+ except Exception:
+ pass
+
+
+@videos_router.delete("/i/{video_name}", operation_id="delete_video", response_model=DeleteVideosResult)
+async def delete_video(
+ current_user: CurrentUserOrDefault,
+ video_name: str = PathParam(description="The name of the video to delete"),
+) -> DeleteVideosResult:
+ _assert_video_owner(video_name, current_user)
+
+ # Let service-level failures surface as 500s rather than swallowing them and returning a
+ # success-shaped response. A previous version of this handler caught everything and
+ # returned an empty ``deleted_videos`` list with HTTP 200; the frontend treated that as
+ # success, dropped the item from its cache, and the video stayed on disk — a silent
+ # data-consistency failure that only became visible on the next page reload.
+ try:
+ video_dto = ApiDependencies.invoker.services.videos.get_dto(video_name)
+ except Exception:
+ raise HTTPException(status_code=404, detail="Video not found")
+
+ board_id = video_dto.board_id or "none"
+ try:
+ ApiDependencies.invoker.services.videos.delete(video_name)
+ except Exception:
+ raise HTTPException(status_code=500, detail="Failed to delete video")
+
+ return DeleteVideosResult(
+ deleted_videos=[video_name],
+ affected_boards=[board_id],
+ )
+
+
+@videos_router.post("/delete", operation_id="delete_videos_from_list", response_model=DeleteVideosResult)
+async def delete_videos_from_list(
+ current_user: CurrentUserOrDefault,
+ video_names: list[str] = Body(description="The list of names of videos to delete", embed=True),
+) -> DeleteVideosResult:
+ # Skip — but do not re-raise — auth failures so a foreign name mid-batch doesn't
+ # discard the response payload for items the caller had already legitimately deleted.
+ # Without this, the client cache never learns about the partial successes and the
+ # already-deleted records reappear in the UI until the next full refresh.
+ deleted_videos: set[str] = set()
+ affected_boards: set[str] = set()
+ for video_name in video_names:
+ try:
+ _assert_video_owner(video_name, current_user)
+ video_dto = ApiDependencies.invoker.services.videos.get_dto(video_name)
+ board_id = video_dto.board_id or "none"
+ ApiDependencies.invoker.services.videos.delete(video_name)
+ deleted_videos.add(video_name)
+ affected_boards.add(board_id)
+ except HTTPException:
+ continue
+ except Exception:
+ pass
+ return DeleteVideosResult(
+ deleted_videos=list(deleted_videos),
+ affected_boards=list(affected_boards),
+ )
+
+
+@videos_router.patch("/i/{video_name}", operation_id="update_video", response_model=VideoDTO)
+async def update_video(
+ current_user: CurrentUserOrDefault,
+ video_name: str = PathParam(description="The name of the video to update"),
+ video_changes: VideoRecordChanges = Body(description="The changes to apply to the video"),
+) -> VideoDTO:
+ _assert_video_owner(video_name, current_user)
+ try:
+ return ApiDependencies.invoker.services.videos.update(video_name, video_changes)
+ except Exception:
+ raise HTTPException(status_code=400, detail="Failed to update video")
+
+
+@videos_router.get("/i/{video_name}", operation_id="get_video_dto", response_model=VideoDTO)
+async def get_video_dto(
+ current_user: CurrentUserOrDefault,
+ video_name: str = PathParam(description="The name of video to get"),
+) -> VideoDTO:
+ _assert_video_read_access(video_name, current_user)
+ try:
+ return ApiDependencies.invoker.services.videos.get_dto(video_name)
+ except Exception:
+ raise HTTPException(status_code=404)
+
+
+@videos_router.get(
+ "/i/{video_name}/metadata", operation_id="get_video_metadata", response_model=Optional[MetadataField]
+)
+async def get_video_metadata(
+ current_user: CurrentUserOrDefault,
+ video_name: str = PathParam(description="The name of video to get"),
+) -> Optional[MetadataField]:
+ _assert_video_read_access(video_name, current_user)
+ try:
+ return ApiDependencies.invoker.services.videos.get_metadata(video_name)
+ except Exception:
+ raise HTTPException(status_code=404)
+
+
+def _parse_range_header(range_header: str, file_size: int) -> Optional[tuple[int, int]]:
+ """Parses an HTTP Range header of the form `bytes=START-END`. Returns inclusive (start, end)
+ byte offsets, or None if the header is malformed or unsatisfiable."""
+ match = re.match(r"^bytes=(\d*)-(\d*)$", range_header.strip())
+ if match is None:
+ return None
+ start_str, end_str = match.group(1), match.group(2)
+ if start_str == "" and end_str == "":
+ return None
+ if start_str == "":
+ # suffix range: last N bytes
+ try:
+ suffix_len = int(end_str)
+ except ValueError:
+ return None
+ if suffix_len == 0:
+ return None
+ start = max(file_size - suffix_len, 0)
+ end = file_size - 1
+ else:
+ try:
+ start = int(start_str)
+ except ValueError:
+ return None
+ if end_str == "":
+ end = file_size - 1
+ else:
+ try:
+ end = int(end_str)
+ except ValueError:
+ return None
+ if start > end or start >= file_size:
+ return None
+ end = min(end, file_size - 1)
+ return start, end
+
+
+@videos_router.get(
+ "/i/{video_name}/full",
+ operation_id="get_video_full",
+ response_class=Response,
+ responses={
+ 200: {"description": "Return the full video file", "content": {"video/mp4": {}}},
+ 206: {"description": "Return a byte-range of the video file", "content": {"video/mp4": {}}},
+ 404: {"description": "Video not found"},
+ },
+)
+@videos_router.head(
+ "/i/{video_name}/full",
+ operation_id="get_video_full_head",
+ response_class=Response,
+ responses={
+ 200: {"description": "Return the full video file", "content": {"video/mp4": {}}},
+ 404: {"description": "Video not found"},
+ },
+)
+async def get_video_full(
+ request: Request,
+ current_user: CurrentMediaUserOrDefault,
+ video_name: str = PathParam(description="The name of video file to get"),
+) -> Response:
+ """Serves the video file with HTTP Range support so HTML5 seek/scrub works.
+
+ Browser media requests authenticate with the path-scoped HttpOnly cookie set at login.
+ """
+ _assert_video_read_access(video_name, current_user)
+ try:
+ path_str = ApiDependencies.invoker.services.videos.get_path(video_name, thumbnail=False)
+ except Exception:
+ raise HTTPException(status_code=404)
+
+ path = Path(path_str)
+ if not path.exists():
+ raise HTTPException(status_code=404)
+
+ file_size = path.stat().st_size
+ range_header = request.headers.get("range") or request.headers.get("Range")
+
+ common_headers = {
+ "Accept-Ranges": "bytes",
+ "Cache-Control": _get_video_cache_control(),
+ "Content-Disposition": f'inline; filename="{video_name}"',
+ }
+
+ # HEAD: respond with metadata only.
+ if request.method == "HEAD":
+ return Response(
+ status_code=200,
+ media_type="video/mp4",
+ headers={**common_headers, "Content-Length": str(file_size)},
+ )
+
+ if range_header is None:
+ # Stream the file via sendfile() rather than reading it into RAM — multi-GB
+ # MP4 downloads (clients without Range, CLI tools, CDN edge fetches) would
+ # otherwise allocate a multi-GB Python bytes object per request.
+ return FileResponse(
+ path,
+ media_type="video/mp4",
+ headers=common_headers,
+ )
+
+ parsed = _parse_range_header(range_header, file_size)
+ if parsed is None:
+ # Unsatisfiable range.
+ return Response(
+ status_code=416,
+ headers={**common_headers, "Content-Range": f"bytes */{file_size}"},
+ )
+ start, end = parsed
+ length = end - start + 1
+ with open(path, "rb") as f:
+ f.seek(start)
+ # Read at most one chunk; clients ask for more via subsequent ranges.
+ read_length = min(length, RANGE_CHUNK_SIZE)
+ chunk = f.read(read_length)
+ actual_end = start + len(chunk) - 1
+ return Response(
+ chunk,
+ status_code=206,
+ media_type="video/mp4",
+ headers={
+ **common_headers,
+ "Content-Range": f"bytes {start}-{actual_end}/{file_size}",
+ "Content-Length": str(len(chunk)),
+ },
+ )
+
+
+@videos_router.get(
+ "/i/{video_name}/thumbnail",
+ operation_id="get_video_thumbnail",
+ response_class=Response,
+ responses={
+ 200: {"description": "Return the video thumbnail", "content": {"image/webp": {}}},
+ 404: {"description": "Video not found"},
+ },
+)
+async def get_video_thumbnail(
+ current_user: CurrentMediaUserOrDefault,
+ video_name: str = PathParam(description="The name of thumbnail file to get"),
+) -> Response:
+ """Returns the first-frame WebP thumbnail of an authorized video."""
+ _assert_video_read_access(video_name, current_user)
+ try:
+ path = ApiDependencies.invoker.services.videos.get_path(video_name, thumbnail=True)
+ except Exception:
+ raise HTTPException(status_code=404)
+ # FileResponse stats the file lazily *after* the route returns, which means a missing
+ # thumbnail surfaces as a server-side error rather than the documented 404. Check up
+ # front so callers get the right status. Video saves are allowed without a thumbnail
+ # (see video_files_disk.save), so this is a reachable path.
+ if not Path(path).is_file():
+ raise HTTPException(status_code=404)
+ return FileResponse(
+ path,
+ media_type="image/webp",
+ headers={"Cache-Control": _get_video_cache_control()},
+ )
+
+
+@videos_router.get("/i/{video_name}/urls", operation_id="get_video_urls", response_model=VideoUrlsDTO)
+async def get_video_urls(
+ current_user: CurrentUserOrDefault,
+ video_name: str = PathParam(description="The name of the video whose URL to get"),
+) -> VideoUrlsDTO:
+ _assert_video_read_access(video_name, current_user)
+ try:
+ video_url = ApiDependencies.invoker.services.videos.get_url(video_name)
+ thumbnail_url = ApiDependencies.invoker.services.videos.get_url(video_name, thumbnail=True)
+ return VideoUrlsDTO(video_name=video_name, video_url=video_url, thumbnail_url=thumbnail_url)
+ except Exception:
+ raise HTTPException(status_code=404)
+
+
+@videos_router.get("/", operation_id="list_video_dtos", response_model=OffsetPaginatedResults[VideoDTO])
+async def list_video_dtos(
+ current_user: CurrentUserOrDefault,
+ video_origin: Optional[ResourceOrigin] = Query(default=None, description="The origin of videos to list."),
+ categories: Optional[list[ImageCategory]] = Query(default=None, description="The categories of video to include."),
+ is_intermediate: Optional[bool] = Query(default=None, description="Whether to list intermediate videos."),
+ board_id: Optional[str] = Query(
+ default=None,
+ description="The board id to filter by. Use 'none' to find videos without a board.",
+ ),
+ offset: int = Query(default=0, description="The page offset"),
+ limit: int = Query(default=10, description="The number of videos per page"),
+ order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
+ starred_first: bool = Query(default=True, description="Whether to sort by starred videos first"),
+ search_term: Optional[str] = Query(default=None, description="The term to search for"),
+) -> OffsetPaginatedResults[VideoDTO]:
+ """Gets a list of video DTOs for the current user."""
+ # Validate that the caller can read from this board. "none" is handled by the SQL layer.
+ if board_id is not None and board_id != "none":
+ _assert_board_read_access(board_id, current_user)
+
+ return ApiDependencies.invoker.services.videos.get_many(
+ offset,
+ limit,
+ starred_first,
+ order_dir,
+ video_origin,
+ categories,
+ is_intermediate,
+ board_id,
+ search_term,
+ current_user.user_id,
+ current_user.is_admin,
+ )
+
+
+@videos_router.get("/names", operation_id="get_video_names")
+async def get_video_names(
+ current_user: CurrentUserOrDefault,
+ video_origin: Optional[ResourceOrigin] = Query(default=None, description="The origin of videos to list."),
+ categories: Optional[list[ImageCategory]] = Query(default=None, description="The categories of video to include."),
+ is_intermediate: Optional[bool] = Query(default=None, description="Whether to list intermediate videos."),
+ board_id: Optional[str] = Query(
+ default=None,
+ description="The board id to filter by. Use 'none' to find videos without a board.",
+ ),
+ order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
+ starred_first: bool = Query(default=True, description="Whether to sort by starred videos first"),
+ search_term: Optional[str] = Query(default=None, description="The term to search for"),
+) -> VideoNamesResult:
+ """Gets ordered list of video names with metadata for optimistic updates."""
+ # Validate that the caller can read from this board. "none" is handled by the SQL layer.
+ if board_id is not None and board_id != "none":
+ _assert_board_read_access(board_id, current_user)
+
+ try:
+ return ApiDependencies.invoker.services.videos.get_video_names(
+ starred_first=starred_first,
+ order_dir=order_dir,
+ video_origin=video_origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=current_user.user_id,
+ is_admin=current_user.is_admin,
+ )
+ except Exception:
+ raise HTTPException(status_code=500, detail="Failed to get video names")
+
+
+@videos_router.post("/star", operation_id="star_videos_in_list", response_model=StarredVideosResult)
+async def star_videos_in_list(
+ current_user: CurrentUserOrDefault,
+ video_names: list[str] = Body(description="The list of names of videos to star", embed=True),
+) -> StarredVideosResult:
+ # Skip — but do not re-raise — auth failures so a foreign name mid-batch doesn't
+ # discard the response payload for items that were already starred. Mirrors
+ # delete_videos_from_list: re-raising turned partial successes into an error-shaped
+ # response, so the client never invalidated caches for the videos that did change.
+ starred_videos: set[str] = set()
+ affected_boards: set[str] = set()
+ for video_name in video_names:
+ try:
+ _assert_video_owner(video_name, current_user)
+ updated = ApiDependencies.invoker.services.videos.update(
+ video_name, changes=VideoRecordChanges(starred=True)
+ )
+ starred_videos.add(video_name)
+ affected_boards.add(updated.board_id or "none")
+ except HTTPException:
+ continue
+ except Exception:
+ pass
+ return StarredVideosResult(starred_videos=list(starred_videos), affected_boards=list(affected_boards))
+
+
+@videos_router.post("/unstar", operation_id="unstar_videos_in_list", response_model=UnstarredVideosResult)
+async def unstar_videos_in_list(
+ current_user: CurrentUserOrDefault,
+ video_names: list[str] = Body(description="The list of names of videos to unstar", embed=True),
+) -> UnstarredVideosResult:
+ # See star_videos_in_list: skip foreign names instead of re-raising mid-batch.
+ unstarred_videos: set[str] = set()
+ affected_boards: set[str] = set()
+ for video_name in video_names:
+ try:
+ _assert_video_owner(video_name, current_user)
+ updated = ApiDependencies.invoker.services.videos.update(
+ video_name, changes=VideoRecordChanges(starred=False)
+ )
+ unstarred_videos.add(video_name)
+ affected_boards.add(updated.board_id or "none")
+ except HTTPException:
+ continue
+ except Exception:
+ pass
+ return UnstarredVideosResult(unstarred_videos=list(unstarred_videos), affected_boards=list(affected_boards))
+
+
+class VideoBoardArg(BaseModel):
+ board_id: str = Field(description="The id of the board to add or remove the video from")
+ video_name: str = Field(description="The name of the video to add to / remove from the board")
+
+
+@videos_router.post(
+ "/board",
+ operation_id="add_video_to_board",
+ response_model=AddVideosToBoardResult,
+)
+async def add_video_to_board(
+ current_user: CurrentUserOrDefault,
+ arg: VideoBoardArg = Body(),
+) -> AddVideosToBoardResult:
+ _assert_board_write_access(arg.board_id, current_user)
+ _assert_video_direct_owner(arg.video_name, current_user)
+ try:
+ # Capture the source board BEFORE mutating so the frontend can invalidate both
+ # the old and new board caches. Mirrors add_image_to_board.
+ old_board_id = (
+ ApiDependencies.invoker.services.board_video_records.get_board_for_video(arg.video_name) or "none"
+ )
+ ApiDependencies.invoker.services.board_video_records.add_video_to_board(
+ board_id=arg.board_id, video_name=arg.video_name
+ )
+ return AddVideosToBoardResult(
+ added_videos=[arg.video_name],
+ affected_boards=list({arg.board_id, old_board_id}),
+ )
+ except Exception:
+ raise HTTPException(status_code=500, detail="Failed to add video to board")
+
+
+@videos_router.delete(
+ "/board",
+ operation_id="remove_video_from_board",
+ response_model=RemoveVideosFromBoardResult,
+)
+async def remove_video_from_board(
+ current_user: CurrentUserOrDefault,
+ video_name: str = Body(description="The name of the video to remove from its board", embed=True),
+) -> RemoveVideosFromBoardResult:
+ # A video association can be removed by EITHER the direct video owner OR a user with
+ # write access to the destination board (admin, board owner, or any contributor when the
+ # board is Public). This mirrors remove_image_from_board and prevents a video from being
+ # stranded when a non-owner uploads into a Public board that is later made Shared/Private:
+ # without the board-write fallback, neither the uploader nor the board owner could
+ # detach the video. See PR #9163 review.
+ old_board_id = ApiDependencies.invoker.services.board_video_records.get_board_for_video(video_name)
+ try:
+ _assert_video_direct_owner(video_name, current_user)
+ except HTTPException:
+ if old_board_id is None:
+ raise
+ _assert_board_write_access(old_board_id, current_user)
+ try:
+ ApiDependencies.invoker.services.board_video_records.remove_video_from_board(video_name=video_name)
+ return RemoveVideosFromBoardResult(
+ removed_videos=[video_name],
+ affected_boards=list({old_board_id or "none", "none"}),
+ )
+ except Exception:
+ raise HTTPException(status_code=500, detail="Failed to remove video from board")
diff --git a/invokeai/app/api/routers/virtual_boards.py b/invokeai/app/api/routers/virtual_boards.py
index f0c9e2edc51..78902dd5dec 100644
--- a/invokeai/app/api/routers/virtual_boards.py
+++ b/invokeai/app/api/routers/virtual_boards.py
@@ -3,6 +3,7 @@
from invokeai.app.api.auth_dependencies import CurrentUserOrDefault
from invokeai.app.api.dependencies import ApiDependencies
+from invokeai.app.services.gallery.gallery_common import GalleryItemNamesResult
from invokeai.app.services.image_records.image_records_common import ImageCategory, ImageNamesResult
from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
from invokeai.app.services.virtual_boards.virtual_boards_common import VirtualSubBoardDTO
@@ -18,9 +19,9 @@
async def list_virtual_boards_by_date(
current_user: CurrentUserOrDefault,
) -> list[VirtualSubBoardDTO]:
- """Gets a list of virtual sub-boards grouped by date."""
+ """Gets a list of virtual sub-boards grouped by date. Covers both images and videos."""
try:
- return ApiDependencies.invoker.services.image_records.get_image_dates(
+ return ApiDependencies.invoker.services.gallery.get_dates(
user_id=current_user.user_id,
is_admin=current_user.is_admin,
)
@@ -41,7 +42,8 @@ async def list_virtual_board_image_names_by_date(
categories: list[ImageCategory] | None = Query(default=None, description="The categories of images to include"),
search_term: str | None = Query(default=None, description="Search term to filter images"),
) -> ImageNamesResult:
- """Gets ordered image names for a specific date."""
+ """Gets ordered image names for a specific date. Image-only; kept for API compatibility —
+ the UI uses the polymorphic `/by_date/{date}/item_names` endpoint."""
try:
return ApiDependencies.invoker.services.image_records.get_image_names_by_date(
date=date,
@@ -54,3 +56,32 @@ async def list_virtual_board_image_names_by_date(
)
except Exception:
raise HTTPException(status_code=500, detail="Failed to get image names for date")
+
+
+@virtual_boards_router.get(
+ "/by_date/{date}/item_names",
+ operation_id="list_virtual_board_item_names_by_date",
+ response_model=GalleryItemNamesResult,
+)
+async def list_virtual_board_item_names_by_date(
+ current_user: CurrentUserOrDefault,
+ date: str = Path(description="The ISO date string, e.g. '2026-03-18'"),
+ starred_first: bool = Query(default=True, description="Whether to sort starred items first"),
+ order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The sort direction"),
+ categories: list[ImageCategory] | None = Query(default=None, description="The categories of items to include"),
+ search_term: str | None = Query(default=None, description="Search term to filter items"),
+) -> GalleryItemNamesResult:
+ """Gets ordered polymorphic (image + video) item refs for a specific date."""
+ try:
+ return ApiDependencies.invoker.services.gallery.list_item_names(
+ starred_first=starred_first,
+ order_dir=order_dir,
+ categories=categories,
+ is_intermediate=False,
+ search_term=search_term,
+ user_id=current_user.user_id,
+ is_admin=current_user.is_admin,
+ created_date=date,
+ )
+ except Exception:
+ raise HTTPException(status_code=500, detail="Failed to get gallery item names for date")
diff --git a/invokeai/app/api_app.py b/invokeai/app/api_app.py
index ed02d75eae3..256f0386cb6 100644
--- a/invokeai/app/api_app.py
+++ b/invokeai/app/api_app.py
@@ -23,6 +23,7 @@
client_state,
custom_nodes,
download_queue,
+ gallery,
image_moves,
images,
model_manager,
@@ -31,6 +32,7 @@
session_queue,
style_presets,
utilities,
+ videos,
virtual_boards,
workflows,
)
@@ -179,6 +181,8 @@ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint):
app.include_router(download_queue.download_queue_router, prefix="/api")
app.include_router(image_moves.image_moves_router, prefix="/api")
app.include_router(images.images_router, prefix="/api")
+app.include_router(videos.videos_router, prefix="/api")
+app.include_router(gallery.gallery_router, prefix="/api")
app.include_router(boards.boards_router, prefix="/api")
app.include_router(board_images.board_images_router, prefix="/api")
app.include_router(virtual_boards.virtual_boards_router, prefix="/api")
diff --git a/invokeai/app/invocations/fields.py b/invokeai/app/invocations/fields.py
index 4418c86371a..654282868a9 100644
--- a/invokeai/app/invocations/fields.py
+++ b/invokeai/app/invocations/fields.py
@@ -141,6 +141,7 @@ class UIComponent(str, Enum, metaclass=MetaEnum):
None_ = "none"
Textarea = "textarea"
Slider = "slider"
+ VideoFrameIndex = "video-frame-index"
class FieldDescriptions:
@@ -174,6 +175,9 @@ class FieldDescriptions:
z_image_model = "Z-Image model (Transformer) to load"
qwen_image_model = "Qwen Image Edit model (Transformer) to load"
qwen_vl_encoder = "Qwen2.5-VL tokenizer, processor and text/vision encoder"
+ wan_model = "Wan 2.2 model (Transformer) to load"
+ wan_t5_encoder = "UMT5-XXL tokenizer and text encoder for Wan 2.2"
+ wan_ref_image = "Reference-image (VAE-latent) conditioning for Wan 2.2 I2V."
sdxl_main_model = "SDXL Main model (UNet, VAE, CLIP1, CLIP2) to load"
sdxl_refiner_model = "SDXL Refiner Main Modde (UNet, VAE, CLIP2) to load"
onnx_main_model = "ONNX Main model (UNet, VAE, CLIP) to load"
@@ -241,6 +245,12 @@ class ImageField(BaseModel):
image_name: str = Field(description="The name of the image")
+class VideoField(BaseModel):
+ """A video primitive field"""
+
+ video_name: str = Field(description="The name of the video")
+
+
class BoardField(BaseModel):
"""A board primitive field"""
@@ -365,6 +375,39 @@ class AnimaConditioningField(BaseModel):
)
+class WanConditioningField(BaseModel):
+ """A Wan 2.2 conditioning tensor primitive value.
+
+ Wan conditioning is the UMT5-XXL hidden state for the prompt plus an attention
+ mask marking valid (non-padding) tokens.
+ """
+
+ conditioning_name: str = Field(description="The name of conditioning tensor")
+
+
+class WanRefImageConditioningField(BaseModel):
+ """Reference-image conditioning for Wan 2.2 I2V.
+
+ Carries the 20-channel VAE-latent condition tensor (4-channel first-frame
+ mask + 16-channel ref-image latents). The denoise loop concatenates this
+ to the 16-channel noise latents along the channel dim each step, producing
+ the 36-channel input the I2V-A14B transformer expects.
+
+ Also carries the spatial dims and frame count used to encode the image so
+ the denoise node can sanity-check the user's width/height/num_frames — a
+ latent temporal-dim mismatch is hard to debug from the downstream error.
+ """
+
+ condition_tensor_name: str = Field(description="Name of the saved [1, 20, T_lat, H/8, W/8] condition tensor.")
+ width: int = Field(description="Image width used during VAE encoding (matches denoise width).")
+ height: int = Field(description="Image height used during VAE encoding (matches denoise height).")
+ num_frames: int = Field(
+ default=1,
+ description="Pixel-frame count the condition was built for. 1 for single-frame I2V "
+ "(image output), 81+ for video.",
+ )
+
+
class ConditioningField(BaseModel):
"""A conditioning tensor primitive value"""
diff --git a/invokeai/app/invocations/metadata.py b/invokeai/app/invocations/metadata.py
index da24d8802bb..c5acc6757d9 100644
--- a/invokeai/app/invocations/metadata.py
+++ b/invokeai/app/invocations/metadata.py
@@ -174,6 +174,11 @@ def invoke(self, context: InvocationContext) -> MetadataOutput:
"anima_img2img",
"anima_inpaint",
"anima_outpaint",
+ "wan_txt2img",
+ "wan_img2img",
+ "wan_inpaint",
+ "wan_outpaint",
+ "wan_i2v",
]
diff --git a/invokeai/app/invocations/model.py b/invokeai/app/invocations/model.py
index c82b4369bd8..cd40cf7bcdc 100644
--- a/invokeai/app/invocations/model.py
+++ b/invokeai/app/invocations/model.py
@@ -87,6 +87,14 @@ class Qwen3EncoderField(BaseModel):
loras: List[LoRAField] = Field(default_factory=list, description="LoRAs to apply on model loading")
+class WanT5EncoderField(BaseModel):
+ """Field for the UMT5-XXL text encoder used by Wan 2.2 models."""
+
+ tokenizer: ModelIdentifierField = Field(description="Info to load tokenizer submodel")
+ text_encoder: ModelIdentifierField = Field(description="Info to load text_encoder submodel")
+ loras: List[LoRAField] = Field(default_factory=list, description="LoRAs to apply on model loading")
+
+
class VAEField(BaseModel):
vae: ModelIdentifierField = Field(description="Info to load vae submodel")
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
@@ -101,6 +109,46 @@ class TransformerField(BaseModel):
loras: List[LoRAField] = Field(description="LoRAs to apply on model loading")
+class WanTransformerField(BaseModel):
+ """Transformer field for Wan 2.2 models.
+
+ Wan 2.2 A14B is a Mixture-of-Experts model with two transformer experts:
+ a high-noise expert (active at large timesteps) and a low-noise expert
+ (active at small timesteps). TI2V-5B is a single-transformer model and only
+ populates ``transformer``.
+
+ ``boundary_ratio`` matches Diffusers' ``WanPipeline`` semantics: it's the
+ boundary timestep as a fraction of ``num_train_timesteps`` (typically 1000),
+ so ``boundary_ratio=0.875`` means the high-noise expert handles t >= 875 and
+ the low-noise expert handles t < 875.
+ """
+
+ transformer: ModelIdentifierField = Field(
+ description="Primary transformer submodel. For A14B this is the high-noise expert."
+ )
+ transformer_low_noise: ModelIdentifierField | None = Field(
+ default=None,
+ description="Low-noise transformer expert (Wan 2.2 A14B only). None for TI2V-5B.",
+ )
+ loras: List[LoRAField] = Field(
+ default_factory=list,
+ description="LoRAs to apply to the primary transformer. For A14B applied to the high-noise expert.",
+ )
+ loras_low_noise: List[LoRAField] = Field(
+ default_factory=list,
+ description="Optional separate LoRAs for the low-noise expert (Wan 2.2 A14B). "
+ "If empty and transformer_low_noise is set, the primary 'loras' list is reused.",
+ )
+ boundary_ratio: float = Field(
+ default=0.875,
+ ge=0.0,
+ le=1.0,
+ description="Boundary timestep as a fraction of num_train_timesteps (Wan 2.2 A14B only). "
+ "High-noise expert: t >= boundary_ratio * num_train_timesteps. Low-noise expert: t below. "
+ "Ignored for TI2V-5B.",
+ )
+
+
@invocation_output("unet_output")
class UNetOutput(BaseInvocationOutput):
"""Base class for invocations that output a UNet field."""
diff --git a/invokeai/app/invocations/primitives.py b/invokeai/app/invocations/primitives.py
index 6249de0cd8e..d6b096b2232 100644
--- a/invokeai/app/invocations/primitives.py
+++ b/invokeai/app/invocations/primitives.py
@@ -29,10 +29,14 @@
SD3ConditioningField,
TensorField,
UIComponent,
+ VideoField,
+ WanConditioningField,
+ WanRefImageConditioningField,
ZImageConditioningField,
)
from invokeai.app.services.images.images_common import ImageDTO
from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.app.services.videos.videos_common import VideoDTO
"""
Primitives: Boolean, Integer, Float, String, Image, Latents, Conditioning, Color
@@ -510,6 +514,44 @@ def build(cls, conditioning_name: str) -> "AnimaConditioningOutput":
return cls(conditioning=AnimaConditioningField(conditioning_name=conditioning_name))
+@invocation_output("wan_conditioning_output")
+class WanConditioningOutput(BaseInvocationOutput):
+ """Base class for nodes that output a Wan 2.2 text conditioning tensor."""
+
+ conditioning: WanConditioningField = OutputField(description=FieldDescriptions.cond)
+
+ @classmethod
+ def build(cls, conditioning_name: str) -> "WanConditioningOutput":
+ return cls(conditioning=WanConditioningField(conditioning_name=conditioning_name))
+
+
+@invocation_output("wan_ref_image_output")
+class WanRefImageOutput(BaseInvocationOutput):
+ """Output of a Wan 2.2 reference-image VAE-encoder."""
+
+ ref_image: WanRefImageConditioningField = OutputField(
+ description="VAE-latent reference-image conditioning for Wan 2.2 I2V.",
+ title="Reference Image",
+ )
+
+ @classmethod
+ def build(
+ cls,
+ condition_tensor_name: str,
+ width: int,
+ height: int,
+ num_frames: int = 1,
+ ) -> "WanRefImageOutput":
+ return cls(
+ ref_image=WanRefImageConditioningField(
+ condition_tensor_name=condition_tensor_name,
+ width=width,
+ height=height,
+ num_frames=num_frames,
+ )
+ )
+
+
@invocation_output("conditioning_output")
class ConditioningOutput(BaseInvocationOutput):
"""Base class for nodes that output a single conditioning tensor"""
@@ -521,6 +563,57 @@ def build(cls, conditioning_name: str) -> "ConditioningOutput":
return cls(conditioning=ConditioningField(conditioning_name=conditioning_name))
+@invocation_output("video_output")
+class VideoOutput(BaseInvocationOutput):
+ """Output of a node that produces a video file (e.g. Wan 2.2 latents-to-video)."""
+
+ video: VideoField = OutputField(description="The output video")
+ width: int = OutputField(description="The width of the video in pixels")
+ height: int = OutputField(description="The height of the video in pixels")
+ num_frames: int = OutputField(description="The number of frames in the video")
+ fps: float = OutputField(description="The frames-per-second of the video")
+ duration: float = OutputField(description="The duration of the video in seconds")
+
+ @classmethod
+ def build(cls, video_dto: VideoDTO, num_frames: Optional[int] = None) -> "VideoOutput":
+ # Frame count isn't stored on the DTO; derive it from duration * fps when fps is known.
+ fps = video_dto.fps or 0.0
+ if num_frames is None:
+ num_frames = int(round(video_dto.duration * fps)) if fps > 0 else 0
+ return cls(
+ video=VideoField(video_name=video_dto.video_name),
+ width=video_dto.width,
+ height=video_dto.height,
+ num_frames=num_frames,
+ fps=fps,
+ duration=video_dto.duration,
+ )
+
+
+@invocation(
+ "video",
+ title="Video Primitive",
+ tags=["primitives", "video"],
+ category="primitives",
+ version="1.0.0",
+)
+class VideoInvocation(BaseInvocation):
+ """A video primitive value. Drop a video onto the field to make it available as an input
+ to downstream nodes (e.g. Frame from Video, Concatenate Videos)."""
+
+ video: VideoField = InputField(description="The video to load")
+
+ # Return annotation is a real class (not a forward-ref string) because a previous
+ # `from __future__ import annotations` left wan_l2v with a stringified annotation
+ # and crashed the output-class registry on startup (commit cac366229a).
+ def invoke(self, context: InvocationContext) -> VideoOutput:
+ from invokeai.app.util.video_thumbnails import decoder_frame_count
+
+ video_dto = context.videos.get_dto(self.video.video_name)
+ num_frames = decoder_frame_count(context.videos.get_path(self.video.video_name))
+ return VideoOutput.build(video_dto=video_dto, num_frames=num_frames)
+
+
@invocation_output("conditioning_collection_output")
class ConditioningCollectionOutput(BaseInvocationOutput):
"""Base class for nodes that output a collection of conditioning tensors"""
diff --git a/invokeai/app/invocations/video_concat.py b/invokeai/app/invocations/video_concat.py
new file mode 100644
index 00000000000..0ee69243863
--- /dev/null
+++ b/invokeai/app/invocations/video_concat.py
@@ -0,0 +1,259 @@
+"""Concatenate two or more videos with an optional transition.
+
+Pairs naturally with the I2V chaining workflow: feed several Wan-generated
+clips into this node to glue them into one longer video. The transition
+options hide the seam between independently-denoised clips.
+
+Implementation uses imageio (FFMPEG plugin) for both decode and encode, matching
+``wan_latents_to_video`` and ``video_thumbnails`` — so we can read our own
+output without surprises. Frames stream from the decoders into the encoder one
+at a time, buffering only the transition windows, so peak memory stays
+O(transition_frames) even when the inputs are long uploads (the upload cap
+admits files whose decoded frames would run to tens of gigabytes).
+"""
+
+import tempfile
+from collections import deque
+from pathlib import Path
+from typing import Callable, Iterable, Iterator, Literal, Optional
+
+import imageio.v2 as iio2
+import numpy as np
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ InputField,
+ VideoField,
+ WithBoard,
+ WithMetadata,
+)
+from invokeai.app.invocations.primitives import VideoOutput
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.app.util.video_thumbnails import iter_video_frames, probe_video
+
+TransitionMode = Literal["cut", "crossfade", "fade_through_black"]
+MAX_TRANSITION_MEMORY_BYTES = 512 * 1024 * 1024
+_BLEND_WORKING_FRAMES = 13
+
+
+def _crossfade(a_tail: list[np.ndarray], b_head: list[np.ndarray]) -> Iterator[np.ndarray]:
+ """Yields a linear A to B cross-dissolve without retaining the blended frames."""
+ n = len(a_tail)
+ for i in range(n):
+ alpha = (i + 1) / (n + 1)
+ blended = a_tail[i].astype(np.float32) * (1.0 - alpha) + b_head[i].astype(np.float32) * alpha
+ yield np.clip(blended, 0, 255).astype(np.uint8)
+
+
+def _fade_through_black(a_tail: list[np.ndarray], b_head: list[np.ndarray]) -> Iterator[np.ndarray]:
+ """A fades to black, then black fades to B. Consumes N/2 frames from each side and returns N output frames.
+
+ Asymmetric framing: the first ``len(a_tail)`` output frames are the trailing A frames scaled
+ toward zero brightness; the next ``len(b_head)`` are the leading B frames scaled up from zero.
+ """
+ n_a = len(a_tail)
+ for i, fa in enumerate(a_tail):
+ # 1.0 at i=0 (fully visible) → near 0 at i=n_a-1 (essentially black).
+ alpha = 1.0 - (i + 1) / (n_a + 1)
+ yield np.clip(fa.astype(np.float32) * alpha, 0, 255).astype(np.uint8)
+ n_b = len(b_head)
+ for j, fb in enumerate(b_head):
+ alpha = (j + 1) / (n_b + 1)
+ yield np.clip(fb.astype(np.float32) * alpha, 0, 255).astype(np.uint8)
+
+
+@invocation(
+ "video_concat",
+ title="Concatenate Videos",
+ tags=["video", "concat", "transition"],
+ category="video",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class VideoConcatInvocation(BaseInvocation, WithMetadata, WithBoard):
+ """Join two or more videos into a single MP4.
+
+ Transitions:
+
+ * ``cut`` — hard splice, no blending. Fastest; total length is the sum of inputs.
+ * ``crossfade`` — linear A→B cross-dissolve over ``transition_frames``. Each boundary
+ consumes ``transition_frames`` from both adjacent clips, so total length is
+ ``sum(inputs) - transition_frames * (n - 1)``.
+ * ``fade_through_black`` — A fades to black, then B fades in from black. Each boundary
+ consumes ``transition_frames // 2`` frames from the preceding clip's tail and the
+ remainder (``transition_frames - transition_frames // 2``) from the next clip's head,
+ so the total emitted is exactly ``transition_frames`` per boundary — even for odd
+ ``transition_frames`` — and the overall length equals the sum of inputs.
+
+ All inputs must share the same pixel dimensions. Output frame rate defaults to the
+ first input's fps; override with ``fps`` to force a specific rate (the frames are not
+ resampled, only the container is encoded at the new rate).
+ """
+
+ videos: list[VideoField] = InputField(
+ min_length=2,
+ description="Videos to concatenate, in order. At least two are required.",
+ )
+ transition: TransitionMode = InputField(
+ default="cut",
+ description="Transition between consecutive clips.",
+ )
+ transition_frames: int = InputField(
+ default=8,
+ ge=0,
+ le=240,
+ description="Length of each transition in frames. Ignored when transition is 'cut'.",
+ )
+ fps: Optional[int] = InputField(
+ default=None,
+ ge=1,
+ le=120,
+ description="Output frame rate. Defaults to the first input's fps.",
+ )
+
+ def invoke(self, context: InvocationContext) -> VideoOutput:
+ if len(self.videos) < 2:
+ raise ValueError("video_concat requires at least two input videos.")
+
+ paths: list[Path] = [context.videos.get_path(v.video_name) for v in self.videos]
+
+ # Probe inputs up front: enforce matching dims and pick the default output fps.
+ probes = [probe_video(p) for p in paths]
+ widths = {(w, h) for (w, h, _, _) in probes}
+ if len(widths) > 1:
+ raise ValueError(
+ f"All inputs must share the same dimensions. Got: "
+ f"{sorted(widths)}. Re-render at a single resolution before concatenating."
+ )
+ width, height, _, first_fps = probes[0]
+ self._validate_transition_memory(width, height)
+ output_fps = float(self.fps) if self.fps is not None else (first_fps or 16.0)
+
+ context.util.signal_progress(f"Joining {len(self.videos)} clip(s) ({self.transition}) @ {output_fps:.2f} fps")
+
+ tmp = tempfile.NamedTemporaryFile(prefix="invokeai_video_concat_", suffix=".mp4", delete=False)
+ tmp.close()
+ tmp_path = Path(tmp.name)
+ try:
+ # Frames stream from the decoders straight into the encoder; only the
+ # transition windows are buffered. See _iter_joined_frames.
+ writer = iio2.get_writer(str(tmp_path), format="FFMPEG", mode="I", fps=output_fps, codec="libx264")
+ num_frames = 0
+ try:
+ clip_iters = [iter_video_frames(p, is_canceled=context.util.is_canceled) for p in paths]
+ for frame in self._iter_joined_frames(clip_iters, is_canceled=context.util.is_canceled):
+ writer.append_data(frame)
+ num_frames += 1
+ finally:
+ writer.close()
+
+ if num_frames == 0:
+ raise ValueError("Concatenation produced zero output frames.")
+
+ duration = num_frames / output_fps
+ context.logger.info(
+ f"Encoded concatenated MP4: {num_frames} frames @ {output_fps:.2f} fps "
+ f"({duration:.2f}s) at {width}x{height}"
+ )
+ video_dto = context.videos.save(
+ source_path=tmp_path,
+ width=width,
+ height=height,
+ duration=duration,
+ fps=output_fps,
+ )
+ context.logger.info(f"Saved concatenated video: {video_dto.video_name}")
+ return VideoOutput.build(video_dto)
+ finally:
+ try:
+ tmp_path.unlink(missing_ok=True)
+ except Exception:
+ pass
+
+ def _estimate_transition_memory(self, width: int, height: int) -> int:
+ if self.transition == "cut" or self.transition_frames == 0:
+ return 0
+ buffered_frames = self.transition_frames * (2 if self.transition == "crossfade" else 1)
+ frame_bytes = width * height * 3
+ return frame_bytes * (buffered_frames + _BLEND_WORKING_FRAMES)
+
+ def _validate_transition_memory(self, width: int, height: int) -> None:
+ estimated_bytes = self._estimate_transition_memory(width, height)
+ if estimated_bytes > MAX_TRANSITION_MEMORY_BYTES:
+ estimated_mib = estimated_bytes / (1024 * 1024)
+ limit_mib = MAX_TRANSITION_MEMORY_BYTES / (1024 * 1024)
+ raise ValueError(
+ f"The requested transition needs an estimated {estimated_mib:.0f} MiB, "
+ f"which exceeds the {limit_mib:.0f} MiB transition memory budget. "
+ "Lower transition_frames or use lower-resolution clips."
+ )
+
+ def _iter_joined_frames(
+ self,
+ clips: list[Iterable[np.ndarray]],
+ is_canceled: Optional[Callable[[], bool]] = None,
+ ) -> Iterator[np.ndarray]:
+ """Yields the joined output frames, pulling lazily from each clip's frame iterator.
+
+ A frame is emitted as soon as it can no longer participate in a transition, so at
+ most one transition window (the previous clip's tail plus the current clip's head,
+ each bounded by ``transition_frames``) is buffered at a time — never a whole clip.
+
+ Transition layout matches the class docstring:
+
+ * ``crossfade`` consumes ``tf`` frames from both sides of each boundary and emits
+ ``tf`` blended frames in their place.
+ * ``fade_through_black`` splits ``tf`` asymmetrically (``tf // 2`` from the previous
+ clip's tail, the remainder from the next clip's head) so an odd ``tf`` still emits
+ exactly ``tf`` frames per boundary.
+
+ Raises ValueError if a clip decodes to zero frames or is too short to supply its
+ transition windows. Because frames stream straight into the encoder, that error can
+ surface mid-encode; the caller discards the partial output file.
+ """
+ if self.transition == "crossfade" and self.transition_frames > 0:
+ tail_need = head_need = self.transition_frames
+ blend = _crossfade
+ elif self.transition == "fade_through_black" and self.transition_frames > 0:
+ tail_need = self.transition_frames // 2
+ head_need = self.transition_frames - tail_need
+ blend = _fade_through_black
+ else:
+ # "cut", or a zero-length transition: plain concatenation.
+ tail_need = head_need = 0
+ blend = None
+
+ a_tail: list[np.ndarray] = []
+ for i, clip in enumerate(clips):
+ head_want = 0 if i == 0 else head_need
+ tail_keep = 0 if i == len(clips) - 1 else tail_need
+ b_head: list[np.ndarray] = []
+ tail_buf: deque[np.ndarray] = deque()
+ n_frames = 0
+ for frame in clip:
+ if is_canceled is not None and is_canceled():
+ raise CanceledException
+ frame = np.ascontiguousarray(frame)
+ n_frames += 1
+ # The clip's first head_want frames are consumed into the boundary blend
+ # with the previous clip's tail rather than emitted directly.
+ if len(b_head) < head_want:
+ b_head.append(frame)
+ if len(b_head) == head_want and blend is not None:
+ yield from blend(a_tail, b_head)
+ continue
+ # Hold back the last tail_keep frames seen so far; anything older is
+ # guaranteed not to be part of the next boundary and can be emitted.
+ tail_buf.append(frame)
+ if len(tail_buf) > tail_keep:
+ yield tail_buf.popleft()
+ if n_frames == 0:
+ raise ValueError(f"Input video {i} ({self.videos[i].video_name}) decoded to zero frames.")
+ if n_frames < head_want + tail_keep:
+ raise ValueError(
+ f"Clip {i} has {n_frames} frames but the requested transitions need "
+ f"{head_want} from its head + {tail_keep} from its tail. Lower "
+ f"transition_frames or use longer clips."
+ )
+ a_tail = list(tail_buf)
diff --git a/invokeai/app/invocations/video_frame_extract.py b/invokeai/app/invocations/video_frame_extract.py
new file mode 100644
index 00000000000..ff048ba4ff3
--- /dev/null
+++ b/invokeai/app/invocations/video_frame_extract.py
@@ -0,0 +1,75 @@
+"""Extract a single frame from a video as an image.
+
+Enables I2V "shot extension": take the last frame of one clip and feed it back
+in as the reference image for the next clip, then concatenate the MP4s
+externally to get a video longer than the model's single-shot frame budget.
+Also useful as a general-purpose video-to-image step.
+"""
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ InputField,
+ UIComponent,
+ VideoField,
+ WithBoard,
+ WithMetadata,
+)
+from invokeai.app.invocations.primitives import ImageOutput
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.app.util.video_thumbnails import decoder_frame_count, extract_video_frame, probe_video
+
+
+@invocation(
+ "video_frame_extract",
+ title="Frame from Video",
+ tags=["video", "image", "frame"],
+ category="image",
+ version="1.1.0",
+ classification=Classification.Prototype,
+)
+class VideoFrameExtractInvocation(BaseInvocation, WithMetadata, WithBoard):
+ """Extract a single frame from a video and save it as an image.
+
+ ``frame_index`` is 0-based. Negative indices count from the end, so the
+ default of -1 returns the final frame — the typical setup for chaining
+ I2V clips into a longer sequence.
+ """
+
+ video: VideoField = InputField(description="The video to extract a frame from.")
+ frame_index: int = InputField(
+ default=-1,
+ description="Index of the frame to extract. 0 = first frame, -1 = last frame, -2 = second-to-last, etc.",
+ ui_component=UIComponent.VideoFrameIndex,
+ )
+
+ def invoke(self, context: InvocationContext) -> ImageOutput:
+ video_path = context.videos.get_path(self.video.video_name)
+
+ # Resolve negative indices against the actual frame count rather than
+ # trusting imageio plugins to accept index=-1 uniformly. Use the decoder's
+ # frame count when available — duration*fps can be off-by-one for VFR
+ # uploads or containers with approximate metadata, causing frame_index=-1
+ # to point past the final frame.
+ index = self.frame_index
+ if index < 0:
+ n_frames = decoder_frame_count(video_path)
+ if n_frames is None:
+ _, _, duration, fps = probe_video(video_path)
+ if not fps or duration <= 0:
+ raise ValueError(
+ f"Cannot resolve negative frame index for video {self.video.video_name}: "
+ f"probe returned duration={duration}, fps={fps}."
+ )
+ n_frames = int(round(duration * fps))
+ if n_frames <= 0:
+ raise ValueError(f"Video {self.video.video_name} has no decodable frames (probed {n_frames}).")
+ index = n_frames + index
+ if index < 0:
+ raise ValueError(f"frame_index {self.frame_index} is out of range for a {n_frames}-frame video.")
+
+ frame = extract_video_frame(video_path, frame_index=index)
+ if frame is None:
+ raise ValueError(f"Failed to extract frame {index} from {self.video.video_name}.")
+
+ image_dto = context.images.save(image=frame)
+ return ImageOutput.build(image_dto=image_dto)
diff --git a/invokeai/app/invocations/video_frame_extract_range.py b/invokeai/app/invocations/video_frame_extract_range.py
new file mode 100644
index 00000000000..cd8dc26e062
--- /dev/null
+++ b/invokeai/app/invocations/video_frame_extract_range.py
@@ -0,0 +1,227 @@
+"""Extract a contiguous range of frames from a video and re-encode as MP4.
+
+Companion to ``video_frame_extract`` (single frame → image) and
+``video_concat`` (many videos → one). This node takes a slice of an input
+video and emits a new MP4, so the output can be fed straight into
+Concatenate Videos to splice clips together — e.g. trim a generated clip
+to a usable middle section before chaining it to another shot.
+"""
+
+import tempfile
+from itertools import islice
+from pathlib import Path
+from typing import Callable, Iterator, Optional, Protocol
+
+import imageio.v2 as iio2
+import numpy as np
+
+from invokeai.app.invocations.baseinvocation import (
+ BaseInvocation,
+ BaseInvocationOutput,
+ Classification,
+ invocation,
+ invocation_output,
+)
+from invokeai.app.invocations.fields import (
+ InputField,
+ OutputField,
+ UIComponent,
+ VideoField,
+ WithBoard,
+ WithMetadata,
+)
+from invokeai.app.invocations.primitives import VideoOutput
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.app.util.video_thumbnails import decoder_frame_count, iter_video_frames, probe_video
+
+
+class _FrameWriter(Protocol):
+ def append_data(self, frame: np.ndarray) -> None: ...
+
+
+def _write_frame_range(
+ frames: Iterator[np.ndarray],
+ writer: _FrameWriter,
+ start: int,
+ end: int,
+ is_canceled: Optional[Callable[[], bool]] = None,
+) -> int:
+ """Streams frames[start..end] (inclusive) from a lazy decoder into the writer.
+
+ Frames are appended one at a time as they stream past — the full range is never
+ materialized in RAM. The upload cap admits files whose decoded frames would run to
+ tens of gigabytes, so peak memory here must stay one-frame-sized regardless of the
+ requested range. Decoding stops as soon as ``end`` has been written. Returns the
+ number of frames written.
+ """
+ written = 0
+ for idx, frame in enumerate(islice(frames, end + 1)):
+ if is_canceled is not None and is_canceled():
+ raise CanceledException
+ if idx < start:
+ continue
+ writer.append_data(np.ascontiguousarray(frame))
+ written += 1
+ return written
+
+
+@invocation_output("extract_video_range_output")
+class ExtractVideoRangeOutput(BaseInvocationOutput):
+ """Output of ``extract_video_range``: a trimmed video plus the resolved frame indices.
+
+ Mirrors ``VideoOutput`` so the video can be piped directly into Concatenate Videos or
+ any other ``VideoField``-consuming node, and additionally exposes the resolved
+ (positive, clamped) start and end indices so chained workflows can feed them back in
+ — e.g. drive a downstream Frame from Video to pull the same boundary frame.
+ """
+
+ video: VideoField = OutputField(description="The trimmed video")
+ width: int = OutputField(description="The width of the video in pixels")
+ height: int = OutputField(description="The height of the video in pixels")
+ num_frames: int = OutputField(description="The number of frames in the trimmed video")
+ fps: float = OutputField(description="The frames-per-second of the trimmed video")
+ duration: float = OutputField(description="The duration of the trimmed video in seconds")
+ start_frame: int = OutputField(description="The resolved (positive, 0-based) start frame index in the source video")
+ end_frame: int = OutputField(description="The resolved (positive, 0-based) end frame index in the source video")
+
+
+@invocation(
+ "extract_video_range",
+ title="Frame Range from Video",
+ tags=["video", "trim", "range", "frames"],
+ category="video",
+ version="1.1.0",
+ classification=Classification.Prototype,
+)
+class ExtractVideoRangeInvocation(BaseInvocation, WithMetadata, WithBoard):
+ """Trim a video to a contiguous frame range and re-encode as MP4.
+
+ Both bounds are inclusive and 0-based — ``start_frame=10, end_frame=50``
+ emits 41 frames. Negative indices count from the end (``end_frame=-1``
+ is the final frame), matching ``video_frame_extract``. The output frame
+ rate defaults to the source video's frame rate; set ``fps=0`` to inherit
+ it (or 16 fps if the source rate can't be probed).
+
+ The resolved (positive) ``start_frame`` and ``end_frame`` are also emitted as
+ outputs, so chained workflows can re-use the boundary indices — e.g. feeding
+ them into a downstream Frame from Video to extract the same boundary frame.
+ """
+
+ video: VideoField = InputField(description="The video to extract a frame range from.")
+ start_frame: int = InputField(
+ default=0,
+ description=("First frame to keep, inclusive. 0 = first frame. Negative indices count from the end."),
+ ui_component=UIComponent.VideoFrameIndex,
+ )
+ end_frame: int = InputField(
+ default=-1,
+ description=("Last frame to keep, inclusive. -1 = last frame. Negative indices count from the end."),
+ ui_component=UIComponent.VideoFrameIndex,
+ )
+ fps: int = InputField(
+ default=0,
+ ge=0,
+ le=120,
+ description="Output frame rate. 0 = match the source video's frame rate "
+ "(falls back to 16 fps if the source rate can't be probed).",
+ )
+
+ def invoke(self, context: InvocationContext) -> ExtractVideoRangeOutput:
+ video_path = context.videos.get_path(self.video.video_name)
+ width, height, duration, source_fps = probe_video(video_path)
+
+ n_frames = decoder_frame_count(video_path)
+ if n_frames is None:
+ if not source_fps or duration <= 0:
+ raise ValueError(
+ f"Cannot determine frame count for {self.video.video_name}: "
+ f"probe returned duration={duration}, fps={source_fps}."
+ )
+ n_frames = int(round(duration * source_fps))
+ if n_frames <= 0:
+ raise ValueError(f"Video {self.video.video_name} has no decodable frames (probed {n_frames}).")
+
+ start = self._resolve_index(self.start_frame, n_frames, "start_frame")
+ end = self._resolve_index(self.end_frame, n_frames, "end_frame")
+ if end < start:
+ raise ValueError(
+ f"end_frame ({self.end_frame} → {end}) must be >= start_frame "
+ f"({self.start_frame} → {start}) after resolving negative indices."
+ )
+
+ # Derive the output frame rate from the source video when ``fps`` is 0
+ # (the default), so a trimmed clip plays back at the same speed as its
+ # source. Fall back to 16 fps (the Wan video default) when the source
+ # rate couldn't be probed.
+ if self.fps > 0:
+ output_fps = float(self.fps)
+ elif source_fps and source_fps > 0:
+ output_fps = float(source_fps)
+ else:
+ output_fps = 16.0
+
+ context.util.signal_progress(f"Transcoding frames {start}-{end} of {n_frames} @ {output_fps:.2f} fps")
+
+ tmp = tempfile.NamedTemporaryFile(prefix="invokeai_video_range_", suffix=".mp4", delete=False)
+ tmp.close()
+ tmp_path = Path(tmp.name)
+ try:
+ # imageio's iter_index isn't exposed by iio.imiter, so we enumerate and skip.
+ # Frames stream straight from the decoder into the encoder; see _write_frame_range.
+ writer = iio2.get_writer(str(tmp_path), format="FFMPEG", mode="I", fps=output_fps, codec="libx264")
+ try:
+ num_frames = _write_frame_range(
+ iter_video_frames(video_path, is_canceled=context.util.is_canceled),
+ writer,
+ start,
+ end,
+ is_canceled=context.util.is_canceled,
+ )
+ finally:
+ writer.close()
+
+ expected_frames = end - start + 1
+ if num_frames != expected_frames:
+ raise ValueError(
+ f"Decoded only {num_frames} of {expected_frames} requested frames for range {start}-{end} "
+ f"of {self.video.video_name} "
+ f"(probed {n_frames} frames). The container's metadata may be inaccurate."
+ )
+
+ out_duration = num_frames / output_fps
+ context.logger.info(
+ f"Encoded trimmed MP4: {num_frames} frames @ {output_fps:.2f} fps "
+ f"({out_duration:.2f}s) at {width}x{height}"
+ )
+ video_dto = context.videos.save(
+ source_path=tmp_path,
+ width=width,
+ height=height,
+ duration=out_duration,
+ fps=output_fps,
+ )
+ context.logger.info(f"Saved trimmed video: {video_dto.video_name}")
+ base = VideoOutput.build(video_dto)
+ return ExtractVideoRangeOutput(
+ video=base.video,
+ width=base.width,
+ height=base.height,
+ num_frames=base.num_frames,
+ fps=base.fps,
+ duration=base.duration,
+ start_frame=start,
+ end_frame=end,
+ )
+ finally:
+ try:
+ tmp_path.unlink(missing_ok=True)
+ except Exception:
+ pass
+
+ @staticmethod
+ def _resolve_index(value: int, n_frames: int, field_name: str) -> int:
+ resolved = value + n_frames if value < 0 else value
+ if resolved < 0 or resolved >= n_frames:
+ raise ValueError(f"{field_name}={value} is out of range for a {n_frames}-frame video.")
+ return resolved
diff --git a/invokeai/app/invocations/wan_denoise.py b/invokeai/app/invocations/wan_denoise.py
new file mode 100644
index 00000000000..b5edf22946f
--- /dev/null
+++ b/invokeai/app/invocations/wan_denoise.py
@@ -0,0 +1,699 @@
+"""Wan 2.2 denoise invocation.
+
+Supports both single-transformer (TI2V-5B) and dual-expert MoE (A14B) denoising.
+For A14B the high-noise expert handles timesteps ``t >= boundary_timestep`` and
+the low-noise expert handles ``t < boundary_timestep``, where
+``boundary_timestep = boundary_ratio * num_train_timesteps`` (typically 1000).
+
+To keep VRAM usage manageable both experts are pinned in the model cache
+(system RAM) but only one is GPU-resident at a time. The boundary is normally
+crossed once per denoise, so the swap incurs a single CPU→GPU transfer.
+
+Phase 8 will add inpaint via :class:`RectifiedFlowInpaintExtension`.
+
+The transformer call signature mirrors Diffusers' ``WanPipeline``:
+
+ transformer(
+ hidden_states=latents_5d, # [B, C, 1, H/s, W/s]
+ timestep=t.expand(B), # scheduler-time
+ encoder_hidden_states=prompt_embeds, # [B, seq_len, 4096]
+ attention_kwargs=None,
+ return_dict=False,
+ )[0]
+"""
+
+from contextlib import ExitStack
+from pathlib import Path
+from typing import Any, Callable, Iterable, Iterator, Optional, Tuple
+
+import torch
+import torchvision.transforms as tv_transforms
+from torchvision.transforms.functional import resize as tv_resize
+from tqdm import tqdm
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ DenoiseMaskField,
+ FieldDescriptions,
+ Input,
+ InputField,
+ LatentsField,
+ WanConditioningField,
+ WanRefImageConditioningField,
+)
+from invokeai.app.invocations.model import LoRAField, WanTransformerField
+from invokeai.app.invocations.primitives import LatentsOutput
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, WanVariantType
+from invokeai.backend.patches.layer_patcher import LayerPatcher
+from invokeai.backend.patches.lora_conversions.wan_lora_constants import WAN_LORA_TRANSFORMER_PREFIX
+from invokeai.backend.patches.model_patch_raw import ModelPatchRaw
+from invokeai.backend.rectified_flow.rectified_flow_inpaint_extension import RectifiedFlowInpaintExtension
+from invokeai.backend.stable_diffusion.diffusers_pipeline import PipelineIntermediateState
+from invokeai.backend.stable_diffusion.diffusion.conditioning_data import WanConditioningInfo
+from invokeai.backend.util.devices import TorchDevice
+from invokeai.backend.wan.sampling_utils import get_spatial_scale_factor, make_noise
+
+# Type alias: a factory that produces a fresh iterator of (LoRA patch, weight)
+# pairs each time it is called. We need fresh iterators because the patcher
+# consumes the iterator once per ``apply_smart_model_patches`` invocation, and
+# the expert may be swapped (and re-entered) multiple times in a render.
+LoRAIteratorFactory = Callable[[], Iterable[Tuple[ModelPatchRaw, float]]]
+
+
+def _resolve_variant(context: InvocationContext, transformer_field: WanTransformerField) -> WanVariantType:
+ """Look up the Wan variant from the main model config that produced this transformer."""
+ config = context.models.get_config(transformer_field.transformer)
+ variant = getattr(config, "variant", None)
+ if not isinstance(variant, WanVariantType):
+ raise ValueError(f"Could not determine Wan variant from model {config.name!r}: variant is {variant!r}.")
+ return variant
+
+
+def _scheduler_path_for_transformer(context: InvocationContext, transformer_field: WanTransformerField) -> Path | None:
+ """Return the on-disk ``scheduler/`` directory for the main model, or None."""
+ config = context.models.get_config(transformer_field.transformer)
+ model_root = context.models.get_absolute_path(config)
+ if model_root.is_file():
+ return None
+ candidate = model_root / "scheduler"
+ if (candidate / "scheduler_config.json").exists():
+ return candidate
+ return None
+
+
+def _default_scheduler_for_variant(variant: WanVariantType):
+ """Build a variant-appropriate scheduler when no on-disk config is available.
+
+ Standalone GGUF / single-file installs don't ship a ``scheduler/`` directory,
+ so we have to reconstruct the scheduler from variant knowledge. Values are
+ verbatim from each variant's ``scheduler/scheduler_config.json`` in the
+ matching ``Wan-AI/Wan2.2-*-Diffusers`` repo.
+ """
+ from diffusers import FlowMatchEulerDiscreteScheduler, UniPCMultistepScheduler
+
+ if variant == WanVariantType.TI2V_5B:
+ # Wan-AI/Wan2.2-TI2V-5B-Diffusers/scheduler/scheduler_config.json. The
+ # combination of flow_prediction + use_flow_sigmas + flow_shift=5.0 is
+ # what differentiates this from a generic UniPC schedule; without it
+ # samples drift on this model.
+ return UniPCMultistepScheduler(
+ num_train_timesteps=1000,
+ solver_order=2,
+ prediction_type="flow_prediction",
+ flow_shift=5.0,
+ use_flow_sigmas=True,
+ solver_type="bh2",
+ final_sigmas_type="zero",
+ )
+ # A14B variants ship FlowMatchEulerDiscreteScheduler at default settings.
+ return FlowMatchEulerDiscreteScheduler()
+
+
+class _ExpertSwapper:
+ """Manages GPU residency and LoRA patching of one or two Wan transformer experts.
+
+ Both experts are kept in the model cache (system RAM); only one is on
+ device at a time. ``get(label)`` returns the model for the requested label,
+ swapping GPU residency when the label changes and applying that expert's
+ LoRA patches via ``LayerPatcher.apply_smart_model_patches``.
+
+ Ordering on swap: exit the active expert's LoRA context (restores weights)
+ -> exit ``model_on_device`` (returns expert to RAM) -> load the new expert
+ (fresh handle) -> enter its device context -> apply its LoRAs. This
+ mirrors the pattern used by ``flux_denoise``/``anima_denoise`` but adds
+ the extra context layer needed for dual experts.
+
+ Model handles are obtained lazily inside ``get()`` rather than cached at
+ construction. With dual ~9 GB GGUF experts plus a UMT5-XXL encoder
+ competing for the RAM cache, holding both ``LoadedModel`` handles upfront
+ can leave one of them stale by the time the swap happens — InvokeAI's
+ model cache emits a ``has already been dropped from the RAM cache``
+ warning and reloads from disk per swap. See issue #7513 for the broader
+ pattern.
+ """
+
+ HIGH = "high"
+ LOW = "low"
+
+ def __init__(
+ self,
+ context: InvocationContext,
+ high_model: Any,
+ low_model: Any | None,
+ inference_dtype: torch.dtype,
+ high_lora_factory: LoRAIteratorFactory | None = None,
+ low_lora_factory: LoRAIteratorFactory | None = None,
+ high_is_quantized: bool = False,
+ low_is_quantized: bool = False,
+ ) -> None:
+ self._context = context
+ self._high_model = high_model
+ self._low_model = low_model
+ self._inference_dtype = inference_dtype
+ self._high_lora_factory = high_lora_factory
+ self._low_lora_factory = low_lora_factory
+ self._high_is_quantized = high_is_quantized
+ self._low_is_quantized = low_is_quantized
+ self._active_label: str | None = None
+ self._active_info: Any | None = None
+ self._active_device_ctx: Any | None = None
+ self._active_lora_ctx: Any | None = None
+ self._active_model: Any | None = None
+
+ def get(self, label: str) -> Any:
+ if label not in (self.HIGH, self.LOW):
+ raise ValueError(f"Unknown expert label: {label!r}")
+ if label == self.LOW and self._low_model is None:
+ raise ValueError("Low-noise expert was requested but is not available.")
+ if label == self._active_label:
+ assert self._active_model is not None
+ return self._active_model
+
+ # Capture the outgoing expert's cache record before _release() drops our handle.
+ # We need it to force-unload below.
+ outgoing_cached_model = None
+ if self._active_info is not None:
+ # ``LoadedModel`` exposes its cache_record only via a private attribute. There
+ # is no public ``unload_from_vram`` on the LoadedModel today, and we don't want
+ # to take on a broader backend refactor in this fix; tolerate AttributeError
+ # so a future refactor doesn't break the swap.
+ outgoing_cached_model = getattr(self._active_info, "_cache_record", None)
+ if outgoing_cached_model is not None:
+ outgoing_cached_model = getattr(outgoing_cached_model, "cached_model", None)
+
+ # Release current GPU residency before bringing the other expert on device.
+ self._release()
+
+ # Force the outgoing expert off GPU. The model cache's automatic offload
+ # (inside lock() -> _offload_unlocked_models) decides how much to free based on
+ # ``torch.cuda.memory_allocated()`` minus a 3 GB working-memory budget. With Wan
+ # 81-frame video the intermediate activations from the previous denoise step are
+ # still allocated alongside the just-unlocked high-noise expert, so the cache
+ # underestimates how much room the new expert really needs and partial-loads
+ # most of its layers to CPU. The user-visible symptom: log line "Loaded model
+ # ... VRAM: 2381 MB (25.9%)" instead of ~100% for the incoming expert.
+ #
+ # Sidestep the heuristic by explicitly unloading every weight of the outgoing
+ # expert to RAM. This is safe even if the cache evicted the entry between unlock
+ # and now — the cached_model object still owns the tensors.
+ if outgoing_cached_model is not None:
+ try:
+ outgoing_cached_model.full_unload_from_vram()
+ except Exception:
+ pass
+
+ # Hand the PyTorch allocator a clean slate before partial_load_to_vram measures
+ # free space — the freed blocks stay pinned in the caching allocator until
+ # empty_cache is called.
+ TorchDevice.empty_cache()
+
+ # Load the requested expert lazily so its ``LoadedModel`` handle is
+ # always fresh — see class docstring for the cache-eviction reasoning.
+ model_id = self._high_model if label == self.HIGH else self._low_model
+ info = self._context.models.load(model_id)
+ device_ctx = info.model_on_device()
+ cached_weights, model = device_ctx.__enter__()
+
+ # Stash the device-context state immediately. If anything below fails (most
+ # likely the LoRA patcher), the surrounding ExitStack will eventually call
+ # ``swapper.close() -> _release()`` to clean up, and ``_release`` needs to
+ # see the device context to exit it. Without this early stash the expert's
+ # weights stay GPU-resident (8-9 GB for GGUF experts) until the cache's LRU
+ # finally evicts them, masquerading as a memory leak.
+ self._active_label = label
+ self._active_info = info
+ self._active_device_ctx = device_ctx
+ self._active_model = model
+
+ # Apply LoRA patches for this expert. GGUF transformers need sidecar
+ # patching since direct patching of GGMLTensors isn't supported.
+ lora_factory = self._high_lora_factory if label == self.HIGH else self._low_lora_factory
+ is_quantized = self._high_is_quantized if label == self.HIGH else self._low_is_quantized
+ lora_ctx: Any | None = None
+ if lora_factory is not None:
+ lora_ctx = LayerPatcher.apply_smart_model_patches(
+ model=model,
+ patches=lora_factory(),
+ prefix=WAN_LORA_TRANSFORMER_PREFIX,
+ dtype=self._inference_dtype,
+ cached_weights=cached_weights,
+ force_sidecar_patching=is_quantized,
+ )
+ lora_ctx.__enter__()
+
+ self._active_lora_ctx = lora_ctx
+ return model
+
+ def _release(self) -> None:
+ # LoRA context first so weights are restored before the model leaves GPU.
+ if self._active_lora_ctx is not None:
+ self._active_lora_ctx.__exit__(None, None, None)
+ if self._active_device_ctx is not None:
+ self._active_device_ctx.__exit__(None, None, None)
+ self._active_label = None
+ self._active_info = None
+ self._active_device_ctx = None
+ self._active_lora_ctx = None
+ self._active_model = None
+
+ def close(self) -> None:
+ self._release()
+
+
+@invocation(
+ "wan_denoise",
+ title="Denoise - Wan 2.2",
+ tags=["image", "wan"],
+ category="image",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanDenoiseInvocation(BaseInvocation):
+ """Run the denoising process with a Wan 2.2 model.
+
+ Drives a flow-matching Euler schedule via Diffusers'
+ ``FlowMatchEulerDiscreteScheduler``. CFG is supported when negative
+ conditioning is provided and ``guidance_scale != 1.0``.
+
+ For Wan 2.2 A14B the high-noise expert handles timesteps at and above
+ ``boundary_ratio * num_train_timesteps``; the low-noise expert handles
+ timesteps below. Both experts share the model cache; only the active one is
+ GPU-resident at any time.
+ """
+
+ transformer: WanTransformerField = InputField(
+ description="Wan transformer field (transformer + optional dual-expert metadata).",
+ input=Input.Connection,
+ title="Transformer",
+ )
+ positive_conditioning: WanConditioningField = InputField(
+ description=FieldDescriptions.positive_cond, input=Input.Connection
+ )
+ negative_conditioning: Optional[WanConditioningField] = InputField(
+ default=None, description=FieldDescriptions.negative_cond, input=Input.Connection
+ )
+
+ ref_image: Optional[WanRefImageConditioningField] = InputField(
+ default=None,
+ description=FieldDescriptions.wan_ref_image,
+ input=Input.Connection,
+ title="Reference Image",
+ )
+
+ latents: Optional[LatentsField] = InputField(
+ default=None,
+ description=FieldDescriptions.latents,
+ input=Input.Connection,
+ )
+ denoise_mask: Optional[DenoiseMaskField] = InputField(
+ default=None,
+ description=FieldDescriptions.denoise_mask,
+ input=Input.Connection,
+ )
+
+ denoising_start: float = InputField(default=0.0, ge=0, le=1, description=FieldDescriptions.denoising_start)
+ denoising_end: float = InputField(default=1.0, ge=0, le=1, description=FieldDescriptions.denoising_end)
+ add_noise: bool = InputField(default=True, description="Add noise based on denoising start.")
+
+ guidance_scale: float = InputField(
+ default=4.0,
+ ge=1.0,
+ description="Classifier-free guidance scale. 4.0 is the Wan 2.2 default for A14B; "
+ "TI2V-5B can tolerate higher values up to ~5.5.",
+ title="Guidance Scale",
+ )
+ guidance_scale_low_noise: Optional[float] = InputField(
+ default=None,
+ ge=0.0,
+ description="Optional separate CFG scale for the low-noise expert (Wan 2.2 A14B only). "
+ "Values below 1.0 (including 0) fall back to the primary 'Guidance Scale'. "
+ "Ignored for TI2V-5B.",
+ title="Guidance Scale (Low Noise)",
+ )
+ # Wan transformer has ``patch_size=(1, 2, 2)``: combined with the VAE's
+ # 8x spatial scale, generated H/W must be a multiple of 16 (not just 8)
+ # or the patch round-trip lands off-by-one and the scheduler step fails
+ # with a spatial-dim mismatch.
+ width: int = InputField(default=1024, multiple_of=16, description="Width of the generated image.")
+ height: int = InputField(default=1024, multiple_of=16, description="Height of the generated image.")
+ steps: int = InputField(default=40, gt=0, description="Number of denoising steps.")
+ seed: int = InputField(default=0, description="Randomness seed for reproducibility.")
+
+ @torch.no_grad()
+ def invoke(self, context: InvocationContext) -> LatentsOutput:
+ latents = self._run_diffusion(context)
+ latents = latents.detach().to("cpu")
+ name = context.tensors.save(tensor=latents)
+ return LatentsOutput.build(latents_name=name, latents=latents, seed=None)
+
+ def _run_diffusion(self, context: InvocationContext) -> torch.Tensor:
+ if self.denoising_start >= self.denoising_end:
+ raise ValueError(
+ f"denoising_start ({self.denoising_start}) must be less than denoising_end ({self.denoising_end})."
+ )
+
+ device = TorchDevice.choose_torch_device()
+ inference_dtype = TorchDevice.choose_bfloat16_safe_dtype(device)
+
+ variant = _resolve_variant(context, self.transformer)
+ spatial_scale = get_spatial_scale_factor(variant)
+
+ scheduler = self._build_scheduler(context, device)
+
+ pos_cond = self._load_conditioning(context, self.positive_conditioning, device=device, dtype=inference_dtype)
+ do_cfg = self.guidance_scale != 1.0 and self.negative_conditioning is not None
+ neg_cond: WanConditioningInfo | None = None
+ if do_cfg:
+ assert self.negative_conditioning is not None
+ neg_cond = self._load_conditioning(
+ context, self.negative_conditioning, device=device, dtype=inference_dtype
+ )
+
+ # Reference-image conditioning (Wan 2.2 I2V-A14B only). The condition
+ # tensor is 20 channels (4 mask + 16 VAE-encoded image latents); it
+ # gets concatenated to the 16-channel noise latents each step,
+ # yielding the 36-channel input the I2V transformer expects.
+ ref_condition: torch.Tensor | None = None
+ if self.ref_image is not None:
+ if variant != WanVariantType.I2V_A14B:
+ raise ValueError(
+ f"Reference-image conditioning is only supported by the Wan 2.2 I2V variant. "
+ f"The selected transformer is {variant.value!r}. Remove the Reference Image input "
+ "or load an I2V model."
+ )
+ if self.ref_image.width != self.width or self.ref_image.height != self.height:
+ raise ValueError(
+ f"Reference-image dimensions ({self.ref_image.width}x{self.ref_image.height}) must "
+ f"match denoise dimensions ({self.width}x{self.height})."
+ )
+ if self.ref_image.num_frames > 1:
+ # The image denoise produces single-frame output; concatenating a multi-frame
+ # condition to a single-frame noise tensor mismatches the temporal dim and the
+ # downstream tensor-shape error would be unhelpful.
+ raise ValueError(
+ f"This denoise node produces a single-frame image but the reference image was "
+ f"encoded for {self.ref_image.num_frames} frames. Use the Denoise Video - Wan 2.2 "
+ "node for video I2V, or set num_frames=1 on the Reference Image node."
+ )
+ ref_condition = context.tensors.load(self.ref_image.condition_tensor_name).to(
+ device=device, dtype=inference_dtype
+ )
+
+ # Schedule timesteps. set_timesteps populates scheduler.timesteps and
+ # scheduler.sigmas (where sigmas is in [0, 1] flow-matching space).
+ scheduler.set_timesteps(num_inference_steps=self.steps, device=device)
+ timesteps = scheduler.timesteps
+ # sigmas has length steps + 1.
+ sigmas = scheduler.sigmas
+
+ # Apply denoising_start / denoising_end clipping.
+ if self.denoising_start > 0 or self.denoising_end < 1:
+ start_idx = int(self.denoising_start * self.steps)
+ end_idx = int(self.denoising_end * self.steps)
+ timesteps = timesteps[start_idx:end_idx]
+ sigmas = sigmas[start_idx : end_idx + 1]
+ total_steps = len(timesteps)
+
+ # Latents stay in fp32 throughout the denoise loop to avoid accumulating
+ # bf16 quantization across the scheduler's small per-step deltas. We
+ # cast to bf16 only when calling the transformer, matching Diffusers'
+ # WanPipeline (which calls ``prepare_latents(..., dtype=torch.float32)``
+ # then ``latent_model_input = latents.to(transformer_dtype)``).
+ latent_dtype = torch.float32
+
+ # Load init latents (img2img) and convert 4D → 5D.
+ init_latents_5d: torch.Tensor | None = None
+ if self.latents is not None:
+ loaded = context.tensors.load(self.latents.latents_name).to(device=device, dtype=latent_dtype)
+ if loaded.ndim == 4:
+ loaded = loaded.unsqueeze(2)
+ init_latents_5d = loaded
+
+ # Determine the latent channel count. Prefer init_latents shape; otherwise
+ # fall back to the variant default. (We avoid loading the transformer just
+ # to read .config.in_channels; the variant gives us the right answer.)
+ latent_channels = (
+ init_latents_5d.shape[1]
+ if init_latents_5d is not None
+ else (48 if variant == WanVariantType.TI2V_5B else 16)
+ )
+
+ noise = make_noise(
+ batch_size=1,
+ latent_channels=latent_channels,
+ height=self.height,
+ width=self.width,
+ spatial_scale_factor=spatial_scale,
+ device=device,
+ dtype=latent_dtype,
+ seed=self.seed,
+ )
+
+ # Combine init latents + noise per the schedule's starting sigma.
+ if init_latents_5d is not None:
+ if self.add_noise:
+ s_0 = float(sigmas[0])
+ latents = s_0 * noise + (1.0 - s_0) * init_latents_5d
+ else:
+ latents = init_latents_5d
+ else:
+ if self.denoising_start > 1e-5:
+ raise ValueError("denoising_start should be 0 when initial latents are not provided.")
+ latents = noise
+
+ if total_steps <= 0:
+ return latents.squeeze(2)
+
+ # Inpaint extension (4D space — the existing extension is shape-agnostic
+ # but operates on the squeezed-T shape we use for masks).
+ inpaint_mask = self._prep_inpaint_mask(context, latents.squeeze(2))
+ inpaint_extension: RectifiedFlowInpaintExtension | None = None
+ if inpaint_mask is not None:
+ if init_latents_5d is None:
+ raise ValueError("Initial latents are required when using an inpaint mask (img2img inpainting).")
+ inpaint_extension = RectifiedFlowInpaintExtension(
+ init_latents=init_latents_5d.squeeze(2),
+ inpaint_mask=inpaint_mask,
+ noise=noise.squeeze(2),
+ )
+
+ step_callback = self._build_step_callback(context)
+
+ # Resolve experts and the boundary timestep that triggers the MoE swap.
+ #
+ # We deliberately do NOT call ``context.models.load(...)`` for the
+ # transformer experts here — that would put both ~9 GB GGUF handles
+ # in the model cache concurrently. With UMT5-XXL (~10 GB) competing
+ # for the same cache, the LRU policy can drop one of them by the
+ # time the denoise loop swaps in, producing the
+ # "has already been dropped from the RAM cache" warning and forcing
+ # a disk reload per swap. The swapper calls ``models.load`` lazily
+ # inside each ``get()`` instead, so handles are always fresh.
+ #
+ # The config metadata (variant / format) is fine to read upfront —
+ # ``get_config`` doesn't touch the weights cache.
+ high_model = self.transformer.transformer
+ low_model = self.transformer.transformer_low_noise
+ low_config = context.models.get_config(low_model) if low_model is not None else None
+ # FlowMatchEulerDiscreteScheduler stores num_train_timesteps in its config
+ # (default 1000). Diffusers' WanPipeline computes:
+ # boundary_timestep = boundary_ratio * num_train_timesteps
+ num_train_timesteps = int(scheduler.config.num_train_timesteps)
+ boundary_timestep = self.transformer.boundary_ratio * num_train_timesteps if low_model is not None else None
+
+ # LoRA wiring. The high-noise expert uses ``transformer.loras``; the
+ # low-noise expert uses ``transformer.loras_low_noise``, falling back
+ # to the primary list if empty (matches the WanTransformerField semantics).
+ # Quantized (GGUF) experts force sidecar patching so GGMLTensor weights
+ # aren't touched directly.
+ high_loras = self.transformer.loras
+ low_loras = self.transformer.loras_low_noise or self.transformer.loras
+ high_config = context.models.get_config(high_model)
+ high_is_quantized = high_config.format == ModelFormat.GGUFQuantized
+ low_is_quantized = low_config.format == ModelFormat.GGUFQuantized if low_config is not None else False
+
+ def high_lora_factory() -> Iterable[Tuple[ModelPatchRaw, float]]:
+ return self._lora_iterator(context, high_loras)
+
+ def low_lora_factory() -> Iterable[Tuple[ModelPatchRaw, float]]:
+ return self._lora_iterator(context, low_loras)
+
+ with ExitStack() as exit_stack:
+ swapper = _ExpertSwapper(
+ context=context,
+ high_model=high_model,
+ low_model=low_model,
+ inference_dtype=inference_dtype,
+ high_lora_factory=high_lora_factory if high_loras else None,
+ low_lora_factory=low_lora_factory if low_loras else None,
+ high_is_quantized=high_is_quantized,
+ low_is_quantized=low_is_quantized,
+ )
+ exit_stack.callback(swapper.close)
+
+ for step_idx, t in enumerate(tqdm(timesteps, desc="Denoising (Wan 2.2)", total=total_steps)):
+ timestep = t.expand(latents.shape[0])
+
+ # Pick the active expert: high-noise for t >= boundary_timestep,
+ # low-noise below. Single-transformer models always use HIGH.
+ if low_model is not None and float(t) < float(boundary_timestep):
+ active_label = _ExpertSwapper.LOW
+ # Treat None or values below 1.0 (incl. the FE's default 0)
+ # as "use the primary guidance_scale".
+ low_cfg = self.guidance_scale_low_noise
+ active_cfg = low_cfg if (low_cfg is not None and low_cfg >= 1.0) else self.guidance_scale
+ else:
+ active_label = _ExpertSwapper.HIGH
+ active_cfg = self.guidance_scale
+
+ transformer = swapper.get(active_label)
+
+ # Cast latents to the transformer's dtype only for the forward
+ # pass; keep the scheduler-level latents in fp32.
+ latent_model_input = latents.to(dtype=inference_dtype)
+
+ # For I2V, concatenate the ref-image condition (4-ch mask + 16-ch
+ # image latents) along the channel dim, producing the 36-channel
+ # input the I2V transformer's patch_embedding expects.
+ if ref_condition is not None:
+ latent_model_input = torch.cat([latent_model_input, ref_condition], dim=1)
+
+ noise_pred_cond = transformer(
+ hidden_states=latent_model_input,
+ timestep=timestep,
+ encoder_hidden_states=pos_cond.prompt_embeds.unsqueeze(0),
+ attention_kwargs=None,
+ return_dict=False,
+ )[0]
+
+ if do_cfg and neg_cond is not None:
+ noise_pred_uncond = transformer(
+ hidden_states=latent_model_input,
+ timestep=timestep,
+ encoder_hidden_states=neg_cond.prompt_embeds.unsqueeze(0),
+ attention_kwargs=None,
+ return_dict=False,
+ )[0]
+ noise_pred = noise_pred_uncond + active_cfg * (noise_pred_cond - noise_pred_uncond)
+ else:
+ noise_pred = noise_pred_cond
+
+ latents = scheduler.step(noise_pred, t, latents, return_dict=False)[0]
+
+ if inpaint_extension is not None:
+ sigma_prev = float(sigmas[step_idx + 1])
+ latents_4d = latents.squeeze(2)
+ latents_4d = inpaint_extension.merge_intermediate_latents_with_init_latents(latents_4d, sigma_prev)
+ latents = latents_4d.unsqueeze(2)
+
+ step_callback(
+ PipelineIntermediateState(
+ step=step_idx + 1,
+ order=1,
+ total_steps=total_steps,
+ timestep=int(t.item()),
+ latents=latents.squeeze(2),
+ )
+ )
+
+ # Squeeze T for downstream 4D consumers.
+ return latents.squeeze(2)
+
+ def _build_scheduler(self, context: InvocationContext, device: torch.device):
+ """Construct the scheduler matching the model's on-disk ``scheduler_config.json``.
+
+ Wan model variants ship different schedulers — e.g. TI2V-5B uses
+ ``UniPCMultistepScheduler`` with ``flow_shift=5.0``, while the
+ standard A14B reference uses ``FlowMatchEulerDiscreteScheduler``.
+ We dispatch on ``_class_name`` so the noise schedule matches what the
+ model was trained against. When no on-disk config is available
+ (standalone GGUF / single-file installs that don't ship a
+ ``scheduler/`` directory), fall back to a variant-aware default —
+ TI2V-5B gets its UniPC scheduler with the right flow params instead
+ of the generic FlowMatchEuler, which otherwise produces drifty
+ samples for that model.
+ """
+ import json
+
+ import diffusers
+ from diffusers import FlowMatchEulerDiscreteScheduler
+
+ scheduler_dir = _scheduler_path_for_transformer(context, self.transformer)
+ if scheduler_dir is None:
+ variant = _resolve_variant(context, self.transformer)
+ return _default_scheduler_for_variant(variant)
+
+ # Read the on-disk class name and instantiate that class. Diffusers'
+ # SchedulerMixin.from_pretrained does class dispatch internally, but
+ # only when called from the abstract base; calling a concrete subclass
+ # silently builds the wrong type. Resolve it explicitly.
+ config_path = scheduler_dir / "scheduler_config.json"
+ try:
+ with config_path.open("r", encoding="utf-8") as f:
+ cfg = json.load(f)
+ class_name = cfg.get("_class_name")
+ scheduler_cls = getattr(diffusers, class_name, None) if class_name else None
+ except (OSError, json.JSONDecodeError):
+ scheduler_cls = None
+
+ if scheduler_cls is None:
+ scheduler_cls = FlowMatchEulerDiscreteScheduler
+
+ return scheduler_cls.from_pretrained(str(scheduler_dir), local_files_only=True)
+
+ def _load_conditioning(
+ self,
+ context: InvocationContext,
+ cond_field: WanConditioningField,
+ *,
+ device: torch.device,
+ dtype: torch.dtype,
+ ) -> WanConditioningInfo:
+ cond_data = context.conditioning.load(cond_field.conditioning_name)
+ assert len(cond_data.conditionings) == 1
+ cond_info = cond_data.conditionings[0]
+ assert isinstance(cond_info, WanConditioningInfo)
+ return cond_info.to(device=device, dtype=dtype)
+
+ def _prep_inpaint_mask(self, context: InvocationContext, latents_4d: torch.Tensor) -> torch.Tensor | None:
+ """Resize the user-supplied mask down to latent resolution.
+
+ Convention matches Anima/FLUX: the original mask has 0 = preserve and
+ 1 = denoise; the extension expects the inverted form.
+ """
+ if self.denoise_mask is None:
+ return None
+ mask = context.tensors.load(self.denoise_mask.mask_name)
+ mask = 1.0 - mask
+ _, _, latent_h, latent_w = latents_4d.shape
+ mask = tv_resize(
+ img=mask,
+ size=[latent_h, latent_w],
+ interpolation=tv_transforms.InterpolationMode.BILINEAR,
+ antialias=False,
+ )
+ return mask.to(device=latents_4d.device, dtype=latents_4d.dtype)
+
+ def _build_step_callback(self, context: InvocationContext) -> Callable[[PipelineIntermediateState], None]:
+ def step_callback(state: PipelineIntermediateState) -> None:
+ context.util.sd_step_callback(state, BaseModelType.Wan)
+
+ return step_callback
+
+ def _lora_iterator(
+ self, context: InvocationContext, loras: list[LoRAField]
+ ) -> Iterator[Tuple[ModelPatchRaw, float]]:
+ """Yield (ModelPatchRaw, weight) pairs for the given LoRA list.
+
+ The caller passes either ``transformer.loras`` (high-noise expert) or
+ ``transformer.loras_low_noise`` (low-noise expert) — the fallback to
+ the primary list when low-noise is empty is handled at the call site.
+ """
+ for lora_field in loras:
+ lora_info = context.models.load(lora_field.lora)
+ assert isinstance(lora_info.model, ModelPatchRaw), (
+ f"Wan LoRA model must be ModelPatchRaw, got {type(lora_info.model).__name__}"
+ )
+ yield (lora_info.model, lora_field.weight)
+ del lora_info
diff --git a/invokeai/app/invocations/wan_ideal_dimensions.py b/invokeai/app/invocations/wan_ideal_dimensions.py
new file mode 100644
index 00000000000..2f65c1e0f99
--- /dev/null
+++ b/invokeai/app/invocations/wan_ideal_dimensions.py
@@ -0,0 +1,207 @@
+"""Compute Wan 2.2-compatible pixel dimensions for a target short-side resolution.
+
+Wan's transformer ``patch_size=(1, 2, 2)`` adds a 2x patchify on top of the VAE's
+spatial compression, so pixel dimensions must be a multiple of (2 × VAE scale):
+
+- I2V-A14B / T2V (8x VAE) → multiples of 16
+- TI2V-5B (Wan 2.2-VAE, 16x VAE) → multiples of 32
+
+This module exposes one node per family. Each takes a source image's W×H and a
+target short-side preset (480p / 720p / 1080p) and returns the scaled, snapped
+(width, height) that can be fed directly into the matching ``wan_ref_image_encoder``
+/ ``wan_denoise`` inputs. Both nodes share :func:`_scale_and_snap`; they differ
+only in the pixel multiple they snap to.
+"""
+
+import math
+from typing import Literal
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, invocation
+from invokeai.app.invocations.fields import InputField
+from invokeai.app.invocations.ideal_size import IdealSizeOutput
+from invokeai.app.services.shared.invocation_context import InvocationContext
+
+WanTargetResolution = Literal["480p", "720p", "1080p"]
+WanRounding = Literal["nearest", "floor", "ceiling"]
+
+# Pixel-grid multiple = 2 (transformer patch) × VAE spatial scale. The 8x-VAE
+# I2V/T2V models need multiples of 16; the 16x-VAE TI2V-5B needs multiples of 32.
+WAN_I2V_PIXEL_MULTIPLE = 16
+WAN_TI2V_PIXEL_MULTIPLE = 32
+
+# Short-side pixel count for each preset. "p" notation is by convention the *short*
+# dimension in modern video (so a portrait 720p video is 720 wide × 1280 tall).
+WAN_TARGET_RESOLUTION_PX: dict[str, int] = {
+ "480p": 480,
+ "720p": 720,
+ "1080p": 1080,
+}
+
+WAN_TARGET_RESOLUTION_LABELS: dict[str, str] = {
+ "480p": "480p (Wan native)",
+ "720p": "720p (Wan native, default)",
+ "1080p": "1080p (extrapolated — not a Wan training size)",
+}
+
+
+def _scale_and_snap(
+ width: int,
+ height: int,
+ target_short_side: int,
+ rounding: WanRounding,
+ multiple: int,
+) -> tuple[int, int]:
+ """Scale a source W×H so its shorter side equals ``target_short_side``, then
+ snap each dimension to ``multiple`` using the requested rounding mode.
+
+ ``multiple`` is the Wan pixel-grid constraint (16 for the 8x-VAE I2V/T2V
+ models, 32 for the 16x-VAE TI2V-5B). Shared by both ideal-dimensions nodes.
+ """
+ short = min(width, height)
+ if short <= 0:
+ raise ValueError("Source dimensions must be positive.")
+
+ # Reject sources so narrow that the scaled long side is still under one Wan
+ # pixel grid. The downstream clamp to ``max(w, multiple)`` would otherwise
+ # silently return multiple×multiple, which has no relation to the requested
+ # aspect ratio — better to fail fast and have the workflow author fix inputs.
+ long_side = max(width, height)
+ if long_side < multiple:
+ raise ValueError(
+ f"Source longer side ({long_side}px) is smaller than the Wan pixel grid ({multiple}px). "
+ f"Use an input image at least {multiple}px on its longer side."
+ )
+
+ scale = target_short_side / short
+ raw_w = width * scale
+ raw_h = height * scale
+
+ if rounding == "floor":
+ w = int(raw_w // multiple) * multiple
+ h = int(raw_h // multiple) * multiple
+ elif rounding == "ceiling":
+ w = int(math.ceil(raw_w / multiple)) * multiple
+ h = int(math.ceil(raw_h / multiple)) * multiple
+ else: # nearest
+ w = round(raw_w / multiple) * multiple
+ h = round(raw_h / multiple) * multiple
+
+ # Belt-and-suspenders clamp against floor-of- IdealSizeOutput:
+ target_short_side = WAN_TARGET_RESOLUTION_PX[self.target_resolution]
+ w, h = _scale_and_snap(
+ self.width, self.height, target_short_side, self.rounding, multiple=WAN_I2V_PIXEL_MULTIPLE
+ )
+ return IdealSizeOutput(width=w, height=h)
+
+
+@invocation(
+ "wan_ti2v_ideal_dimensions",
+ title="Wan 2.2 TI2V Ideal Dimensions (5B)",
+ tags=["wan", "video", "dimensions", "math"],
+ category="video",
+ version="1.0.0",
+)
+class WanTI2VIdealDimensionsInvocation(BaseInvocation):
+ """Ideal dimensions for the Wan 2.2 TI2V-5B model.
+
+ Use this node for TI2V-5B only. For the A14B models (I2V-A14B / T2V-A14B) use
+ "Wan 2.2 I2V Ideal Dimensions" instead — those need multiples of 16, and this
+ node's multiples-of-32 dims would overshoot their pixel grid.
+
+ Identical to the A14B node but snaps each dimension to a multiple of 32 instead
+ of 16: the Wan 2.2-VAE used by TI2V-5B applies 16x spatial compression and the
+ transformer adds a 2x patch on top, so pixel dims must divide by 32 for the
+ patchify step. Wire from ``Image Primitive``'s width/height outputs and into
+ the matching ``wan_denoise`` inputs.
+ """
+
+ width: int = InputField(
+ default=1024,
+ gt=0,
+ description="Source image width in pixels.",
+ )
+ height: int = InputField(
+ default=1024,
+ gt=0,
+ description="Source image height in pixels.",
+ )
+ target_resolution: WanTargetResolution = InputField(
+ default="720p",
+ description=(
+ "Short-side resolution preset. 480p and 720p are Wan 2.2's native training "
+ "resolutions; 1080p works but is extrapolation and costs ~2.25x the memory "
+ "of 720p."
+ ),
+ ui_choice_labels=WAN_TARGET_RESOLUTION_LABELS,
+ )
+ rounding: WanRounding = InputField(
+ default="nearest",
+ description=(
+ "How to snap each dimension to a multiple of 32. 'floor' rounds down — "
+ "safest for VRAM, guaranteed not to exceed the unsnapped target. "
+ "'ceiling' rounds up. 'nearest' minimizes aspect-ratio drift (default)."
+ ),
+ )
+
+ def invoke(self, context: InvocationContext) -> IdealSizeOutput:
+ target_short_side = WAN_TARGET_RESOLUTION_PX[self.target_resolution]
+ w, h = _scale_and_snap(
+ self.width, self.height, target_short_side, self.rounding, multiple=WAN_TI2V_PIXEL_MULTIPLE
+ )
+ return IdealSizeOutput(width=w, height=h)
diff --git a/invokeai/app/invocations/wan_image_to_latents.py b/invokeai/app/invocations/wan_image_to_latents.py
new file mode 100644
index 00000000000..d5827110d0a
--- /dev/null
+++ b/invokeai/app/invocations/wan_image_to_latents.py
@@ -0,0 +1,104 @@
+"""Wan 2.2 image-to-latents invocation.
+
+Encodes an image to latent space using the Wan VAE (AutoencoderKLWan). The Wan
+VAE expects 5D ``[B, C, T, H, W]`` input with ``T=1`` for single images. After
+encoding, latents are normalised against the per-channel ``latents_mean`` and
+``latents_std`` stored in the VAE config — this matches the Diffusers
+``WanPipeline`` reference and is the inverse of the denormalisation in
+``wan_latents_to_image.py``.
+"""
+
+import einops
+import torch
+from diffusers.models.autoencoders import AutoencoderKLWan
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ FieldDescriptions,
+ ImageField,
+ Input,
+ InputField,
+ WithBoard,
+ WithMetadata,
+)
+from invokeai.app.invocations.model import VAEField
+from invokeai.app.invocations.primitives import LatentsOutput
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.model_manager.load.load_base import LoadedModel
+from invokeai.backend.stable_diffusion.diffusers_pipeline import image_resized_to_grid_as_tensor
+from invokeai.backend.util.devices import TorchDevice
+from invokeai.backend.util.vae_working_memory import estimate_vae_working_memory_flux
+
+
+@invocation(
+ "wan_i2l",
+ title="Image to Latents - Wan 2.2",
+ tags=["image", "latents", "vae", "i2l", "wan"],
+ category="image",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanImageToLatentsInvocation(BaseInvocation, WithMetadata, WithBoard):
+ """Encodes an image with the Wan VAE (AutoencoderKLWan).
+
+ The output latents have the temporal dimension squeezed out, so downstream
+ nodes see 4D ``[B, C, H, W]``. The denoise loop re-adds ``T=1`` before
+ feeding the transformer.
+ """
+
+ image: ImageField = InputField(description="The image to encode.")
+ vae: VAEField = InputField(description=FieldDescriptions.vae, input=Input.Connection)
+
+ @staticmethod
+ def vae_encode(vae_info: LoadedModel, image_tensor: torch.Tensor) -> torch.Tensor:
+ if not isinstance(vae_info.model, AutoencoderKLWan):
+ raise TypeError(f"Expected AutoencoderKLWan for Wan VAE, got {type(vae_info.model).__name__}.")
+
+ estimated_working_memory = estimate_vae_working_memory_flux(
+ operation="encode",
+ image_tensor=image_tensor,
+ vae=vae_info.model,
+ )
+
+ with vae_info.model_on_device(working_mem_bytes=estimated_working_memory) as (_, vae):
+ assert isinstance(vae, AutoencoderKLWan)
+
+ vae_dtype = next(iter(vae.parameters())).dtype
+ image_tensor = image_tensor.to(device=TorchDevice.choose_torch_device(), dtype=vae_dtype)
+
+ with torch.inference_mode():
+ # Wan VAE expects 5D [B, C, T, H, W].
+ if image_tensor.ndim == 4:
+ image_tensor = image_tensor.unsqueeze(2) # [B, C, H, W] -> [B, C, 1, H, W]
+
+ encoded = vae.encode(image_tensor, return_dict=False)[0]
+ latents = encoded.sample().to(dtype=vae_dtype)
+
+ # Normalise to the denoiser's expected zero-centred space:
+ # (latents - mean) / std
+ latents_mean = torch.tensor(vae.config.latents_mean).view(1, -1, 1, 1, 1).to(latents)
+ latents_std = torch.tensor(vae.config.latents_std).view(1, -1, 1, 1, 1).to(latents)
+ latents = (latents - latents_mean) / latents_std
+
+ # Drop the temporal dim to keep the rest of the InvokeAI pipeline 4D.
+ if latents.ndim == 5:
+ latents = latents.squeeze(2)
+
+ return latents
+
+ @torch.no_grad()
+ def invoke(self, context: InvocationContext) -> LatentsOutput:
+ image = context.images.get_pil(self.image.image_name)
+
+ image_tensor = image_resized_to_grid_as_tensor(image.convert("RGB"))
+ if image_tensor.dim() == 3:
+ image_tensor = einops.rearrange(image_tensor, "c h w -> 1 c h w")
+
+ vae_info = context.models.load(self.vae.vae)
+
+ context.util.signal_progress("Running Wan VAE encode")
+ latents = self.vae_encode(vae_info=vae_info, image_tensor=image_tensor)
+
+ latents = latents.to("cpu")
+ name = context.tensors.save(tensor=latents)
+ return LatentsOutput.build(latents_name=name, latents=latents, seed=None)
diff --git a/invokeai/app/invocations/wan_latents_to_image.py b/invokeai/app/invocations/wan_latents_to_image.py
new file mode 100644
index 00000000000..049959646c1
--- /dev/null
+++ b/invokeai/app/invocations/wan_latents_to_image.py
@@ -0,0 +1,93 @@
+"""Wan 2.2 latents-to-image invocation.
+
+Decodes Wan latents using the Wan VAE (AutoencoderKLWan).
+
+Latents from the denoise loop are in normalised space (zero-centred). Before
+VAE decode they are denormalised using the VAE config's per-channel
+``latents_mean`` / ``latents_std`` (matching Diffusers ``WanPipeline``).
+
+The VAE expects 5D ``[B, C, T, H, W]``; downstream nodes work with 4D, so this
+node re-adds ``T=1`` before decode and squeezes it back out afterwards.
+"""
+
+import torch
+from diffusers.models.autoencoders import AutoencoderKLWan
+from einops import rearrange
+from PIL import Image
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ FieldDescriptions,
+ Input,
+ InputField,
+ LatentsField,
+ WithBoard,
+ WithMetadata,
+)
+from invokeai.app.invocations.model import VAEField
+from invokeai.app.invocations.primitives import ImageOutput
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.util.devices import TorchDevice
+from invokeai.backend.util.vae_working_memory import estimate_vae_working_memory_flux
+
+
+@invocation(
+ "wan_l2i",
+ title="Latents to Image - Wan 2.2",
+ tags=["latents", "image", "vae", "l2i", "wan"],
+ category="latents",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanLatentsToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
+ """Decodes Wan latents back to RGB."""
+
+ latents: LatentsField = InputField(description=FieldDescriptions.latents, input=Input.Connection)
+ vae: VAEField = InputField(description=FieldDescriptions.vae, input=Input.Connection)
+
+ @torch.no_grad()
+ def invoke(self, context: InvocationContext) -> ImageOutput:
+ latents = context.tensors.load(self.latents.latents_name)
+
+ vae_info = context.models.load(self.vae.vae)
+ if not isinstance(vae_info.model, AutoencoderKLWan):
+ raise TypeError(f"Expected AutoencoderKLWan for Wan VAE, got {type(vae_info.model).__name__}.")
+
+ estimated_working_memory = estimate_vae_working_memory_flux(
+ operation="decode",
+ image_tensor=latents,
+ vae=vae_info.model,
+ )
+
+ with vae_info.model_on_device(working_mem_bytes=estimated_working_memory) as (_, vae):
+ context.util.signal_progress("Running Wan VAE decode")
+ assert isinstance(vae, AutoencoderKLWan)
+
+ vae_dtype = next(iter(vae.parameters())).dtype
+ latents = latents.to(device=TorchDevice.choose_torch_device(), dtype=vae_dtype)
+
+ TorchDevice.empty_cache()
+
+ with torch.inference_mode():
+ # Re-add the temporal dim if upstream squeezed it out.
+ if latents.ndim == 4:
+ latents = latents.unsqueeze(2)
+
+ # Denormalise from denoiser space back to raw VAE space.
+ latents_mean = torch.tensor(vae.config.latents_mean).view(1, -1, 1, 1, 1).to(latents)
+ latents_std = torch.tensor(vae.config.latents_std).view(1, -1, 1, 1, 1).to(latents)
+ latents = latents * latents_std + latents_mean
+
+ decoded = vae.decode(latents, return_dict=False)[0]
+
+ if decoded.ndim == 5:
+ decoded = decoded.squeeze(2)
+
+ img = decoded.clamp(-1, 1)
+ img = rearrange(img[0], "c h w -> h w c")
+ img_pil = Image.fromarray((127.5 * (img + 1.0)).byte().cpu().numpy())
+
+ TorchDevice.empty_cache()
+
+ image_dto = context.images.save(image=img_pil)
+ return ImageOutput.build(image_dto)
diff --git a/invokeai/app/invocations/wan_latents_to_video.py b/invokeai/app/invocations/wan_latents_to_video.py
new file mode 100644
index 00000000000..ca6ffc104a6
--- /dev/null
+++ b/invokeai/app/invocations/wan_latents_to_video.py
@@ -0,0 +1,163 @@
+"""Wan 2.2 latents-to-video invocation.
+
+Decodes multi-frame Wan latents with the Wan VAE and encodes the result to an
+MP4 file via :mod:`imageio` (backed by the bundled FFmpeg binary from
+``imageio-ffmpeg``). The video is then persisted through ``context.videos.save``,
+which moves the temp file into ``outputs/videos/`` and records the DTO.
+
+Latent shape on input is 5D ``[B, C, T_lat, H_lat, W_lat]`` (typically B=1).
+The VAE expands the temporal dim by 4× during decode minus the initial offset:
+``T_pixel = (T_lat - 1) * 4 + 1`` (e.g. T_lat=21 → 81 pixel frames).
+"""
+
+import tempfile
+from pathlib import Path
+from typing import Callable, Protocol
+
+import imageio.v2 as iio2
+import numpy as np
+import torch
+from diffusers.models.autoencoders import AutoencoderKLWan
+from einops import rearrange
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ FieldDescriptions,
+ Input,
+ InputField,
+ LatentsField,
+ WithBoard,
+ WithMetadata,
+)
+from invokeai.app.invocations.model import VAEField
+from invokeai.app.invocations.primitives import VideoOutput
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.util.devices import TorchDevice
+
+
+class _FrameWriter(Protocol):
+ def append_data(self, frame: np.ndarray) -> None: ...
+
+
+def _write_video_frames(writer: _FrameWriter, frames: np.ndarray, is_canceled: Callable[[], bool]) -> None:
+ for frame in frames:
+ if is_canceled():
+ raise CanceledException
+ writer.append_data(np.ascontiguousarray(frame))
+
+
+@invocation(
+ "wan_l2v",
+ title="Latents to Video - Wan 2.2",
+ tags=["latents", "video", "vae", "l2v", "wan"],
+ category="latents",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanLatentsToVideoInvocation(BaseInvocation, WithMetadata, WithBoard):
+ """Decode 5D Wan latents to RGB frames and encode an MP4."""
+
+ latents: LatentsField = InputField(description=FieldDescriptions.latents, input=Input.Connection)
+ vae: VAEField = InputField(description=FieldDescriptions.vae, input=Input.Connection)
+ fps: int = InputField(
+ default=16,
+ ge=1,
+ le=120,
+ description="Frames-per-second for the encoded MP4. Wan 2.2 was trained at 16 FPS.",
+ )
+
+ @torch.no_grad()
+ def invoke(self, context: InvocationContext) -> VideoOutput:
+ latents = context.tensors.load(self.latents.latents_name)
+ if latents.ndim == 4:
+ # Promote 4D (single-frame) to 5D so this node can also serve as a
+ # one-frame "video" encode if someone wires it that way.
+ latents = latents.unsqueeze(2)
+ if latents.ndim != 5:
+ raise ValueError(
+ f"Wan latents-to-video expects a 5D latent tensor [B, C, T, H, W]; got {tuple(latents.shape)}."
+ )
+
+ vae_info = context.models.load(self.vae.vae)
+ if not isinstance(vae_info.model, AutoencoderKLWan):
+ raise TypeError(f"Expected AutoencoderKLWan for Wan VAE, got {type(vae_info.model).__name__}.")
+
+ with vae_info.model_on_device() as (_, vae):
+ assert isinstance(vae, AutoencoderKLWan)
+ _, _, t_lat, h_lat, w_lat = latents.shape
+ t_pixel = (t_lat - 1) * 4 + 1
+ context.logger.info(
+ f"Running Wan VAE decode: {t_lat} latent frames -> {t_pixel} pixel frames at {w_lat * 8}x{h_lat * 8}"
+ )
+ context.util.signal_progress("Running Wan VAE decode (video)")
+
+ vae_dtype = next(iter(vae.parameters())).dtype
+ latents = latents.to(device=TorchDevice.choose_torch_device(), dtype=vae_dtype)
+
+ TorchDevice.empty_cache()
+
+ with torch.inference_mode():
+ # Denormalise from denoiser space back to VAE space.
+ latents_mean = torch.tensor(vae.config.latents_mean).view(1, -1, 1, 1, 1).to(latents)
+ latents_std = torch.tensor(vae.config.latents_std).view(1, -1, 1, 1, 1).to(latents)
+ latents = latents * latents_std + latents_mean
+
+ # [B, C=3, T_pixel, H, W] in [-1, 1] (roughly).
+ decoded = vae.decode(latents, return_dict=False)[0]
+
+ decoded = decoded.clamp(-1, 1)
+ # Take batch 0 (we generate one video at a time).
+ decoded = decoded[0] # [C, T, H, W]
+
+ TorchDevice.empty_cache()
+
+ if context.util.is_canceled():
+ raise CanceledException
+
+ # Convert to a list of numpy uint8 frames [H, W, C].
+ decoded = rearrange(decoded, "c t h w -> t h w c")
+ # [-1, 1] -> [0, 255]
+ frames = (127.5 * (decoded.cpu().float() + 1.0)).round().clamp(0, 255).byte().numpy()
+ if frames.shape[0] == 0:
+ raise ValueError("Wan VAE decode produced zero frames.")
+
+ height, width = frames[0].shape[:2]
+ num_frames = frames.shape[0]
+ duration = num_frames / float(self.fps)
+
+ # Encode to a temporary MP4 via imageio's FFMPEG plugin (backed by the
+ # bundled imageio-ffmpeg binary). libx264 + yuv420p is the default for
+ # this plugin, which is what we want for broadly-compatible browser
+ # playback — no need to override.
+ tmp = tempfile.NamedTemporaryFile(prefix="invokeai_wan_video_", suffix=".mp4", delete=False)
+ tmp.close()
+ tmp_path = Path(tmp.name)
+ try:
+ context.logger.info(
+ f"Encoding MP4: {num_frames} frames @ {self.fps} fps ({duration:.2f}s) at {width}x{height} via libx264"
+ )
+ context.util.signal_progress(f"Encoding MP4 ({num_frames} frames @ {self.fps} fps)")
+ writer = iio2.get_writer(str(tmp_path), format="FFMPEG", mode="I", codec="libx264", fps=self.fps)
+ try:
+ _write_video_frames(writer, frames, context.util.is_canceled)
+ finally:
+ writer.close()
+ encoded_bytes = tmp_path.stat().st_size
+ context.logger.info(f"MP4 encode complete: {encoded_bytes / 1024:.1f} KB")
+ video_dto = context.videos.save(
+ source_path=tmp_path,
+ width=width,
+ height=height,
+ duration=duration,
+ fps=float(self.fps),
+ )
+ context.logger.info(f"Saved video: {video_dto.video_name}")
+ return VideoOutput.build(video_dto)
+ finally:
+ # If save() moved the file this is a no-op; if it failed earlier, we
+ # don't want a lingering temp file.
+ try:
+ tmp_path.unlink(missing_ok=True)
+ except Exception:
+ pass
diff --git a/invokeai/app/invocations/wan_lora_loader.py b/invokeai/app/invocations/wan_lora_loader.py
new file mode 100644
index 00000000000..ad0cff906ea
--- /dev/null
+++ b/invokeai/app/invocations/wan_lora_loader.py
@@ -0,0 +1,190 @@
+from typing import Literal, Optional
+
+from invokeai.app.invocations.baseinvocation import (
+ BaseInvocation,
+ BaseInvocationOutput,
+ Classification,
+ invocation,
+ invocation_output,
+)
+from invokeai.app.invocations.fields import FieldDescriptions, Input, InputField, OutputField
+from invokeai.app.invocations.model import LoRAField, ModelIdentifierField, WanTransformerField
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType
+
+# Target option for routing a LoRA to one or both Wan A14B expert lists.
+#
+# - ``auto``: read the LoRA config's ``expert`` field (set by the probe / from
+# filename). ``"high"`` -> primary list only, ``"low"`` -> low-noise list
+# only, ``None`` -> both lists.
+# - ``both``: append to both lists regardless of the config.
+# - ``high``: append only to the primary list (high-noise expert).
+# - ``low``: append only to the low-noise list (low-noise expert).
+WanLoRATarget = Literal["auto", "both", "high", "low"]
+
+
+def _resolve_target(target: WanLoRATarget, lora_expert: str | None) -> tuple[bool, bool]:
+ """Return (apply_to_primary, apply_to_low_noise) based on the requested
+ target and the LoRA's recorded expert tag."""
+ if target == "both":
+ return True, True
+ if target == "high":
+ return True, False
+ if target == "low":
+ return False, True
+ # auto
+ if lora_expert == "high":
+ return True, False
+ if lora_expert == "low":
+ return False, True
+ return True, True
+
+
+@invocation_output("wan_lora_loader_output")
+class WanLoRALoaderOutput(BaseInvocationOutput):
+ """Wan 2.2 LoRA loader output."""
+
+ transformer: Optional[WanTransformerField] = OutputField(
+ default=None, description=FieldDescriptions.transformer, title="Wan Transformer"
+ )
+
+
+@invocation(
+ "wan_lora_loader",
+ title="Apply LoRA - Wan 2.2",
+ tags=["lora", "model", "wan"],
+ category="model",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanLoRALoaderInvocation(BaseInvocation):
+ """Apply a LoRA to the Wan 2.2 transformer(s).
+
+ For A14B (dual expert) the LoRA's recorded ``expert`` field determines
+ which expert list it lands in: ``"high"`` -> primary list, ``"low"`` ->
+ low-noise list, ``None`` (untagged) -> both lists. Use the ``target``
+ field to override.
+
+ For TI2V-5B (single transformer) only the primary list is used at denoise
+ time; the low-noise routing is harmless but ignored.
+ """
+
+ lora: ModelIdentifierField = InputField(
+ description=FieldDescriptions.lora_model,
+ title="LoRA",
+ ui_model_base=BaseModelType.Wan,
+ ui_model_type=ModelType.LoRA,
+ )
+ weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight)
+ target: WanLoRATarget = InputField(
+ default="auto",
+ description="Which expert(s) to apply this LoRA to. 'auto' uses the LoRA's "
+ "recorded expert tag (or both if untagged); 'both'/'high'/'low' override it.",
+ )
+ transformer: WanTransformerField | None = InputField(
+ default=None,
+ description=FieldDescriptions.transformer,
+ input=Input.Connection,
+ title="Wan Transformer",
+ )
+
+ def invoke(self, context: InvocationContext) -> WanLoRALoaderOutput:
+ lora_key = self.lora.key
+
+ if not context.models.exists(lora_key):
+ raise ValueError(f"Unknown lora: {lora_key}!")
+
+ output = WanLoRALoaderOutput()
+ if self.transformer is None:
+ return output
+
+ lora_config = context.models.get_config(self.lora)
+ lora_expert = getattr(lora_config, "expert", None)
+ to_primary, to_low_noise = _resolve_target(self.target, lora_expert)
+
+ # Reject duplicates on whichever list(s) we're about to append to.
+ if to_primary and any(item.lora.key == lora_key for item in self.transformer.loras):
+ raise ValueError(f'LoRA "{lora_key}" already applied to primary transformer list.')
+ if to_low_noise and any(item.lora.key == lora_key for item in self.transformer.loras_low_noise):
+ raise ValueError(f'LoRA "{lora_key}" already applied to low-noise transformer list.')
+
+ output.transformer = self.transformer.model_copy(deep=True)
+ new_lora = LoRAField(lora=self.lora, weight=self.weight)
+ if to_primary:
+ output.transformer.loras.append(new_lora)
+ if to_low_noise:
+ output.transformer.loras_low_noise.append(new_lora)
+
+ return output
+
+
+@invocation(
+ "wan_lora_collection_loader",
+ title="Apply LoRA Collection - Wan 2.2",
+ tags=["lora", "model", "wan"],
+ category="model",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanLoRACollectionLoader(BaseInvocation):
+ """Apply a collection of LoRAs to the Wan 2.2 transformer(s).
+
+ Each LoRA is routed to the primary and/or low-noise list based on its
+ recorded ``expert`` tag (set by the probe from the filename). Untagged
+ LoRAs go to both lists.
+ """
+
+ loras: Optional[LoRAField | list[LoRAField]] = InputField(
+ default=None,
+ description="LoRAs to apply. May be a single LoRA or a collection.",
+ title="LoRAs",
+ ui_model_base=[BaseModelType.Wan],
+ ui_model_type=ModelType.LoRA,
+ )
+ transformer: Optional[WanTransformerField] = InputField(
+ default=None,
+ description=FieldDescriptions.transformer,
+ input=Input.Connection,
+ title="Wan Transformer",
+ )
+
+ def invoke(self, context: InvocationContext) -> WanLoRALoaderOutput:
+ output = WanLoRALoaderOutput()
+
+ if self.transformer is None:
+ return output
+
+ output.transformer = self.transformer.model_copy(deep=True)
+
+ if self.loras is None:
+ return output
+
+ loras = self.loras if isinstance(self.loras, list) else [self.loras]
+ added: set[str] = set()
+
+ for lora in loras:
+ if lora is None or lora.lora.key in added:
+ continue
+
+ if not context.models.exists(lora.lora.key):
+ raise ValueError(f"Unknown lora: {lora.lora.key}!")
+
+ if lora.lora.base is not BaseModelType.Wan:
+ raise ValueError(
+ f"LoRA '{lora.lora.key}' is for "
+ f"{lora.lora.base.value if lora.lora.base else 'unknown'} models, "
+ "not Wan 2.2."
+ )
+
+ lora_config = context.models.get_config(lora.lora)
+ lora_expert = getattr(lora_config, "expert", None)
+ to_primary, to_low_noise = _resolve_target("auto", lora_expert)
+
+ added.add(lora.lora.key)
+
+ if to_primary:
+ output.transformer.loras.append(lora)
+ if to_low_noise:
+ output.transformer.loras_low_noise.append(lora)
+
+ return output
diff --git a/invokeai/app/invocations/wan_model_loader.py b/invokeai/app/invocations/wan_model_loader.py
new file mode 100644
index 00000000000..a4d986d8aa3
--- /dev/null
+++ b/invokeai/app/invocations/wan_model_loader.py
@@ -0,0 +1,239 @@
+from typing import Optional
+
+from invokeai.app.invocations.baseinvocation import (
+ BaseInvocation,
+ BaseInvocationOutput,
+ Classification,
+ invocation,
+ invocation_output,
+)
+from invokeai.app.invocations.fields import FieldDescriptions, Input, InputField, OutputField
+from invokeai.app.invocations.model import (
+ ModelIdentifierField,
+ VAEField,
+ WanT5EncoderField,
+ WanTransformerField,
+)
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType, SubModelType
+
+
+@invocation_output("wan_model_loader_output")
+class WanModelLoaderOutput(BaseInvocationOutput):
+ """Wan 2.2 model loader output."""
+
+ transformer: WanTransformerField = OutputField(
+ description="Wan transformer (one or two experts depending on the variant)",
+ title="Transformer",
+ )
+ wan_t5_encoder: WanT5EncoderField = OutputField(
+ description=FieldDescriptions.wan_t5_encoder,
+ title="UMT5-XXL Encoder",
+ )
+ vae: VAEField = OutputField(description=FieldDescriptions.vae, title="VAE")
+
+
+@invocation(
+ "wan_model_loader",
+ title="Main Model - Wan 2.2",
+ tags=["model", "wan"],
+ category="model",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanModelLoaderInvocation(BaseInvocation):
+ """Loads a Wan 2.2 model, outputting its submodels.
+
+ Components can be mixed and matched, mirroring the Qwen Image loader pattern:
+
+ - Transformer(s):
+ * Diffusers main: emits ``transformer/`` and (for A14B) ``transformer_2/``
+ from the same model record.
+ * GGUF main: emits the single GGUF as the primary transformer; for A14B
+ the second-expert GGUF must be wired to ``Transformer (Low Noise)``.
+ - VAE: standalone Wan VAE > main (if Diffusers) > Component Source (Diffusers).
+ - UMT5-XXL encoder: standalone Wan T5 encoder > main (if Diffusers) >
+ Component Source (Diffusers).
+
+ The Component Source slot lets users supply a Diffusers Wan main model purely
+ for VAE / encoder extraction when the actual transformer is in a single-file
+ format. Together, the standalone VAE + standalone encoder let a GGUF
+ transformer run without a full ~30 GB Diffusers install.
+ """
+
+ model: ModelIdentifierField = InputField(
+ description=FieldDescriptions.wan_model,
+ input=Input.Direct,
+ ui_model_base=BaseModelType.Wan,
+ ui_model_type=ModelType.Main,
+ title="Transformer",
+ )
+
+ transformer_low_noise_model: Optional[ModelIdentifierField] = InputField(
+ default=None,
+ description="Optional second GGUF transformer for the A14B low-noise expert. "
+ "Only relevant when the main model is a single-file GGUF and the variant is A14B; "
+ "ignored when the main is a Diffusers A14B (both experts are pulled from "
+ "transformer/ and transformer_2/ already) or when the variant is TI2V-5B.",
+ input=Input.Direct,
+ ui_model_base=BaseModelType.Wan,
+ ui_model_type=ModelType.Main,
+ ui_model_format=ModelFormat.GGUFQuantized,
+ title="Transformer (Low Noise)",
+ )
+
+ vae_model: Optional[ModelIdentifierField] = InputField(
+ default=None,
+ description="Standalone Wan VAE model. If not set, the VAE is loaded from the main model "
+ "(when in Diffusers format) or from the Component Source.",
+ input=Input.Direct,
+ ui_model_base=BaseModelType.Wan,
+ ui_model_type=ModelType.VAE,
+ title="VAE",
+ )
+
+ wan_t5_encoder_model: Optional[ModelIdentifierField] = InputField(
+ default=None,
+ description="Standalone Wan UMT5-XXL encoder. If not set, the encoder is loaded from the main "
+ "model (when in Diffusers format) or from the Component Source.",
+ input=Input.Direct,
+ ui_model_type=ModelType.WanT5Encoder,
+ title="Wan T5 Encoder",
+ )
+
+ component_source: Optional[ModelIdentifierField] = InputField(
+ default=None,
+ description="Diffusers Wan main model to extract VAE and/or encoder from. "
+ "Use this if you don't have separate VAE/encoder models. "
+ "Ignored for any submodel that is provided separately.",
+ input=Input.Direct,
+ ui_model_base=BaseModelType.Wan,
+ ui_model_type=ModelType.Main,
+ ui_model_format=ModelFormat.Diffusers,
+ title="Component Source (Diffusers)",
+ )
+
+ def invoke(self, context: InvocationContext) -> WanModelLoaderOutput:
+ main_config = context.models.get_config(self.model)
+ main_format = main_config.format
+ main_is_diffusers = main_format == ModelFormat.Diffusers
+ main_is_gguf = main_format == ModelFormat.GGUFQuantized
+
+ # Resolve transformer + dual-expert wiring + boundary_ratio.
+ #
+ # Diffusers main: transformer/ is the primary, transformer_2/ is the
+ # low-noise expert (A14B only). boundary_ratio comes from the probed
+ # model_index.json.
+ #
+ # GGUF main: the file itself is one expert (high or low). For A14B,
+ # the user wires the other expert to transformer_low_noise_model.
+ # We swap so the *high*-noise expert is always the primary if needed.
+ # boundary_ratio falls back to 0.875 unless a Diffusers component_source
+ # provides a recorded value.
+ boundary_ratio = 0.875
+ transformer_low_noise: Optional[ModelIdentifierField] = None
+
+ if main_is_diffusers:
+ transformer = self.model.model_copy(update={"submodel_type": SubModelType.Transformer})
+ if getattr(main_config, "has_dual_expert", False):
+ transformer_low_noise = self.model.model_copy(update={"submodel_type": SubModelType.Transformer2})
+ recorded = getattr(main_config, "boundary_ratio", None)
+ if recorded is not None:
+ boundary_ratio = float(recorded)
+ elif main_is_gguf:
+ primary_expert = getattr(main_config, "expert", "none")
+ primary_id = self.model.model_copy(update={"submodel_type": SubModelType.Transformer})
+
+ if self.transformer_low_noise_model is not None:
+ low_config = context.models.get_config(self.transformer_low_noise_model)
+ if low_config.format != ModelFormat.GGUFQuantized:
+ raise ValueError(
+ f"'Transformer (Low Noise)' must be a GGUF-format Wan model. "
+ f"'{low_config.name}' is in {low_config.format.value} format."
+ )
+ low_id = self.transformer_low_noise_model.model_copy(update={"submodel_type": SubModelType.Transformer})
+ low_expert = getattr(low_config, "expert", "none")
+
+ # Make sure 'transformer' is the high-noise expert and
+ # 'transformer_low_noise' is the low-noise expert. If the user
+ # accidentally swapped them, swap back.
+ if primary_expert == "low" and low_expert == "high":
+ transformer = low_id
+ transformer_low_noise = primary_id
+ else:
+ transformer = primary_id
+ transformer_low_noise = low_id
+ else:
+ transformer = primary_id
+ # A14B without a paired low-noise GGUF will produce degraded
+ # quality (only the high-noise expert runs). Warn but don't
+ # abort — TI2V-5B GGUFs are single-expert and totally fine.
+ if getattr(main_config, "variant", None) and main_config.variant.value == "t2v_a14b":
+ context.logger.warning(
+ "A14B GGUF main was provided without a paired 'Transformer (Low Noise)'. "
+ "Only the high-noise expert will run; image quality will be reduced."
+ )
+
+ # Borrow the boundary_ratio recorded on the optional Diffusers
+ # component_source, when one is wired.
+ if self.component_source is not None:
+ src_cfg = context.models.get_config(self.component_source)
+ src_boundary = getattr(src_cfg, "boundary_ratio", None)
+ if src_boundary is not None:
+ boundary_ratio = float(src_boundary)
+ else:
+ raise ValueError(
+ f"Unsupported main model format for Wan: {main_format.value}. "
+ "Use a Diffusers folder or a GGUF single-file checkpoint."
+ )
+
+ # VAE: standalone override > main (if Diffusers) > component source.
+ if self.vae_model is not None:
+ vae = self.vae_model.model_copy(update={"submodel_type": SubModelType.VAE})
+ elif main_is_diffusers:
+ vae = self.model.model_copy(update={"submodel_type": SubModelType.VAE})
+ elif self.component_source is not None:
+ self._validate_component_source_format(context, self.component_source)
+ vae = self.component_source.model_copy(update={"submodel_type": SubModelType.VAE})
+ else:
+ raise ValueError(
+ "No source for VAE. Either set 'VAE' to a standalone Wan VAE, "
+ "or set 'Component Source' to a Diffusers Wan main model."
+ )
+
+ # Tokenizer + text encoder: standalone override > main (if Diffusers) > component source.
+ if self.wan_t5_encoder_model is not None:
+ tokenizer = self.wan_t5_encoder_model.model_copy(update={"submodel_type": SubModelType.Tokenizer})
+ text_encoder = self.wan_t5_encoder_model.model_copy(update={"submodel_type": SubModelType.TextEncoder})
+ elif main_is_diffusers:
+ tokenizer = self.model.model_copy(update={"submodel_type": SubModelType.Tokenizer})
+ text_encoder = self.model.model_copy(update={"submodel_type": SubModelType.TextEncoder})
+ elif self.component_source is not None:
+ self._validate_component_source_format(context, self.component_source)
+ tokenizer = self.component_source.model_copy(update={"submodel_type": SubModelType.Tokenizer})
+ text_encoder = self.component_source.model_copy(update={"submodel_type": SubModelType.TextEncoder})
+ else:
+ raise ValueError(
+ "No source for Wan T5 encoder. "
+ "Either set 'Wan T5 Encoder' to a standalone UMT5-XXL encoder, "
+ "or set 'Component Source' to a Diffusers Wan main model."
+ )
+
+ return WanModelLoaderOutput(
+ transformer=WanTransformerField(
+ transformer=transformer,
+ transformer_low_noise=transformer_low_noise,
+ boundary_ratio=boundary_ratio,
+ ),
+ wan_t5_encoder=WanT5EncoderField(tokenizer=tokenizer, text_encoder=text_encoder),
+ vae=VAEField(vae=vae),
+ )
+
+ @staticmethod
+ def _validate_component_source_format(context: InvocationContext, model: ModelIdentifierField) -> None:
+ source_config = context.models.get_config(model)
+ if source_config.format != ModelFormat.Diffusers:
+ raise ValueError(
+ f"The Component Source model must be in Diffusers format. "
+ f"The selected model '{source_config.name}' is in {source_config.format.value} format."
+ )
diff --git a/invokeai/app/invocations/wan_ref_image_encoder.py b/invokeai/app/invocations/wan_ref_image_encoder.py
new file mode 100644
index 00000000000..1fc9012ad68
--- /dev/null
+++ b/invokeai/app/invocations/wan_ref_image_encoder.py
@@ -0,0 +1,184 @@
+"""Reference-image (VAE-latent) encoder for Wan 2.2 I2V-A14B.
+
+Wan 2.2 I2V conditions on a reference image by VAE-encoding it and
+concatenating the resulting latents to the noise latents along the channel
+dim. This invocation produces the 20-channel condition tensor (4-ch first-
+frame mask + 16-ch image latents) the denoise loop will consume.
+
+Supports both single-frame (image I2V, ``num_frames=1``) and multi-frame
+(video I2V, e.g. ``num_frames=81``) condition tensors.
+"""
+
+from typing import Optional
+
+import torch
+from diffusers.models.autoencoders import AutoencoderKLWan
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ FieldDescriptions,
+ ImageField,
+ Input,
+ InputField,
+)
+from invokeai.app.invocations.model import VAEField
+from invokeai.app.invocations.primitives import WanRefImageOutput
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.util.devices import TorchDevice
+from invokeai.backend.wan.extensions.wan_ref_image_extension import (
+ encode_reference_image_to_condition,
+ encode_reference_image_to_ti2v_condition,
+ encode_reference_image_to_video_condition,
+)
+
+
+@invocation(
+ "wan_ref_image_encoder",
+ title="Reference Image - Wan 2.2",
+ tags=["image", "conditioning", "wan", "i2v"],
+ category="conditioning",
+ version="1.2.0",
+ classification=Classification.Prototype,
+)
+class WanRefImageEncoderInvocation(BaseInvocation):
+ """VAE-encode a reference image into Wan 2.2 I2V conditioning.
+
+ Output is a ``[1, 20, T_lat, height // 8, width // 8]`` condition tensor
+ that the denoise loop concatenates to the 16-channel noise latents each
+ step, producing the 36-channel input the I2V-A14B transformer expects.
+
+ For image (single-frame) I2V leave ``num_frames=1`` (T_lat=1). For video
+ I2V set ``num_frames`` to match the value on the video-denoise node
+ (e.g. 81 for the Wan 2.2 reference defaults).
+
+ Supply an optional ``end_image`` for **first-last-frame interpolation
+ (FLF2V)** — the model then interpolates the motion from ``image`` (first
+ frame) to ``end_image`` (final frame). FLF2V is I2V-A14B video only
+ (``num_frames > 1``); it is not supported for TI2V-5B or single-frame I2V.
+
+ Only works with I2V-A14B (the denoise loop's variant gate enforces this).
+ For T2V or TI2V-5B, omit this node entirely.
+ """
+
+ image: ImageField = InputField(description="Reference image to condition on (the first frame).")
+ vae: VAEField = InputField(description=FieldDescriptions.vae, input=Input.Connection, title="VAE")
+ # Must match wan_denoise's width/height. multiple_of=16 (not 8) because
+ # Wan's transformer patch_size=(1, 2, 2) needs latent H/W to be even.
+ width: int = InputField(
+ default=1024,
+ multiple_of=16,
+ description="Width to resize the reference image to (must match denoise width).",
+ )
+ height: int = InputField(
+ default=1024,
+ multiple_of=16,
+ description="Height to resize the reference image to (must match denoise height).",
+ )
+ num_frames: int = InputField(
+ default=1,
+ ge=1,
+ description="Pixel-frame count to build the condition for. Use 1 for single-frame image "
+ "I2V. For video I2V, set this to match the video-denoise node's num_frames (and ensure "
+ "(num_frames - 1) %% 4 == 0, e.g. 81).",
+ title="Number of Frames",
+ )
+ end_image: Optional[ImageField] = InputField(
+ default=None,
+ description="Optional end frame for first-last-frame interpolation (FLF2V). When set, the "
+ "video interpolates from the reference image (first frame) to this image (final frame). "
+ "I2V-A14B video only (num_frames > 1); not supported for TI2V-5B or single-frame I2V.",
+ title="End Image (FLF2V)",
+ )
+
+ @torch.no_grad()
+ def invoke(self, context: InvocationContext) -> WanRefImageOutput:
+ if self.num_frames > 1 and (self.num_frames - 1) % 4 != 0:
+ raise ValueError(
+ f"num_frames must satisfy (num_frames - 1) %% 4 == 0 for the Wan VAE's temporal "
+ f"compression (got {self.num_frames}). Try 5, 9, 13, ..., 81, 85, ..."
+ )
+
+ pil_image = context.images.get_pil(self.image.image_name, "RGB")
+ end_pil_image = context.images.get_pil(self.end_image.image_name, "RGB") if self.end_image is not None else None
+
+ vae_info = context.models.load(self.vae.vae)
+ device = TorchDevice.choose_torch_device()
+ target_dtype = TorchDevice.choose_bfloat16_safe_dtype(device)
+
+ with vae_info.model_on_device() as (_, vae):
+ if not isinstance(vae, AutoencoderKLWan):
+ raise TypeError(f"Reference-image encoder requires AutoencoderKLWan, got {type(vae).__name__}.")
+ context.util.signal_progress(
+ ("VAE-encoding FLF2V start+end images" if end_pil_image is not None else "VAE-encoding reference image")
+ + (f" ({self.num_frames} frames)" if self.num_frames > 1 else "")
+ )
+ # Free cached allocator blocks left over from earlier nodes (denoise expert
+ # swaps in particular can leave the cache fragmented in ways that look like
+ # free VRAM but fail a single large contiguous request). Mirrors the
+ # pattern used in wan_latents_to_image.py / wan_latents_to_video.py.
+ TorchDevice.empty_cache()
+ # Pick the encoder path by VAE z_dim: 48 means the Wan 2.2-VAE (TI2V-5B),
+ # which uses a single-frame 48-channel condition that the denoise loop
+ # blends with the noisy latents at every step (expand_timesteps path).
+ # 16 means the standard Wan VAE (A14B), which uses the 20-channel
+ # mask + latent condition concatenated to noise along the channel dim.
+ is_ti2v_5b = getattr(vae.config, "z_dim", 16) == 48
+ if end_pil_image is not None and (is_ti2v_5b or self.num_frames <= 1):
+ raise ValueError(
+ "End-image (FLF2V) interpolation is only supported for I2V-A14B video "
+ f"(num_frames > 1). Got {'TI2V-5B' if is_ti2v_5b else 'single-frame I2V'}. "
+ "Remove the End Image input, or use an A14B VAE with num_frames > 1."
+ )
+ if is_ti2v_5b:
+ # TI2V-5B I2V needs latent H/W to be even for the transformer
+ # patch_size=(1,2,2), so pixel dims must be multiples of 32
+ # (16x VAE * 2 transformer patch). A14B's 8x VAE only needed
+ # multiples of 16.
+ if self.width % 32 != 0 or self.height % 32 != 0:
+ raise ValueError(
+ f"TI2V-5B I2V requires width and height to be multiples of 32 "
+ f"(got {self.width}x{self.height}). The Wan 2.2-VAE uses 16x "
+ f"spatial compression and the transformer adds a 2x patch on "
+ f"top, so pixel dims must divide by 32 for the patchify step."
+ )
+ condition = encode_reference_image_to_ti2v_condition(
+ image=pil_image,
+ vae=vae,
+ width=self.width,
+ height=self.height,
+ device=device,
+ dtype=target_dtype,
+ )
+ elif self.num_frames <= 1:
+ condition = encode_reference_image_to_condition(
+ image=pil_image,
+ vae=vae,
+ width=self.width,
+ height=self.height,
+ device=device,
+ dtype=target_dtype,
+ )
+ else:
+ condition = encode_reference_image_to_video_condition(
+ image=pil_image,
+ vae=vae,
+ width=self.width,
+ height=self.height,
+ num_frames=self.num_frames,
+ device=device,
+ dtype=target_dtype,
+ last_image=end_pil_image,
+ )
+
+ condition = condition.detach().to("cpu")
+ # Release this node's VAE-encode intermediates before the next node tries to
+ # partial-load the denoise transformer — the OOM we saw in PR #9163 review
+ # was the I2V expert load racing against still-cached encode activations.
+ TorchDevice.empty_cache()
+ name = context.tensors.save(tensor=condition)
+ return WanRefImageOutput.build(
+ condition_tensor_name=name,
+ width=self.width,
+ height=self.height,
+ num_frames=self.num_frames,
+ )
diff --git a/invokeai/app/invocations/wan_text_encoder.py b/invokeai/app/invocations/wan_text_encoder.py
new file mode 100644
index 00000000000..396819d5434
--- /dev/null
+++ b/invokeai/app/invocations/wan_text_encoder.py
@@ -0,0 +1,112 @@
+import torch
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ FieldDescriptions,
+ Input,
+ InputField,
+ UIComponent,
+)
+from invokeai.app.invocations.model import WanT5EncoderField
+from invokeai.app.invocations.primitives import WanConditioningOutput
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.model_manager.load.model_cache.utils import get_effective_device
+from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
+ ConditioningFieldData,
+ WanConditioningInfo,
+)
+
+# Wan models are trained with 512-token text sequences (matches the
+# upstream config.json's ``text_len: 512`` and the WanPipeline.__call__
+# default). Diffusers' ``_get_t5_prompt_embeds`` has a stale 226 default
+# that gets overridden by ``__call__``; using 512 here matches the actual
+# pipeline behaviour.
+WAN_T5_MAX_SEQ_LEN = 512
+
+
+@invocation(
+ "wan_text_encoder",
+ title="Prompt - Wan 2.2",
+ tags=["prompt", "conditioning", "wan"],
+ category="conditioning",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanTextEncoderInvocation(BaseInvocation):
+ """Encodes a text prompt for Wan 2.2 using the UMT5-XXL encoder.
+
+ Output is the encoder's last hidden state (shape: [seq_len=226, 4096]) plus
+ an attention mask marking valid (non-padding) tokens. The Wan transformer
+ consumes these directly as ``encoder_hidden_states``.
+ """
+
+ prompt: str = InputField(description="Text prompt for Wan 2.2.", ui_component=UIComponent.Textarea)
+ wan_t5_encoder: WanT5EncoderField = InputField(
+ title="UMT5-XXL Encoder",
+ description=FieldDescriptions.wan_t5_encoder,
+ input=Input.Connection,
+ )
+
+ @torch.no_grad()
+ def invoke(self, context: InvocationContext) -> WanConditioningOutput:
+ prompt_embeds, attention_mask = self._encode(context)
+
+ # Persist on CPU; the denoise loop will move to device as needed.
+ prompt_embeds = prompt_embeds.detach().to("cpu")
+ attention_mask = attention_mask.detach().to("cpu") if attention_mask is not None else None
+
+ conditioning_data = ConditioningFieldData(
+ conditionings=[WanConditioningInfo(prompt_embeds=prompt_embeds, prompt_attention_mask=attention_mask)]
+ )
+ conditioning_name = context.conditioning.save(conditioning_data)
+ return WanConditioningOutput.build(conditioning_name)
+
+ def _encode(self, context: InvocationContext) -> tuple[torch.Tensor, torch.Tensor | None]:
+ from diffusers.pipelines.wan.pipeline_wan import prompt_clean
+ from transformers import UMT5EncoderModel
+
+ cleaned = prompt_clean(self.prompt)
+
+ # Tokenizer + text encoder both routed through the model cache so the
+ # registered loaders handle the nested-vs-flat directory layout for us
+ # (main-model layout: /tokenizer/ + /text_encoder/;
+ # standalone WanT5Encoder layout may also be flat).
+ tokenizer_info = context.models.load(self.wan_t5_encoder.tokenizer)
+ with tokenizer_info.model_on_device() as (_, tokenizer):
+ text_inputs = tokenizer(
+ [cleaned],
+ padding="max_length",
+ max_length=WAN_T5_MAX_SEQ_LEN,
+ truncation=True,
+ add_special_tokens=True,
+ return_attention_mask=True,
+ return_tensors="pt",
+ )
+
+ text_encoder_info = context.models.load(self.wan_t5_encoder.text_encoder)
+ with text_encoder_info.model_on_device() as (_, text_encoder):
+ assert isinstance(text_encoder, UMT5EncoderModel)
+ device = get_effective_device(text_encoder)
+
+ input_ids = text_inputs.input_ids.to(device)
+ attention_mask = text_inputs.attention_mask.to(device)
+
+ context.util.signal_progress("Running UMT5-XXL text encoder")
+ outputs = text_encoder(input_ids, attention_mask)
+ # Drop the batch dim (we always encode one prompt at a time).
+ prompt_embeds = outputs.last_hidden_state.squeeze(0)
+ attention_mask_out = attention_mask.squeeze(0)
+
+ # Match the Diffusers reference: zero out the embeddings past the valid
+ # token count so the transformer sees clean padding.
+ valid_len = int(attention_mask_out.sum().item())
+ if valid_len < prompt_embeds.shape[0]:
+ prompt_embeds = prompt_embeds.clone()
+ prompt_embeds[valid_len:] = 0
+
+ # If every token is valid we don't need the mask downstream.
+ mask_out: torch.Tensor | None = attention_mask_out
+ if attention_mask_out.all():
+ mask_out = None
+
+ return prompt_embeds.to(dtype=torch.bfloat16), mask_out
diff --git a/invokeai/app/invocations/wan_video_denoise.py b/invokeai/app/invocations/wan_video_denoise.py
new file mode 100644
index 00000000000..f201949ff55
--- /dev/null
+++ b/invokeai/app/invocations/wan_video_denoise.py
@@ -0,0 +1,405 @@
+"""Wan 2.2 video denoise invocation (T2V / I2V).
+
+Multi-frame counterpart to :mod:`wan_denoise`. Drives the same flow-matching
+schedule + expert-swap MoE logic, but the noise tensor has a real temporal
+dimension (``T_lat = (num_frames - 1) // 4 + 1``) and the I2V conditioning is
+built across all latent frames (first frame conditioned, rest zero).
+
+Kept as a separate file rather than parameterizing ``WanDenoiseInvocation``
+so the working single-frame T2I path is not risked by the video work; the
+shared bits (expert swapper, scheduler construction, conditioning loading,
+LoRA iteration) live in ``wan_denoise`` and are imported here.
+"""
+
+from contextlib import ExitStack
+from typing import Callable, Iterable, Optional, Tuple
+
+import torch
+from tqdm import tqdm
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.fields import (
+ FieldDescriptions,
+ Input,
+ InputField,
+ WanConditioningField,
+ WanRefImageConditioningField,
+)
+from invokeai.app.invocations.model import WanTransformerField
+from invokeai.app.invocations.primitives import LatentsOutput
+from invokeai.app.invocations.wan_denoise import (
+ WanDenoiseInvocation,
+ _ExpertSwapper,
+ _resolve_variant,
+)
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, WanVariantType
+from invokeai.backend.patches.model_patch_raw import ModelPatchRaw
+from invokeai.backend.stable_diffusion.diffusers_pipeline import PipelineIntermediateState
+from invokeai.backend.stable_diffusion.diffusion.conditioning_data import WanConditioningInfo
+from invokeai.backend.util.devices import TorchDevice
+from invokeai.backend.wan.sampling_utils import (
+ get_default_latent_channels,
+ get_spatial_scale_factor,
+ make_noise,
+ num_latent_frames_for,
+)
+
+
+@invocation(
+ "wan_video_denoise",
+ title="Denoise Video - Wan 2.2",
+ tags=["video", "wan"],
+ category="latents",
+ version="1.0.0",
+ classification=Classification.Prototype,
+)
+class WanVideoDenoiseInvocation(BaseInvocation):
+ """Run the Wan 2.2 denoising loop on a multi-frame latent tensor.
+
+ The output is a 5D ``[1, C, T_lat, H/8, W/8]`` latent tensor ready for
+ :class:`WanLatentsToVideoInvocation` to VAE-decode and encode as MP4.
+
+ Mirrors :class:`WanDenoiseInvocation` for the per-step logic (CFG, MoE
+ expert swap at the boundary timestep, LoRA patching, scheduler selection).
+ Differences from the image denoise:
+
+ * The noise tensor has a real temporal dim built from ``num_frames``.
+ * The I2V condition is built across all latent frames (frame 0
+ conditioned, rest zero) via
+ :func:`encode_reference_image_to_video_condition` upstream — the
+ ``ref_image`` field on this node carries a tensor of shape
+ ``[1, 20, T_lat, H_lat, W_lat]`` instead of ``[1, 20, 1, ...]``.
+ * No ``denoising_start`` / ``denoising_end`` / initial-latents inputs.
+ The image denoise node uses those for img2img (noise injection on an
+ existing latent), but image-conditioned video generation flows through
+ the reference-frame conditioning mechanism instead — the first frame
+ drives subsequent frames at every step, so a partial-schedule run from
+ an initial latent has no analogue here. Run the schedule from t=1
+ to t=0 every time. The base ``WanDenoiseInvocation`` still handles
+ the img2img case for stills.
+ """
+
+ transformer: WanTransformerField = InputField(
+ description=(
+ "Wan transformer field. Supported: T2V-A14B / I2V-A14B (dual-expert) and "
+ "TI2V-5B (single-expert, handles both T2V and I2V). All three accept a "
+ "Reference Image input for image-to-video; A14B uses the 36-channel concat "
+ "scheme while TI2V-5B uses the expand_timesteps first-frame-mask blend."
+ ),
+ input=Input.Connection,
+ title="Transformer",
+ )
+ positive_conditioning: WanConditioningField = InputField(
+ description=FieldDescriptions.positive_cond, input=Input.Connection
+ )
+ negative_conditioning: Optional[WanConditioningField] = InputField(
+ default=None, description=FieldDescriptions.negative_cond, input=Input.Connection
+ )
+ ref_image: Optional[WanRefImageConditioningField] = InputField(
+ default=None,
+ description=FieldDescriptions.wan_ref_image,
+ input=Input.Connection,
+ title="Reference Image",
+ )
+
+ guidance_scale: float = InputField(
+ default=5.0,
+ ge=1.0,
+ description="Classifier-free guidance scale. Wan 2.2 video reference uses 5.0 for the "
+ "high-noise expert and 4.0 for the low-noise expert.",
+ title="Guidance Scale",
+ )
+ guidance_scale_low_noise: Optional[float] = InputField(
+ default=4.0,
+ ge=0.0,
+ description="Optional separate CFG scale for the low-noise expert (Wan 2.2 A14B only). "
+ "Values below 1.0 fall back to the primary 'Guidance Scale'.",
+ title="Guidance Scale (Low Noise)",
+ )
+
+ # Wan transformer patch_size=(1, 2, 2) × VAE spatial 8x => H/W multiple of 16.
+ width: int = InputField(default=832, multiple_of=16, description="Width of the generated video.")
+ height: int = InputField(default=480, multiple_of=16, description="Height of the generated video.")
+ num_frames: int = InputField(
+ default=81,
+ ge=5,
+ description="Number of output frames. Must satisfy (num_frames - 1) %% 4 == 0 so the latent "
+ "temporal dim divides cleanly. Wan 2.2 was trained at 81 frames @ 16 FPS (~5 s).",
+ title="Number of Frames",
+ )
+ steps: int = InputField(default=40, gt=0, description="Number of denoising steps.")
+ seed: int = InputField(default=0, description="Randomness seed for reproducibility.")
+
+ @torch.no_grad()
+ def invoke(self, context: InvocationContext) -> LatentsOutput:
+ latents = self._run_diffusion(context)
+ # Keep the 5D shape (B, C, T, H, W) — wan_latents_to_video expects it.
+ latents = latents.detach().to("cpu")
+ name = context.tensors.save(tensor=latents)
+ # LatentsOutput.build uses latents.size()[3] / [2] for width / height.
+ # For 5D the spatial dims are at indices 4 / 3 instead of 3 / 2, so we
+ # call the constructor directly with the actual H/W from the inputs.
+ from invokeai.app.invocations.fields import LatentsField
+
+ return LatentsOutput(
+ latents=LatentsField(latents_name=name, seed=self.seed),
+ width=self.width,
+ height=self.height,
+ )
+
+ def _run_diffusion(self, context: InvocationContext) -> torch.Tensor:
+ if (self.num_frames - 1) % 4 != 0:
+ raise ValueError(
+ f"num_frames must satisfy (num_frames - 1) %% 4 == 0 for the Wan VAE's temporal "
+ f"compression (got {self.num_frames}). Try 5, 9, 13, ..., 81, 85, ..."
+ )
+
+ device = TorchDevice.choose_torch_device()
+ inference_dtype = TorchDevice.choose_bfloat16_safe_dtype(device)
+
+ variant = _resolve_variant(context, self.transformer)
+ spatial_scale = get_spatial_scale_factor(variant)
+
+ # Reuse the image denoise's scheduler construction so we pick up whatever
+ # scheduler the variant ships with (FlowMatchEulerDiscreteScheduler,
+ # UniPCMultistepScheduler, etc.).
+ scheduler_builder = WanDenoiseInvocation._build_scheduler # bound on instance below
+ # Bind a minimal instance to call _build_scheduler — it only reads
+ # self.transformer, which is shape-compatible.
+ proxy = WanDenoiseInvocation.model_construct(
+ transformer=self.transformer,
+ positive_conditioning=self.positive_conditioning,
+ )
+ scheduler = scheduler_builder(proxy, context, device)
+
+ pos_cond = self._load_conditioning(context, self.positive_conditioning, device=device, dtype=inference_dtype)
+ do_cfg = self.guidance_scale != 1.0 and self.negative_conditioning is not None
+ neg_cond: WanConditioningInfo | None = None
+ if do_cfg:
+ assert self.negative_conditioning is not None
+ neg_cond = self._load_conditioning(
+ context, self.negative_conditioning, device=device, dtype=inference_dtype
+ )
+
+ # I2V condition tensor. Two flavours:
+ # * A14B I2V — [1, 20, T_lat, H_lat, W_lat] (4 mask + 16 latent channels).
+ # Concatenated to noise latents along the channel dim each step → 36ch.
+ # * TI2V-5B I2V — [1, 48, 1, H_lat, W_lat] (single latent frame, same
+ # channel count as the noise latents). Blended with noise via a
+ # first_frame_mask at every step (expand_timesteps path).
+ # Variant dispatch happens via the condition tensor's channel count below.
+ ref_condition: torch.Tensor | None = None
+ if self.ref_image is not None:
+ if variant not in (WanVariantType.I2V_A14B, WanVariantType.TI2V_5B):
+ raise ValueError(
+ f"Reference-image conditioning is only supported by Wan 2.2 I2V variants "
+ f"(I2V-A14B or TI2V-5B). The selected transformer is {variant.value!r}. "
+ "Remove the Reference Image input or load an I2V variant."
+ )
+ if self.ref_image.width != self.width or self.ref_image.height != self.height:
+ raise ValueError(
+ f"Reference-image dimensions ({self.ref_image.width}x{self.ref_image.height}) must "
+ f"match denoise dimensions ({self.width}x{self.height})."
+ )
+ # A14B encodes one condition tensor per pixel-frame count, so the
+ # encoder's num_frames must match. TI2V-5B's condition is always
+ # single-frame regardless of the output length, so the field's
+ # num_frames is informational only and we skip this check.
+ if variant == WanVariantType.I2V_A14B and self.ref_image.num_frames != self.num_frames:
+ raise ValueError(
+ f"Reference-image num_frames ({self.ref_image.num_frames}) must match denoise "
+ f"num_frames ({self.num_frames}). Re-run the Reference Image - Wan 2.2 node with "
+ f"num_frames={self.num_frames}."
+ )
+ if variant == WanVariantType.TI2V_5B and (self.width % 32 or self.height % 32):
+ raise ValueError(
+ f"TI2V-5B I2V requires width and height to be multiples of 32 "
+ f"(got {self.width}x{self.height}). Wan 2.2-VAE 16x spatial * "
+ f"transformer patch_size 2 = pixel dims must divide by 32."
+ )
+ ref_condition = context.tensors.load(self.ref_image.condition_tensor_name).to(
+ device=device, dtype=inference_dtype
+ )
+
+ scheduler.set_timesteps(num_inference_steps=self.steps, device=device)
+ timesteps = scheduler.timesteps
+ total_steps = len(timesteps)
+
+ # fp32 latents through the loop; cast to inference_dtype only when
+ # calling the transformer (same as wan_denoise).
+ latent_dtype = torch.float32
+ # 48 for TI2V-5B (Wan 2.2-VAE z_dim=48), 16 for A14B variants.
+ latent_channels = get_default_latent_channels(variant)
+ t_lat = num_latent_frames_for(self.num_frames)
+
+ latents = make_noise(
+ batch_size=1,
+ latent_channels=latent_channels,
+ height=self.height,
+ width=self.width,
+ spatial_scale_factor=spatial_scale,
+ device=device,
+ dtype=latent_dtype,
+ seed=self.seed,
+ num_latent_frames=t_lat,
+ )
+
+ if total_steps <= 0:
+ return latents
+
+ # Sanity-check ref-condition shape per variant. A14B expects matched T_lat;
+ # TI2V-5B expects a single latent frame regardless of output length.
+ if ref_condition is not None:
+ if variant == WanVariantType.TI2V_5B:
+ if ref_condition.shape[1] != 48 or ref_condition.shape[2] != 1:
+ raise ValueError(
+ f"TI2V-5B reference condition must be shape [1, 48, 1, H_lat, W_lat] "
+ f"(got channels={ref_condition.shape[1]}, frames={ref_condition.shape[2]}). "
+ "Re-run the Reference Image - Wan 2.2 node with a TI2V-5B VAE."
+ )
+ elif ref_condition.shape[2] != t_lat:
+ raise ValueError(
+ f"Reference-image condition has {ref_condition.shape[2]} latent frames but the "
+ f"denoise loop expected {t_lat}. Ensure the ref-image encoder was called with "
+ f"the same num_frames ({self.num_frames})."
+ )
+
+ # Build the TI2V-5B first-frame mask once: 0 at frame 0 (locked to the
+ # condition), 1 elsewhere (free to denoise). Broadcasts across channel dim.
+ first_frame_mask: torch.Tensor | None = None
+ if ref_condition is not None and variant == WanVariantType.TI2V_5B:
+ _, _, _, h_lat, w_lat = latents.shape
+ first_frame_mask = torch.ones(1, 1, t_lat, h_lat, w_lat, device=device, dtype=inference_dtype)
+ first_frame_mask[:, :, 0] = 0
+
+ step_callback = self._build_step_callback(context)
+
+ high_model = self.transformer.transformer
+ low_model = self.transformer.transformer_low_noise
+ low_config = context.models.get_config(low_model) if low_model is not None else None
+ num_train_timesteps = int(scheduler.config.num_train_timesteps)
+ boundary_timestep = self.transformer.boundary_ratio * num_train_timesteps if low_model is not None else None
+
+ high_loras = self.transformer.loras
+ low_loras = self.transformer.loras_low_noise or self.transformer.loras
+ high_config = context.models.get_config(high_model)
+ high_is_quantized = high_config.format == ModelFormat.GGUFQuantized
+ low_is_quantized = low_config.format == ModelFormat.GGUFQuantized if low_config is not None else False
+
+ def high_lora_factory() -> Iterable[Tuple[ModelPatchRaw, float]]:
+ return proxy._lora_iterator(context, high_loras)
+
+ def low_lora_factory() -> Iterable[Tuple[ModelPatchRaw, float]]:
+ return proxy._lora_iterator(context, low_loras)
+
+ with ExitStack() as exit_stack:
+ swapper = _ExpertSwapper(
+ context=context,
+ high_model=high_model,
+ low_model=low_model,
+ inference_dtype=inference_dtype,
+ high_lora_factory=high_lora_factory if high_loras else None,
+ low_lora_factory=low_lora_factory if low_loras else None,
+ high_is_quantized=high_is_quantized,
+ low_is_quantized=low_is_quantized,
+ )
+ exit_stack.callback(swapper.close)
+
+ for step_idx, t in enumerate(
+ tqdm(timesteps, desc=f"Denoising Wan 2.2 video ({self.num_frames} frames)", total=total_steps)
+ ):
+ if low_model is not None and float(t) < float(boundary_timestep):
+ active_label = _ExpertSwapper.LOW
+ low_cfg = self.guidance_scale_low_noise
+ active_cfg = low_cfg if (low_cfg is not None and low_cfg >= 1.0) else self.guidance_scale
+ else:
+ active_label = _ExpertSwapper.HIGH
+ active_cfg = self.guidance_scale
+
+ transformer = swapper.get(active_label)
+
+ latent_model_input = latents.to(dtype=inference_dtype)
+
+ # Per-variant conditioning. Two distinct mechanisms:
+ if first_frame_mask is not None:
+ # TI2V-5B I2V (expand_timesteps): blend the condition into frame 0
+ # and the noisy latents elsewhere. Per-token timestep tensor
+ # gates the model so it sees timestep=0 at frame-0 positions
+ # (nothing to denoise) and the normal `t` everywhere else.
+ assert ref_condition is not None
+ latent_model_input = (1 - first_frame_mask) * ref_condition + first_frame_mask * latent_model_input
+ # Strided slice matches the transformer's spatial patch_size=2;
+ # flatten gives per-token timesteps. Shape: [1, T_lat * H_lat//2 * W_lat//2].
+ temp_ts = (first_frame_mask[0, 0, :, ::2, ::2] * t).flatten()
+ timestep = temp_ts.unsqueeze(0).expand(latents.shape[0], -1).to(dtype=inference_dtype)
+ elif ref_condition is not None:
+ # A14B I2V: concat 20-ch condition along channel dim → 36-ch input.
+ latent_model_input = torch.cat([latent_model_input, ref_condition], dim=1)
+ timestep = t.expand(latents.shape[0])
+ else:
+ # T2V (any variant): scalar timestep per batch.
+ timestep = t.expand(latents.shape[0])
+
+ noise_pred_cond = transformer(
+ hidden_states=latent_model_input,
+ timestep=timestep,
+ encoder_hidden_states=pos_cond.prompt_embeds.unsqueeze(0),
+ attention_kwargs=None,
+ return_dict=False,
+ )[0]
+
+ if do_cfg and neg_cond is not None:
+ noise_pred_uncond = transformer(
+ hidden_states=latent_model_input,
+ timestep=timestep,
+ encoder_hidden_states=neg_cond.prompt_embeds.unsqueeze(0),
+ attention_kwargs=None,
+ return_dict=False,
+ )[0]
+ noise_pred = noise_pred_uncond + active_cfg * (noise_pred_cond - noise_pred_uncond)
+ else:
+ noise_pred = noise_pred_cond
+
+ latents = scheduler.step(noise_pred, t, latents, return_dict=False)[0]
+
+ step_callback(
+ PipelineIntermediateState(
+ step=step_idx + 1,
+ order=1,
+ total_steps=total_steps,
+ timestep=int(t.item()),
+ # Preview shows the middle frame for video.
+ latents=latents[:, :, t_lat // 2],
+ )
+ )
+
+ # TI2V-5B: frame 0's latents drifted through the scheduler step at each
+ # iteration; restore them to the clean condition before VAE-decoding so
+ # the first output frame matches the reference image. Mirrors
+ # ``WanImageToVideoPipeline`` (pipeline_wan_i2v.py:813-814).
+ if first_frame_mask is not None:
+ assert ref_condition is not None
+ latents = (1 - first_frame_mask) * ref_condition.to(dtype=latents.dtype) + first_frame_mask * latents
+
+ return latents
+
+ def _load_conditioning(
+ self,
+ context: InvocationContext,
+ cond_field: WanConditioningField,
+ *,
+ device: torch.device,
+ dtype: torch.dtype,
+ ) -> WanConditioningInfo:
+ cond_data = context.conditioning.load(cond_field.conditioning_name)
+ assert len(cond_data.conditionings) == 1
+ cond_info = cond_data.conditionings[0]
+ assert isinstance(cond_info, WanConditioningInfo)
+ return cond_info.to(device=device, dtype=dtype)
+
+ def _build_step_callback(self, context: InvocationContext) -> Callable[[PipelineIntermediateState], None]:
+ def step_callback(state: PipelineIntermediateState) -> None:
+ context.util.sd_step_callback(state, BaseModelType.Wan)
+
+ return step_callback
diff --git a/invokeai/app/run_app.py b/invokeai/app/run_app.py
index febd4f4d4b1..af5bb99d9ec 100644
--- a/invokeai/app/run_app.py
+++ b/invokeai/app/run_app.py
@@ -1,3 +1,22 @@
+import os
+
+# Suppress the HuggingFace tokenizers fork-after-parallelism warning. The Rust
+# ``tokenizers`` library warms a thread pool the first time a tokenizer is used
+# (e.g. the UMT5 / T5 text encoder during Wan / FLUX / SD3 conditioning), then
+# complains every time we fork() afterwards — which we do, on every MP4 encode,
+# because imageio's FFMPEG plugin shells out to ffmpeg via subprocess.Popen.
+# The warning is harmless (the child correctly falls back to single-threaded
+# tokenization before exec()) but it spams the log on every video generation.
+#
+# This MUST execute before any HF library is imported. The pyproject console-script
+# (``invokeai-web = invokeai.app.run_app:run_app``) reaches this module first via
+# ``from invokeai.app.run_app import run_app``, so setting the env var at module
+# level — not inside ``run_app()`` — guarantees it lands before any transitive HF
+# import. Use ``setdefault`` so anyone who explicitly exports ``true`` upstream
+# keeps their value.
+os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
+
+
def get_app():
"""Import the app and event loop. We wrap this in a function to more explicitly control when it happens, because
importing from api_app does a bunch of stuff - it's more like calling a function than importing a module.
diff --git a/invokeai/app/services/board_image_records/board_image_records_base.py b/invokeai/app/services/board_image_records/board_image_records_base.py
index 4ccbaa952db..561eb79ce5f 100644
--- a/invokeai/app/services/board_image_records/board_image_records_base.py
+++ b/invokeai/app/services/board_image_records/board_image_records_base.py
@@ -30,8 +30,13 @@ def get_all_board_image_names_for_board(
board_id: str,
categories: list[ImageCategory] | None,
is_intermediate: bool | None,
+ user_id: Optional[str] = None,
) -> list[str]:
- """Gets all board images for a board, as a list of the image names."""
+ """Gets all board images for a board, as a list of the image names.
+
+ When ``user_id`` is provided, results are restricted to images owned by that user;
+ pass ``None`` for the admin path (no per-user restriction).
+ """
pass
@abstractmethod
diff --git a/invokeai/app/services/board_image_records/board_image_records_sqlite.py b/invokeai/app/services/board_image_records/board_image_records_sqlite.py
index b249bb67334..8d1f9003222 100644
--- a/invokeai/app/services/board_image_records/board_image_records_sqlite.py
+++ b/invokeai/app/services/board_image_records/board_image_records_sqlite.py
@@ -80,6 +80,7 @@ def get_all_board_image_names_for_board(
board_id: str,
categories: list[ImageCategory] | None,
is_intermediate: bool | None,
+ user_id: Optional[str] = None,
) -> list[str]:
with self._db.transaction() as cursor:
params: list[str | bool] = []
@@ -124,6 +125,13 @@ def get_all_board_image_names_for_board(
"""
params.append(is_intermediate)
+ # Per-user filter — admins pass user_id=None to skip this clause.
+ if user_id is not None:
+ stmt += """--sql
+ AND images.user_id = ?
+ """
+ params.append(user_id)
+
# Put a ring on it
stmt += ";"
diff --git a/invokeai/app/services/board_images/board_images_base.py b/invokeai/app/services/board_images/board_images_base.py
index c16d971cd28..269cebfeaea 100644
--- a/invokeai/app/services/board_images/board_images_base.py
+++ b/invokeai/app/services/board_images/board_images_base.py
@@ -30,8 +30,13 @@ def get_all_board_image_names_for_board(
board_id: str,
categories: list[ImageCategory] | None,
is_intermediate: bool | None,
+ user_id: Optional[str] = None,
) -> list[str]:
- """Gets all board images for a board, as a list of the image names."""
+ """Gets all board images for a board, as a list of the image names.
+
+ When ``user_id`` is provided, results are restricted to images owned by that user;
+ pass ``None`` for the admin path (no per-user restriction).
+ """
pass
@abstractmethod
diff --git a/invokeai/app/services/board_images/board_images_default.py b/invokeai/app/services/board_images/board_images_default.py
index 437495189f3..b42ae3db031 100644
--- a/invokeai/app/services/board_images/board_images_default.py
+++ b/invokeai/app/services/board_images/board_images_default.py
@@ -29,11 +29,13 @@ def get_all_board_image_names_for_board(
board_id: str,
categories: list[ImageCategory] | None,
is_intermediate: bool | None,
+ user_id: Optional[str] = None,
) -> list[str]:
return self.__invoker.services.board_image_records.get_all_board_image_names_for_board(
board_id,
categories,
is_intermediate,
+ user_id=user_id,
)
def get_board_for_image(
diff --git a/invokeai/app/services/board_video_records/__init__.py b/invokeai/app/services/board_video_records/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/invokeai/app/services/board_video_records/board_video_records_base.py b/invokeai/app/services/board_video_records/board_video_records_base.py
new file mode 100644
index 00000000000..244b7814924
--- /dev/null
+++ b/invokeai/app/services/board_video_records/board_video_records_base.py
@@ -0,0 +1,43 @@
+from abc import ABC, abstractmethod
+from typing import Optional
+
+from invokeai.app.services.image_records.image_records_common import ImageCategory
+
+
+class BoardVideoRecordStorageBase(ABC):
+ """Abstract base class for the one-to-many board-video relationship record storage."""
+
+ @abstractmethod
+ def add_video_to_board(self, board_id: str, video_name: str) -> None:
+ """Adds a video to a board."""
+ pass
+
+ @abstractmethod
+ def remove_video_from_board(self, video_name: str) -> None:
+ """Removes a video from a board."""
+ pass
+
+ @abstractmethod
+ def get_all_board_video_names_for_board(
+ self,
+ board_id: str,
+ categories: list[ImageCategory] | None,
+ is_intermediate: bool | None,
+ user_id: Optional[str] = None,
+ ) -> list[str]:
+ """Gets all board videos for a board, as a list of the video names.
+
+ When ``user_id`` is provided, results are restricted to videos owned by that user;
+ pass ``None`` for the admin path (no per-user restriction).
+ """
+ pass
+
+ @abstractmethod
+ def get_board_for_video(self, video_name: str) -> Optional[str]:
+ """Gets a video's board id, if it has one."""
+ pass
+
+ @abstractmethod
+ def get_video_count_for_board(self, board_id: str) -> int:
+ """Gets the number of videos for a board."""
+ pass
diff --git a/invokeai/app/services/board_video_records/board_video_records_sqlite.py b/invokeai/app/services/board_video_records/board_video_records_sqlite.py
new file mode 100644
index 00000000000..6b780f8eaae
--- /dev/null
+++ b/invokeai/app/services/board_video_records/board_video_records_sqlite.py
@@ -0,0 +1,97 @@
+import sqlite3
+from typing import Optional, cast
+
+from invokeai.app.services.board_video_records.board_video_records_base import BoardVideoRecordStorageBase
+from invokeai.app.services.image_records.image_records_common import ImageCategory
+from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
+
+
+class SqliteBoardVideoRecordStorage(BoardVideoRecordStorageBase):
+ def __init__(self, db: SqliteDatabase) -> None:
+ super().__init__()
+ self._db = db
+
+ def add_video_to_board(self, board_id: str, video_name: str) -> None:
+ with self._db.transaction() as cursor:
+ cursor.execute(
+ """--sql
+ INSERT INTO board_videos (board_id, video_name)
+ VALUES (?, ?)
+ ON CONFLICT (video_name) DO UPDATE SET board_id = ?;
+ """,
+ (board_id, video_name, board_id),
+ )
+
+ def remove_video_from_board(self, video_name: str) -> None:
+ with self._db.transaction() as cursor:
+ cursor.execute(
+ "DELETE FROM board_videos WHERE video_name = ?;",
+ (video_name,),
+ )
+
+ def get_all_board_video_names_for_board(
+ self,
+ board_id: str,
+ categories: list[ImageCategory] | None,
+ is_intermediate: bool | None,
+ user_id: Optional[str] = None,
+ ) -> list[str]:
+ with self._db.transaction() as cursor:
+ params: list[str | bool] = []
+ stmt = """
+ SELECT videos.video_name
+ FROM videos
+ LEFT JOIN board_videos ON board_videos.video_name = videos.video_name
+ WHERE 1=1
+ """
+ if board_id == "none":
+ stmt += " AND board_videos.board_id IS NULL "
+ else:
+ stmt += " AND board_videos.board_id = ? "
+ params.append(board_id)
+
+ if categories is not None:
+ category_strings = [c.value for c in set(categories)]
+ placeholders = ",".join("?" * len(category_strings))
+ stmt += f" AND videos.video_category IN ( {placeholders} ) "
+ for c in category_strings:
+ params.append(c)
+
+ if is_intermediate is not None:
+ stmt += " AND videos.is_intermediate = ? "
+ params.append(is_intermediate)
+
+ if user_id is not None:
+ stmt += " AND videos.user_id = ? "
+ params.append(user_id)
+
+ stmt += ";"
+ cursor.execute(stmt, params)
+ result = cast(list[sqlite3.Row], cursor.fetchall())
+ return [r[0] for r in result]
+
+ def get_board_for_video(self, video_name: str) -> Optional[str]:
+ with self._db.transaction() as cursor:
+ cursor.execute(
+ "SELECT board_id FROM board_videos WHERE video_name = ?;",
+ (video_name,),
+ )
+ result = cursor.fetchone()
+ if result is None:
+ return None
+ return cast(str, result[0])
+
+ def get_video_count_for_board(self, board_id: str) -> int:
+ with self._db.transaction() as cursor:
+ cursor.execute(
+ """--sql
+ SELECT COUNT(*)
+ FROM board_videos
+ INNER JOIN videos ON board_videos.video_name = videos.video_name
+ WHERE videos.is_intermediate = FALSE
+ AND board_videos.board_id = ?;
+ """,
+ (board_id,),
+ )
+ count = cast(int, cursor.fetchone()[0])
+ return count
diff --git a/invokeai/app/services/boards/boards_common.py b/invokeai/app/services/boards/boards_common.py
index 99952fec134..c6310242bde 100644
--- a/invokeai/app/services/boards/boards_common.py
+++ b/invokeai/app/services/boards/boards_common.py
@@ -10,8 +10,14 @@ class BoardDTO(BoardRecord):
cover_image_name: Optional[str] = Field(description="The name of the board's cover image.")
"""The URL of the thumbnail of the most recent image in the board."""
+ cover_video_name: Optional[str] = Field(
+ default=None, description="The name of the board's cover video, when the most recent item is a video."
+ )
+ """The name of the cover video, set when the most-recent item on the board is a video rather than an image."""
image_count: int = Field(description="The number of images in the board.")
"""The number of images in the board."""
+ video_count: int = Field(default=0, description="The number of videos in the board.")
+ """The number of videos in the board."""
asset_count: int = Field(description="The number of assets in the board.")
"""The number of assets in the board."""
owner_username: Optional[str] = Field(default=None, description="The username of the board owner (for admin view).")
@@ -24,12 +30,16 @@ def board_record_to_dto(
image_count: int,
asset_count: int,
owner_username: Optional[str] = None,
+ cover_video_name: Optional[str] = None,
+ video_count: int = 0,
) -> BoardDTO:
"""Converts a board record to a board DTO."""
return BoardDTO(
**board_record.model_dump(exclude={"cover_image_name"}),
cover_image_name=cover_image_name,
+ cover_video_name=cover_video_name,
image_count=image_count,
+ video_count=video_count,
asset_count=asset_count,
owner_username=owner_username,
)
diff --git a/invokeai/app/services/boards/boards_default.py b/invokeai/app/services/boards/boards_default.py
index 71465815ef9..65fb05d6bd3 100644
--- a/invokeai/app/services/boards/boards_default.py
+++ b/invokeai/app/services/boards/boards_default.py
@@ -1,3 +1,5 @@
+from typing import Optional
+
from invokeai.app.services.board_records.board_records_common import BoardChanges, BoardRecordOrderBy
from invokeai.app.services.boards.boards_base import BoardServiceABC
from invokeai.app.services.boards.boards_common import BoardDTO, board_record_to_dto
@@ -12,6 +14,38 @@ class BoardService(BoardServiceABC):
def start(self, invoker: Invoker) -> None:
self.__invoker = invoker
+ def _resolve_cover(self, board_id: str) -> tuple[Optional[str], Optional[str]]:
+ """Pick the cover item for a board, considering both images and videos.
+
+ Returns ``(cover_image_name, cover_video_name)`` — at most one is set.
+ The winner is chosen by ``(starred DESC, created_at DESC)`` across both
+ tables so a recent video can supersede an older image (and vice versa).
+ """
+ cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(board_id)
+ cover_video = self.__invoker.services.video_records.get_most_recent_video_for_board(board_id)
+
+ if cover_image is None and cover_video is None:
+ return None, None
+ if cover_video is None:
+ assert cover_image is not None
+ return cover_image.image_name, None
+ if cover_image is None:
+ return None, cover_video.video_name
+
+ # Both candidates exist — compare on (starred, created_at).
+ image_key = (cover_image.starred, cover_image.created_at)
+ video_key = (cover_video.starred, cover_video.created_at)
+ if video_key > image_key:
+ return None, cover_video.video_name
+ return cover_image.image_name, None
+
+ def _get_counts(self, board_id: str) -> tuple[int, int, int]:
+ """Return ``(image_count, video_count, asset_count)`` for a board."""
+ image_count = self.__invoker.services.board_image_records.get_image_count_for_board(board_id)
+ asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(board_id)
+ video_count = self.__invoker.services.board_video_records.get_video_count_for_board(board_id)
+ return image_count, video_count, asset_count
+
def create(
self,
board_name: str,
@@ -22,14 +56,16 @@ def create(
def get_dto(self, board_id: str) -> BoardDTO:
board_record = self.__invoker.services.board_records.get(board_id)
- cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(board_record.board_id)
- if cover_image:
- cover_image_name = cover_image.image_name
- else:
- cover_image_name = None
- image_count = self.__invoker.services.board_image_records.get_image_count_for_board(board_id)
- asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(board_id)
- return board_record_to_dto(board_record, cover_image_name, image_count, asset_count)
+ cover_image_name, cover_video_name = self._resolve_cover(board_record.board_id)
+ image_count, video_count, asset_count = self._get_counts(board_id)
+ return board_record_to_dto(
+ board_record,
+ cover_image_name,
+ image_count,
+ asset_count,
+ cover_video_name=cover_video_name,
+ video_count=video_count,
+ )
def update(
self,
@@ -37,15 +73,16 @@ def update(
changes: BoardChanges,
) -> BoardDTO:
board_record = self.__invoker.services.board_records.update(board_id, changes)
- cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(board_record.board_id)
- if cover_image:
- cover_image_name = cover_image.image_name
- else:
- cover_image_name = None
-
- image_count = self.__invoker.services.board_image_records.get_image_count_for_board(board_id)
- asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(board_id)
- return board_record_to_dto(board_record, cover_image_name, image_count, asset_count)
+ cover_image_name, cover_video_name = self._resolve_cover(board_record.board_id)
+ image_count, video_count, asset_count = self._get_counts(board_id)
+ return board_record_to_dto(
+ board_record,
+ cover_image_name,
+ image_count,
+ asset_count,
+ cover_video_name=cover_video_name,
+ video_count=video_count,
+ )
def delete(self, board_id: str) -> None:
self.__invoker.services.board_records.delete(board_id)
@@ -65,14 +102,8 @@ def get_many(
)
board_dtos = []
for r in board_records.items:
- cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(r.board_id)
- if cover_image:
- cover_image_name = cover_image.image_name
- else:
- cover_image_name = None
-
- image_count = self.__invoker.services.board_image_records.get_image_count_for_board(r.board_id)
- asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(r.board_id)
+ cover_image_name, cover_video_name = self._resolve_cover(r.board_id)
+ image_count, video_count, asset_count = self._get_counts(r.board_id)
# For admin users, include owner username
owner_username = None
@@ -81,7 +112,17 @@ def get_many(
if owner:
owner_username = owner.display_name or owner.email
- board_dtos.append(board_record_to_dto(r, cover_image_name, image_count, asset_count, owner_username))
+ board_dtos.append(
+ board_record_to_dto(
+ r,
+ cover_image_name,
+ image_count,
+ asset_count,
+ owner_username,
+ cover_video_name=cover_video_name,
+ video_count=video_count,
+ )
+ )
return OffsetPaginatedResults[BoardDTO](items=board_dtos, offset=offset, limit=limit, total=len(board_dtos))
@@ -98,14 +139,8 @@ def get_all(
)
board_dtos = []
for r in board_records:
- cover_image = self.__invoker.services.image_records.get_most_recent_image_for_board(r.board_id)
- if cover_image:
- cover_image_name = cover_image.image_name
- else:
- cover_image_name = None
-
- image_count = self.__invoker.services.board_image_records.get_image_count_for_board(r.board_id)
- asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(r.board_id)
+ cover_image_name, cover_video_name = self._resolve_cover(r.board_id)
+ image_count, video_count, asset_count = self._get_counts(r.board_id)
# For admin users, include owner username
owner_username = None
@@ -114,6 +149,16 @@ def get_all(
if owner:
owner_username = owner.display_name or owner.email
- board_dtos.append(board_record_to_dto(r, cover_image_name, image_count, asset_count, owner_username))
+ board_dtos.append(
+ board_record_to_dto(
+ r,
+ cover_image_name,
+ image_count,
+ asset_count,
+ owner_username,
+ cover_video_name=cover_video_name,
+ video_count=video_count,
+ )
+ )
return board_dtos
diff --git a/invokeai/app/services/gallery/__init__.py b/invokeai/app/services/gallery/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/invokeai/app/services/gallery/gallery_base.py b/invokeai/app/services/gallery/gallery_base.py
new file mode 100644
index 00000000000..fbadabdeb9f
--- /dev/null
+++ b/invokeai/app/services/gallery/gallery_base.py
@@ -0,0 +1,60 @@
+from abc import ABC, abstractmethod
+from typing import Optional
+
+from invokeai.app.services.gallery.gallery_common import GalleryItem, GalleryItemNamesResult
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.virtual_boards.virtual_boards_common import VirtualSubBoardDTO
+
+
+class GalleryServiceABC(ABC):
+ """High-level service producing a polymorphic stream of images and videos."""
+
+ @abstractmethod
+ def list_items(
+ self,
+ offset: int = 0,
+ limit: int = 10,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> OffsetPaginatedResults[GalleryItem]:
+ """Lists a paginated, time-sorted stream of image + video items."""
+ pass
+
+ @abstractmethod
+ def list_item_names(
+ self,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ created_date: Optional[str] = None,
+ ) -> GalleryItemNamesResult:
+ """Returns ordered (kind, name) refs for optimistic UI / virtualized lists.
+
+ `created_date` restricts the result to items created on the given ISO date — used by
+ date-based virtual boards.
+ """
+ pass
+
+ @abstractmethod
+ def get_dates(
+ self,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> list[VirtualSubBoardDTO]:
+ """Returns date-based virtual sub-boards covering both images and videos."""
+ pass
diff --git a/invokeai/app/services/gallery/gallery_common.py b/invokeai/app/services/gallery/gallery_common.py
new file mode 100644
index 00000000000..9cebc124558
--- /dev/null
+++ b/invokeai/app/services/gallery/gallery_common.py
@@ -0,0 +1,55 @@
+"""Polymorphic gallery types: images and videos appearing in a single time-sorted stream."""
+
+import datetime
+from enum import Enum
+from typing import Optional, Union
+
+from pydantic import BaseModel, Field
+
+from invokeai.app.services.image_records.image_records_common import ImageCategory
+from invokeai.app.util.metaenum import MetaEnum
+from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
+
+
+class GalleryItemKind(str, Enum, metaclass=MetaEnum):
+ """Discriminator for polymorphic gallery items."""
+
+ IMAGE = "image"
+ VIDEO = "video"
+
+
+class GalleryItemRef(BaseModel):
+ """A thin reference to a gallery item — used for ordered name lists."""
+
+ kind: GalleryItemKind = Field(description="Whether the item is an image or video.")
+ name: str = Field(description="The unique name of the image or video.")
+
+
+class GalleryItem(BaseModelExcludeNull):
+ """A gallery item — either an image or a video, with shared fields and a discriminator.
+
+ Frontend code should dispatch on `kind` to render image- vs video-specific UI.
+ """
+
+ kind: GalleryItemKind = Field(description="Whether the item is an image or video.")
+ name: str = Field(description="The unique name of the image or video.")
+ full_url: str = Field(description="URL to the full-resolution image PNG or the full-quality video MP4.")
+ thumbnail_url: str = Field(description="URL to the static (WebP) thumbnail.")
+ width: int = Field(description="The width of the item in pixels.")
+ height: int = Field(description="The height of the item in pixels.")
+ category: ImageCategory = Field(description="The category of the item (images and videos share the same enum).")
+ starred: bool = Field(description="Whether the item is starred.")
+ is_intermediate: bool = Field(description="Whether the item is an intermediate output.")
+ board_id: Optional[str] = Field(default=None, description="Owning board id, if any.")
+ created_at: Union[datetime.datetime, str] = Field(description="The created timestamp of the item.")
+ # Video-only fields. None for images.
+ duration: Optional[float] = Field(default=None, description="Video duration in seconds. None for images.")
+ fps: Optional[float] = Field(default=None, description="Video frames per second. None for images.")
+
+
+class GalleryItemNamesResult(BaseModel):
+ """Ordered list of gallery item references plus counts for optimistic UI."""
+
+ items: list[GalleryItemRef] = Field(description="Ordered list of (kind, name) references.")
+ starred_count: int = Field(description="Number of starred items (when starred_first=True).")
+ total_count: int = Field(description="Total number of items matching the query.")
diff --git a/invokeai/app/services/gallery/gallery_default.py b/invokeai/app/services/gallery/gallery_default.py
new file mode 100644
index 00000000000..b5ceab65922
--- /dev/null
+++ b/invokeai/app/services/gallery/gallery_default.py
@@ -0,0 +1,390 @@
+import sqlite3
+from typing import Optional, Union, cast
+
+from invokeai.app.services.gallery.gallery_base import GalleryServiceABC
+from invokeai.app.services.gallery.gallery_common import (
+ GalleryItem,
+ GalleryItemKind,
+ GalleryItemNamesResult,
+ GalleryItemRef,
+)
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.invoker import Invoker
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
+from invokeai.app.services.virtual_boards.virtual_boards_common import VirtualSubBoardDTO
+
+
+class SqliteGalleryService(GalleryServiceABC):
+ """Implements a polymorphic gallery via UNION ALL across the `images` and `videos` tables.
+
+ Filters are applied identically on each half. The two halves expose a common column set so
+ the result is shape-compatible (a literal `kind` discriminator + a `name` alias + duration/fps
+ that are NULL for images).
+ """
+
+ __invoker: Invoker
+
+ def __init__(self, db: SqliteDatabase) -> None:
+ super().__init__()
+ self._db = db
+
+ def start(self, invoker: Invoker) -> None:
+ self.__invoker = invoker
+
+ def list_items(
+ self,
+ offset: int = 0,
+ limit: int = 10,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> OffsetPaginatedResults[GalleryItem]:
+ image_half, image_params, image_count_query = self._build_half(
+ kind="image",
+ origin=origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=user_id,
+ is_admin=is_admin,
+ )
+ video_half, video_params, video_count_query = self._build_half(
+ kind="video",
+ origin=origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=user_id,
+ is_admin=is_admin,
+ )
+
+ if starred_first:
+ order_clause = f"ORDER BY starred DESC, created_at {order_dir.value}"
+ else:
+ order_clause = f"ORDER BY created_at {order_dir.value}"
+
+ union_query = f"""--sql
+ SELECT * FROM (
+ {image_half}
+ UNION ALL
+ {video_half}
+ )
+ {order_clause}
+ LIMIT ? OFFSET ?
+ ;
+ """
+
+ with self._db.transaction() as cursor:
+ cursor.execute(union_query, image_params + video_params + [limit, offset])
+ rows = cast(list[sqlite3.Row], cursor.fetchall())
+
+ cursor.execute(image_count_query, image_params)
+ image_count = cast(int, cursor.fetchone()[0])
+ cursor.execute(video_count_query, video_params)
+ video_count = cast(int, cursor.fetchone()[0])
+
+ urls = self.__invoker.services.urls
+ items = [self._row_to_item(row, urls) for row in rows]
+ return OffsetPaginatedResults[GalleryItem](
+ items=items,
+ offset=offset,
+ limit=limit,
+ total=image_count + video_count,
+ )
+
+ def list_item_names(
+ self,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ created_date: Optional[str] = None,
+ ) -> GalleryItemNamesResult:
+ image_half, image_params, _ = self._build_half(
+ kind="image",
+ origin=origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=user_id,
+ is_admin=is_admin,
+ names_only=True,
+ created_date=created_date,
+ )
+ video_half, video_params, _ = self._build_half(
+ kind="video",
+ origin=origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=user_id,
+ is_admin=is_admin,
+ names_only=True,
+ created_date=created_date,
+ )
+
+ if starred_first:
+ order_clause = f"ORDER BY starred DESC, created_at {order_dir.value}"
+ else:
+ order_clause = f"ORDER BY created_at {order_dir.value}"
+
+ union_query = f"""--sql
+ SELECT * FROM (
+ {image_half}
+ UNION ALL
+ {video_half}
+ )
+ {order_clause}
+ ;
+ """
+
+ with self._db.transaction() as cursor:
+ cursor.execute(union_query, image_params + video_params)
+ rows = cast(list[sqlite3.Row], cursor.fetchall())
+
+ starred_count = 0
+ if starred_first:
+ starred_count = sum(1 for r in rows if r["starred"])
+
+ refs = [GalleryItemRef(kind=GalleryItemKind(row["kind"]), name=row["name"]) for row in rows]
+ return GalleryItemNamesResult(items=refs, starred_count=starred_count, total_count=len(refs))
+
+ def get_dates(
+ self,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> list[VirtualSubBoardDTO]:
+ image_conditions = " AND images.is_intermediate = 0 "
+ video_conditions = " AND videos.is_intermediate = 0 "
+ image_params: list[Union[int, str, bool]] = []
+ video_params: list[Union[int, str, bool]] = []
+
+ if user_id is not None and not is_admin:
+ image_conditions += " AND images.user_id = ? "
+ image_params.append(user_id)
+ video_conditions += " AND videos.user_id = ? "
+ video_params.append(user_id)
+
+ union = f"""--sql
+ SELECT
+ images.created_at AS created_at,
+ 'image' AS kind,
+ images.image_name AS name,
+ images.image_category AS category
+ FROM images
+ WHERE 1=1 {image_conditions}
+ UNION ALL
+ SELECT
+ videos.created_at AS created_at,
+ 'video' AS kind,
+ videos.video_name AS name,
+ videos.video_category AS category
+ FROM videos
+ WHERE 1=1 {video_conditions}
+ """
+
+ counts_query = f"""--sql
+ SELECT
+ DATE(created_at) AS date,
+ SUM(CASE WHEN kind = 'image' AND category = 'general' THEN 1 ELSE 0 END) AS image_count,
+ SUM(CASE WHEN kind = 'image' AND category != 'general' THEN 1 ELSE 0 END) AS asset_count,
+ SUM(CASE WHEN kind = 'video' THEN 1 ELSE 0 END) AS video_count
+ FROM ({union})
+ GROUP BY DATE(created_at)
+ ORDER BY date DESC;
+ """
+
+ # SQLite guarantees that bare columns in an aggregate query come from the row that
+ # matched MAX() — so `kind`/`name` here are the newest item of each date, which
+ # becomes the cover.
+ covers_query = f"""--sql
+ SELECT
+ DATE(created_at) AS date,
+ kind,
+ name,
+ MAX(created_at) AS newest
+ FROM ({union})
+ GROUP BY DATE(created_at);
+ """
+
+ with self._db.transaction() as cursor:
+ cursor.execute(counts_query, image_params + video_params)
+ count_rows = cast(list[sqlite3.Row], cursor.fetchall())
+ cursor.execute(covers_query, image_params + video_params)
+ cover_rows = cast(list[sqlite3.Row], cursor.fetchall())
+
+ covers = {row["date"]: (row["kind"], row["name"]) for row in cover_rows}
+
+ boards: list[VirtualSubBoardDTO] = []
+ for row in count_rows:
+ date = row["date"]
+ cover_kind, cover_name = covers.get(date, (None, None))
+ boards.append(
+ VirtualSubBoardDTO(
+ virtual_board_id=f"by_date:{date}",
+ board_name=date,
+ date=date,
+ image_count=row["image_count"],
+ asset_count=row["asset_count"],
+ video_count=row["video_count"],
+ cover_image_name=cover_name if cover_kind == "image" else None,
+ cover_video_name=cover_name if cover_kind == "video" else None,
+ )
+ )
+ return boards
+
+ def _build_half(
+ self,
+ kind: str,
+ origin: Optional[ResourceOrigin],
+ categories: Optional[list[ImageCategory]],
+ is_intermediate: Optional[bool],
+ board_id: Optional[str],
+ search_term: Optional[str],
+ user_id: Optional[str],
+ is_admin: bool,
+ names_only: bool = False,
+ created_date: Optional[str] = None,
+ ) -> tuple[str, list[Union[int, str, bool]], str]:
+ """Builds one half of the union (either `images` or `videos`).
+
+ Returns `(query_with_select, params, count_query)`. Both halves emit the same columns so
+ UNION ALL is shape-compatible: `kind`, `name`, `width`, `height`, `category`, `starred`,
+ `is_intermediate`, `board_id`, `created_at`, `duration`, `fps`.
+
+ `names_only=True` selects only `kind`, `name`, `starred`, `created_at` (the minimum needed
+ for ordering + the counts result).
+ """
+ if kind == "image":
+ base_table = "images"
+ join_table = "board_images"
+ name_col = "image_name"
+ category_col = "image_category"
+ origin_col = "image_origin"
+ duration_expr = "NULL"
+ fps_expr = "NULL"
+ elif kind == "video":
+ base_table = "videos"
+ join_table = "board_videos"
+ name_col = "video_name"
+ category_col = "video_category"
+ origin_col = "video_origin"
+ duration_expr = f"{base_table}.duration"
+ fps_expr = f"{base_table}.fps"
+ else:
+ raise ValueError(f"Unknown kind: {kind}")
+
+ if names_only:
+ select_cols = (
+ f"'{kind}' AS kind, "
+ f"{base_table}.{name_col} AS name, "
+ f"{base_table}.starred AS starred, "
+ f"{base_table}.created_at AS created_at"
+ )
+ else:
+ select_cols = (
+ f"'{kind}' AS kind, "
+ f"{base_table}.{name_col} AS name, "
+ f"{base_table}.width AS width, "
+ f"{base_table}.height AS height, "
+ f"{base_table}.{category_col} AS category, "
+ f"{base_table}.starred AS starred, "
+ f"{base_table}.is_intermediate AS is_intermediate, "
+ f"{join_table}.board_id AS board_id, "
+ f"{base_table}.created_at AS created_at, "
+ f"{duration_expr} AS duration, "
+ f"{fps_expr} AS fps"
+ )
+
+ from_clause = f"FROM {base_table} LEFT JOIN {join_table} ON {join_table}.{name_col} = {base_table}.{name_col}"
+
+ conditions = ""
+ params: list[Union[int, str, bool]] = []
+
+ if origin is not None:
+ conditions += f" AND {base_table}.{origin_col} = ? "
+ params.append(origin.value)
+
+ if categories is not None:
+ category_strings = [c.value for c in set(categories)]
+ placeholders = ",".join("?" * len(category_strings))
+ conditions += f" AND {base_table}.{category_col} IN ( {placeholders} ) "
+ for c in category_strings:
+ params.append(c)
+
+ if is_intermediate is not None:
+ conditions += f" AND {base_table}.is_intermediate = ? "
+ params.append(is_intermediate)
+
+ if created_date is not None:
+ conditions += f" AND DATE({base_table}.created_at) = ? "
+ params.append(created_date)
+
+ if board_id == "none":
+ conditions += f" AND {join_table}.board_id IS NULL "
+ if user_id is not None and not is_admin:
+ conditions += f" AND {base_table}.user_id = ? "
+ params.append(user_id)
+ elif board_id is not None:
+ conditions += f" AND {join_table}.board_id = ? "
+ params.append(board_id)
+ elif user_id is not None and not is_admin:
+ # No board_id supplied — still enforce per-user isolation so
+ # non-admin callers cannot enumerate other users' items.
+ conditions += f" AND {base_table}.user_id = ? "
+ params.append(user_id)
+
+ if search_term:
+ conditions += f" AND ({base_table}.metadata LIKE ? OR {base_table}.created_at LIKE ?) "
+ params.append(f"%{search_term.lower()}%")
+ params.append(f"%{search_term.lower()}%")
+
+ half_query = f"SELECT {select_cols} {from_clause} WHERE 1=1 {conditions}"
+ count_query = f"SELECT COUNT(*) {from_clause} WHERE 1=1 {conditions}"
+ return half_query, params, count_query
+
+ def _row_to_item(self, row: sqlite3.Row, urls) -> GalleryItem:
+ kind = GalleryItemKind(row["kind"])
+ name = row["name"]
+ if kind == GalleryItemKind.IMAGE:
+ full_url = urls.get_image_url(name)
+ thumbnail_url = urls.get_image_url(name, thumbnail=True)
+ duration = None
+ fps = None
+ else:
+ full_url = urls.get_video_url(name)
+ thumbnail_url = urls.get_video_url(name, thumbnail=True)
+ duration = row["duration"]
+ fps = row["fps"]
+ return GalleryItem(
+ kind=kind,
+ name=name,
+ full_url=full_url,
+ thumbnail_url=thumbnail_url,
+ width=row["width"],
+ height=row["height"],
+ category=ImageCategory(row["category"]),
+ starred=bool(row["starred"]),
+ is_intermediate=bool(row["is_intermediate"]),
+ board_id=row["board_id"],
+ created_at=row["created_at"],
+ duration=duration,
+ fps=fps,
+ )
diff --git a/invokeai/app/services/images/images_base.py b/invokeai/app/services/images/images_base.py
index aebbead2f35..15a9bab4908 100644
--- a/invokeai/app/services/images/images_base.py
+++ b/invokeai/app/services/images/images_base.py
@@ -148,8 +148,13 @@ def get_intermediates_count(self, user_id: Optional[str] = None) -> int:
pass
@abstractmethod
- def delete_images_on_board(self, board_id: str):
- """Deletes all images on a board."""
+ def delete_images_on_board(self, board_id: str, user_id: Optional[str] = None):
+ """Deletes all images on a board.
+
+ When ``user_id`` is provided, only images owned by that user are deleted (other users'
+ contributions to a public/shared board are preserved). Pass ``None`` for the admin
+ path to delete every image on the board regardless of uploader.
+ """
pass
@abstractmethod
diff --git a/invokeai/app/services/images/images_default.py b/invokeai/app/services/images/images_default.py
index 4a190f37edc..9010f149cf6 100644
--- a/invokeai/app/services/images/images_default.py
+++ b/invokeai/app/services/images/images_default.py
@@ -291,12 +291,15 @@ def delete(self, image_name: str):
self.__invoker.services.logger.error("Problem deleting image record and file")
raise e
- def delete_images_on_board(self, board_id: str):
+ def delete_images_on_board(self, board_id: str, user_id: Optional[str] = None):
try:
+ # When ``user_id`` is set the lookup filters to images owned by that user so the
+ # cascade doesn't destroy other users' contributions to a public/shared board.
image_names = self.__invoker.services.board_image_records.get_all_board_image_names_for_board(
board_id,
categories=None,
is_intermediate=None,
+ user_id=user_id,
)
for image_name in image_names:
try:
diff --git a/invokeai/app/services/invocation_services.py b/invokeai/app/services/invocation_services.py
index 54f9d82b786..eb80237659a 100644
--- a/invokeai/app/services/invocation_services.py
+++ b/invokeai/app/services/invocation_services.py
@@ -15,6 +15,7 @@
from invokeai.app.services.board_image_records.board_image_records_base import BoardImageRecordStorageBase
from invokeai.app.services.board_images.board_images_base import BoardImagesServiceABC
from invokeai.app.services.board_records.board_records_base import BoardRecordStorageBase
+ from invokeai.app.services.board_video_records.board_video_records_base import BoardVideoRecordStorageBase
from invokeai.app.services.boards.boards_base import BoardServiceABC
from invokeai.app.services.bulk_download.bulk_download_base import BulkDownloadBase
from invokeai.app.services.client_state_persistence.client_state_persistence_base import ClientStatePersistenceABC
@@ -22,6 +23,7 @@
from invokeai.app.services.download import DownloadQueueServiceBase
from invokeai.app.services.events.events_base import EventServiceBase
from invokeai.app.services.external_generation.external_generation_base import ExternalGenerationServiceBase
+ from invokeai.app.services.gallery.gallery_base import GalleryServiceABC
from invokeai.app.services.image_files.image_files_base import ImageFileStorageBase
from invokeai.app.services.image_moves.image_moves_default import ImageMoveService
from invokeai.app.services.image_records.image_records_base import ImageRecordStorageBase
@@ -39,6 +41,9 @@
from invokeai.app.services.session_queue.session_queue_base import SessionQueueBase
from invokeai.app.services.urls.urls_base import UrlServiceBase
from invokeai.app.services.users.users_base import UserServiceBase
+ from invokeai.app.services.video_files.video_files_base import VideoFileStorageBase
+ from invokeai.app.services.video_records.video_records_base import VideoRecordStorageBase
+ from invokeai.app.services.videos.videos_base import VideoServiceABC
from invokeai.app.services.workflow_records.workflow_records_base import WorkflowRecordsStorageBase
from invokeai.app.services.workflow_thumbnails.workflow_thumbnails_base import WorkflowThumbnailServiceBase
from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ConditioningFieldData
@@ -80,6 +85,11 @@ def __init__(
workflow_thumbnails: "WorkflowThumbnailServiceBase",
client_state_persistence: "ClientStatePersistenceABC",
users: "UserServiceBase",
+ videos: "VideoServiceABC",
+ video_files: "VideoFileStorageBase",
+ video_records: "VideoRecordStorageBase",
+ board_video_records: "BoardVideoRecordStorageBase",
+ gallery: "GalleryServiceABC",
image_moves: "ImageMoveService | None" = None,
):
self.board_images = board_images
@@ -114,3 +124,8 @@ def __init__(
self.workflow_thumbnails = workflow_thumbnails
self.client_state_persistence = client_state_persistence
self.users = users
+ self.videos = videos
+ self.video_files = video_files
+ self.video_records = video_records
+ self.board_video_records = board_video_records
+ self.gallery = gallery
diff --git a/invokeai/app/services/model_records/model_records_base.py b/invokeai/app/services/model_records/model_records_base.py
index e06f8f2df91..4d5a9d102ca 100644
--- a/invokeai/app/services/model_records/model_records_base.py
+++ b/invokeai/app/services/model_records/model_records_base.py
@@ -33,6 +33,8 @@
Qwen3VariantType,
QwenImageVariantType,
SchedulerPredictionType,
+ WanLoRAVariantType,
+ WanVariantType,
ZImageVariantType,
)
@@ -134,6 +136,8 @@ def validate_source_url(cls, v: Any) -> Optional[str]:
| Flux2VariantType
| ZImageVariantType
| QwenImageVariantType
+ | WanVariantType
+ | WanLoRAVariantType
| Qwen3VariantType
] = Field(description="The variant of the model.", default=None)
prediction_type: Optional[SchedulerPredictionType] = Field(
diff --git a/invokeai/app/services/names/names_base.py b/invokeai/app/services/names/names_base.py
index f892c43c55a..fd97007ced3 100644
--- a/invokeai/app/services/names/names_base.py
+++ b/invokeai/app/services/names/names_base.py
@@ -9,3 +9,8 @@ class NameServiceBase(ABC):
def create_image_name(self) -> str:
"""Creates a name for an image."""
pass
+
+ @abstractmethod
+ def create_video_name(self) -> str:
+ """Creates a name for a video."""
+ pass
diff --git a/invokeai/app/services/names/names_default.py b/invokeai/app/services/names/names_default.py
index 5804a937d6a..d43ed4864cd 100644
--- a/invokeai/app/services/names/names_default.py
+++ b/invokeai/app/services/names/names_default.py
@@ -10,3 +10,8 @@ def create_image_name(self) -> str:
uuid_str = uuid_string()
filename = f"{uuid_str}.png"
return filename
+
+ def create_video_name(self) -> str:
+ uuid_str = uuid_string()
+ filename = f"{uuid_str}.mp4"
+ return filename
diff --git a/invokeai/app/services/session_queue/session_queue_common.py b/invokeai/app/services/session_queue/session_queue_common.py
index 8d068b3fa65..a7536202a6b 100644
--- a/invokeai/app/services/session_queue/session_queue_common.py
+++ b/invokeai/app/services/session_queue/session_queue_common.py
@@ -15,7 +15,7 @@
)
from pydantic_core import to_jsonable_python
-from invokeai.app.invocations.fields import ImageField
+from invokeai.app.invocations.fields import ImageField, VideoField
from invokeai.app.services.shared.graph import Graph, GraphExecutionState, NodeNotFoundError
from invokeai.app.services.workflow_records.workflow_records_common import (
WorkflowWithoutID,
@@ -51,7 +51,7 @@ class SessionQueueItemNotFoundError(ValueError):
# region Batch
-BatchScalarDataType = Union[StrictStr, float, int, ImageField]
+BatchScalarDataType = Union[StrictStr, float, int, ImageField, VideoField]
BatchDataType = Union[BatchScalarDataType, list[BatchScalarDataType]]
diff --git a/invokeai/app/services/shared/invocation_context.py b/invokeai/app/services/shared/invocation_context.py
index e38766d5ba2..1c25884f371 100644
--- a/invokeai/app/services/shared/invocation_context.py
+++ b/invokeai/app/services/shared/invocation_context.py
@@ -9,7 +9,7 @@
from invokeai.app.invocations.constants import IMAGE_MODES
from invokeai.app.invocations.fields import MetadataField, WithBoard, WithMetadata
-from invokeai.app.services.board_records.board_records_common import BoardRecordOrderBy
+from invokeai.app.services.board_records.board_records_common import BoardRecordOrderBy, BoardVisibility
from invokeai.app.services.boards.boards_common import BoardDTO
from invokeai.app.services.config.config_default import InvokeAIAppConfig
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
@@ -18,6 +18,7 @@
from invokeai.app.services.model_records.model_records_base import UnknownModelException
from invokeai.app.services.session_processor.session_processor_common import ProgressImage
from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.videos.videos_common import VideoDTO
from invokeai.app.util.step_callback import diffusion_step_callback
from invokeai.backend.model_manager.configs.base import Config_Base
from invokeai.backend.model_manager.configs.factory import AnyModelConfig
@@ -292,6 +293,115 @@ def get_path(self, image_name: str, thumbnail: bool = False) -> Path:
return Path(self._services.images.get_path(image_name, thumbnail))
+class VideosInterface(InvocationContextInterface):
+ """Save and look up videos produced by invocations.
+
+ Mirrors :class:`ImagesInterface` but consumes a path to an already-encoded
+ MP4 (or other supported container) rather than an in-memory PIL image —
+ video encoding is the caller's responsibility (e.g. the
+ ``wan_latents_to_video`` node uses ``imageio[ffmpeg]``).
+ """
+
+ def __init__(self, services: InvocationServices, data: InvocationContextData, util: "UtilInterface") -> None:
+ super().__init__(services, data)
+ self._util = util
+
+ def _assert_read_access(self, video_name: str) -> None:
+ user_id = self._data.queue_item.user_id
+ user = self._services.users.get(user_id)
+ if user is None:
+ raise PermissionError("Queue user is not authorized to access this video")
+ if user.is_admin or self._services.video_records.get_user_id(video_name) == user_id:
+ return
+ board_id = self._services.board_video_records.get_board_for_video(video_name)
+ if board_id is not None:
+ board = self._services.boards.get_dto(board_id)
+ if board.board_visibility in (BoardVisibility.Shared, BoardVisibility.Public):
+ return
+ raise PermissionError("Queue user is not authorized to access this video")
+
+ def save(
+ self,
+ source_path: Path,
+ width: int,
+ height: int,
+ duration: float,
+ fps: Optional[float] = None,
+ board_id: Optional[str] = None,
+ image_category: ImageCategory = ImageCategory.GENERAL,
+ metadata: Optional[MetadataField] = None,
+ ) -> VideoDTO:
+ """Save a video produced by an invocation. The file at ``source_path`` is moved into
+ the videos output folder; the caller should treat the path as consumed after this
+ returns.
+
+ ``board_id`` falls back to the invocation's :class:`WithBoard` mixin if unset, and
+ ``metadata`` falls back to the :class:`WithMetadata` mixin. Both can be overridden
+ explicitly. ``image_category`` reuses the image enum since the gallery's category
+ filter is shared between kinds.
+ """
+
+ self._util.signal_progress("Saving video")
+
+ metadata_ = None
+ if metadata:
+ metadata_ = metadata.model_dump_json()
+ elif isinstance(self._data.invocation, WithMetadata) and self._data.invocation.metadata:
+ metadata_ = self._data.invocation.metadata.model_dump_json()
+
+ board_id_ = None
+ if board_id:
+ board_id_ = board_id
+ elif isinstance(self._data.invocation, WithBoard) and self._data.invocation.board:
+ board_id_ = self._data.invocation.board.board_id
+
+ if board_id_ is not None:
+ board = self._services.boards.get_dto(board_id_)
+ user = self._services.users.get(self._data.queue_item.user_id)
+ if user is None or (
+ not user.is_admin
+ and board.user_id != self._data.queue_item.user_id
+ and board.board_visibility != BoardVisibility.Public
+ ):
+ raise PermissionError("Queue user is not authorized to save videos to this board")
+
+ workflow_ = None
+ if self._data.queue_item.workflow:
+ workflow_ = self._data.queue_item.workflow.model_dump_json()
+
+ graph_ = None
+ if self._data.queue_item.session.graph:
+ graph_ = self._data.queue_item.session.graph.model_dump_json()
+
+ return self._services.videos.create(
+ source_path=source_path,
+ width=width,
+ height=height,
+ duration=duration,
+ fps=fps,
+ is_intermediate=self._data.invocation.is_intermediate,
+ video_category=image_category,
+ board_id=board_id_,
+ metadata=metadata_,
+ video_origin=ResourceOrigin.INTERNAL,
+ workflow=workflow_,
+ graph=graph_,
+ session_id=self._data.queue_item.session_id,
+ node_id=self._data.invocation.id,
+ user_id=self._data.queue_item.user_id,
+ )
+
+ def get_dto(self, video_name: str) -> VideoDTO:
+ """Get a video DTO by name."""
+ self._assert_read_access(video_name)
+ return self._services.videos.get_dto(video_name)
+
+ def get_path(self, video_name: str, thumbnail: bool = False) -> Path:
+ """Get the on-disk path to a video file or its WebP thumbnail."""
+ self._assert_read_access(video_name)
+ return Path(self._services.videos.get_path(video_name, thumbnail=thumbnail))
+
+
class TensorsInterface(InvocationContextInterface):
def save(self, tensor: Tensor) -> str:
"""Saves a tensor, returning its name.
@@ -736,6 +846,7 @@ class InvocationContext:
def __init__(
self,
images: ImagesInterface,
+ videos: VideosInterface,
tensors: TensorsInterface,
conditioning: ConditioningInterface,
models: ModelsInterface,
@@ -748,6 +859,8 @@ def __init__(
) -> None:
self.images = images
"""Methods to save, get and update images and their metadata."""
+ self.videos = videos
+ """Methods to save and get videos produced by invocations."""
self.tensors = tensors
"""Methods to save and get tensors, including image, noise, masks, and masked images."""
self.conditioning = conditioning
@@ -790,10 +903,12 @@ def build_invocation_context(
conditioning = ConditioningInterface(services=services, data=data)
models = ModelsInterface(services=services, data=data, util=util)
images = ImagesInterface(services=services, data=data, util=util)
+ videos = VideosInterface(services=services, data=data, util=util)
boards = BoardsInterface(services=services, data=data)
ctx = InvocationContext(
images=images,
+ videos=videos,
logger=logger,
config=config,
tensors=tensors,
diff --git a/invokeai/app/services/shared/sqlite_migrator/migrations/migration_2026_07_01_add_videos_tables.py b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_2026_07_01_add_videos_tables.py
new file mode 100644
index 00000000000..86dabdb0014
--- /dev/null
+++ b/invokeai/app/services/shared/sqlite_migrator/migrations/migration_2026_07_01_add_videos_tables.py
@@ -0,0 +1,124 @@
+"""Migration: Add `videos` and `board_videos` tables for minimal video support.
+
+The `videos` table parallels `images` but with extra `duration` and `fps` columns.
+The `board_videos` table parallels `board_images`, providing one-to-many board↔video association.
+Foreign-key cascades from `boards` mirror the image side, so deleting a board also removes its videos' associations.
+"""
+
+import sqlite3
+
+from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_common import Migration
+
+
+class AddVideosTablesCallback:
+ def __call__(self, cursor: sqlite3.Cursor) -> None:
+ self._create_videos(cursor)
+ self._create_board_videos(cursor)
+
+ def _create_videos(self, cursor: sqlite3.Cursor) -> None:
+ tables = [
+ """--sql
+ CREATE TABLE IF NOT EXISTS videos (
+ video_name TEXT NOT NULL PRIMARY KEY,
+ video_origin TEXT NOT NULL,
+ video_category TEXT NOT NULL,
+ width INTEGER NOT NULL,
+ height INTEGER NOT NULL,
+ duration REAL NOT NULL DEFAULT 0.0,
+ fps REAL,
+ session_id TEXT,
+ node_id TEXT,
+ metadata TEXT,
+ is_intermediate BOOLEAN DEFAULT FALSE,
+ starred BOOLEAN DEFAULT FALSE,
+ has_workflow BOOLEAN DEFAULT FALSE,
+ -- Deliberately no FK to users(user_id), matching images/boards/workflows
+ -- (migration_27 adds those user_id columns with an index only): deleting a
+ -- user leaves their media in place for admin review/cleanup rather than
+ -- cascading a row delete that would strand the files on disk.
+ user_id TEXT NOT NULL DEFAULT 'system',
+ video_subfolder TEXT NOT NULL DEFAULT '',
+ created_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
+ -- Updated via trigger
+ updated_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
+ -- Soft delete, currently unused
+ deleted_at DATETIME
+ );
+ """
+ ]
+
+ indices = [
+ "CREATE UNIQUE INDEX IF NOT EXISTS idx_videos_video_name ON videos(video_name);",
+ "CREATE INDEX IF NOT EXISTS idx_videos_video_origin ON videos(video_origin);",
+ "CREATE INDEX IF NOT EXISTS idx_videos_video_category ON videos(video_category);",
+ "CREATE INDEX IF NOT EXISTS idx_videos_created_at ON videos(created_at);",
+ "CREATE INDEX IF NOT EXISTS idx_videos_starred ON videos(starred);",
+ "CREATE INDEX IF NOT EXISTS idx_videos_user_id ON videos(user_id);",
+ ]
+
+ triggers = [
+ """--sql
+ CREATE TRIGGER IF NOT EXISTS tg_videos_updated_at
+ AFTER UPDATE
+ ON videos FOR EACH ROW
+ BEGIN
+ UPDATE videos SET updated_at = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')
+ WHERE video_name = old.video_name;
+ END;
+ """
+ ]
+
+ for stmt in tables + indices + triggers:
+ cursor.execute(stmt)
+
+ def _create_board_videos(self, cursor: sqlite3.Cursor) -> None:
+ tables = [
+ """--sql
+ CREATE TABLE IF NOT EXISTS board_videos (
+ board_id TEXT NOT NULL,
+ video_name TEXT NOT NULL,
+ created_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
+ -- updated via trigger
+ updated_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
+ -- Soft delete, currently unused
+ deleted_at DATETIME,
+ -- enforce one-to-many board↔video using PK on video_name
+ PRIMARY KEY (video_name),
+ FOREIGN KEY (board_id) REFERENCES boards (board_id) ON DELETE CASCADE,
+ FOREIGN KEY (video_name) REFERENCES videos (video_name) ON DELETE CASCADE
+ );
+ """
+ ]
+
+ indices = [
+ "CREATE INDEX IF NOT EXISTS idx_board_videos_board_id ON board_videos (board_id);",
+ "CREATE INDEX IF NOT EXISTS idx_board_videos_board_id_created_at ON board_videos (board_id, created_at);",
+ ]
+
+ triggers = [
+ """--sql
+ CREATE TRIGGER IF NOT EXISTS tg_board_videos_updated_at
+ AFTER UPDATE
+ ON board_videos FOR EACH ROW
+ BEGIN
+ UPDATE board_videos SET updated_at = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')
+ WHERE board_id = old.board_id AND video_name = old.video_name;
+ END;
+ """
+ ]
+
+ for stmt in tables + indices + triggers:
+ cursor.execute(stmt)
+
+
+def build_migration() -> Migration:
+ """Builds the migration that adds the videos and board_videos tables.
+
+ Depends on migration_27, which last reshaped the boards table and created the 'system' user
+ that videos.user_id defaults to.
+ """
+ return Migration(
+ id="2026_07_01_add_videos_tables",
+ depends_on="migration_27",
+ callback=AddVideosTablesCallback(),
+ )
diff --git a/invokeai/app/services/urls/urls_base.py b/invokeai/app/services/urls/urls_base.py
index a5602abb3b4..c566f03f309 100644
--- a/invokeai/app/services/urls/urls_base.py
+++ b/invokeai/app/services/urls/urls_base.py
@@ -9,6 +9,11 @@ def get_image_url(self, image_name: str, thumbnail: bool = False) -> str:
"""Gets the URL for an image or thumbnail."""
pass
+ @abstractmethod
+ def get_video_url(self, video_name: str, thumbnail: bool = False) -> str:
+ """Gets the URL for a video or its first-frame thumbnail."""
+ pass
+
@abstractmethod
def get_model_image_url(self, model_key: str) -> str:
"""Gets the URL for a model image"""
diff --git a/invokeai/app/services/urls/urls_default.py b/invokeai/app/services/urls/urls_default.py
index 2e4f36d9d51..cb21bd02229 100644
--- a/invokeai/app/services/urls/urls_default.py
+++ b/invokeai/app/services/urls/urls_default.py
@@ -17,6 +17,15 @@ def get_image_url(self, image_name: str, thumbnail: bool = False) -> str:
return f"{self._base_url}/images/i/{image_basename}/full"
+ def get_video_url(self, video_name: str, thumbnail: bool = False) -> str:
+ video_basename = os.path.basename(video_name)
+
+ # These paths are determined by the routes in invokeai/app/api/routers/videos.py
+ if thumbnail:
+ return f"{self._base_url}/videos/i/{video_basename}/thumbnail"
+
+ return f"{self._base_url}/videos/i/{video_basename}/full"
+
def get_model_image_url(self, model_key: str) -> str:
return f"{self._base_url_v2}/models/i/{model_key}/image"
diff --git a/invokeai/app/services/video_files/__init__.py b/invokeai/app/services/video_files/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/invokeai/app/services/video_files/video_files_base.py b/invokeai/app/services/video_files/video_files_base.py
new file mode 100644
index 00000000000..3223877bb4c
--- /dev/null
+++ b/invokeai/app/services/video_files/video_files_base.py
@@ -0,0 +1,63 @@
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import Optional
+
+
+class VideoFileStorageBase(ABC):
+ """Low-level service responsible for storing and retrieving video files."""
+
+ @abstractmethod
+ def get_path(self, video_name: str, thumbnail: bool = False, video_subfolder: str = "") -> Path:
+ """Gets the internal path to a video or its thumbnail."""
+ pass
+
+ @abstractmethod
+ def save(
+ self,
+ source_path: Path,
+ video_name: str,
+ thumbnail_size: int = 256,
+ video_subfolder: str = "",
+ metadata: Optional[str] = None,
+ workflow: Optional[str] = None,
+ graph: Optional[str] = None,
+ ) -> None:
+ """Saves a video by moving/copying the file at `source_path` into storage, then writes a sibling
+ WEBP thumbnail extracted from the first frame, plus an optional sidecar JSON of metadata/workflow/graph.
+ """
+ pass
+
+ @abstractmethod
+ def delete(self, video_name: str, video_subfolder: str = "") -> None:
+ """Deletes a video file and its thumbnail (if one exists)."""
+ pass
+
+ @abstractmethod
+ def stage_delete(self, video_name: str, video_subfolder: str = "") -> object:
+ """Moves a video's files out of service and returns a rollback token."""
+ pass
+
+ @abstractmethod
+ def commit_delete(self, token: object) -> None:
+ """Permanently removes files represented by a staged-delete token."""
+ pass
+
+ @abstractmethod
+ def rollback_delete(self, token: object) -> None:
+ """Restores files represented by a staged-delete token."""
+ pass
+
+ @abstractmethod
+ def get_workflow(self, video_name: str, video_subfolder: str = "") -> Optional[str]:
+ """Gets the workflow JSON sidecar of a video, if any."""
+ pass
+
+ @abstractmethod
+ def get_graph(self, video_name: str, video_subfolder: str = "") -> Optional[str]:
+ """Gets the graph JSON sidecar of a video, if any."""
+ pass
+
+ @abstractmethod
+ def validate_path(self, path: str) -> bool:
+ """Validates the path given for a video or thumbnail."""
+ pass
diff --git a/invokeai/app/services/video_files/video_files_common.py b/invokeai/app/services/video_files/video_files_common.py
new file mode 100644
index 00000000000..4743d8a7cc8
--- /dev/null
+++ b/invokeai/app/services/video_files/video_files_common.py
@@ -0,0 +1,19 @@
+class VideoFileNotFoundException(Exception):
+ """Raised when a video file is not found in storage."""
+
+ def __init__(self, message="Video file not found"):
+ super().__init__(message)
+
+
+class VideoFileSaveException(Exception):
+ """Raised when a video file cannot be saved."""
+
+ def __init__(self, message="Video file not saved"):
+ super().__init__(message)
+
+
+class VideoFileDeleteException(Exception):
+ """Raised when a video file cannot be deleted."""
+
+ def __init__(self, message="Video file not deleted"):
+ super().__init__(message)
diff --git a/invokeai/app/services/video_files/video_files_disk.py b/invokeai/app/services/video_files/video_files_disk.py
new file mode 100644
index 00000000000..0304a3d74b1
--- /dev/null
+++ b/invokeai/app/services/video_files/video_files_disk.py
@@ -0,0 +1,273 @@
+import json
+import os
+import shutil
+import tempfile
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Optional, Union
+
+from invokeai.app.services.invoker import Invoker
+from invokeai.app.services.video_files.video_files_base import VideoFileStorageBase
+from invokeai.app.services.video_files.video_files_common import (
+ VideoFileDeleteException,
+ VideoFileNotFoundException,
+ VideoFileSaveException,
+)
+from invokeai.app.util.thumbnails import make_thumbnail
+from invokeai.app.util.video_thumbnails import extract_video_frame, get_video_thumbnail_name
+from invokeai.backend.util.logging import InvokeAILogger
+
+
+@dataclass
+class _StagedDelete:
+ directory: Path
+ files: list[tuple[Path, Path]]
+
+
+class DiskVideoFileStorage(VideoFileStorageBase):
+ """Stores video files on disk under {outputs}/videos/, with first-frame WebP thumbnails under
+ {outputs}/videos/thumbnails/ and optional JSON sidecars for metadata/workflow/graph under
+ {outputs}/videos/sidecars/."""
+
+ def __init__(self, output_folder: Union[str, Path]):
+ self.__output_folder = output_folder if isinstance(output_folder, Path) else Path(output_folder)
+ self.__thumbnails_folder = self.__output_folder / "thumbnails"
+ self.__sidecars_folder = self.__output_folder / "sidecars"
+ self.__validate_storage_folders()
+
+ def start(self, invoker: Invoker) -> None:
+ self.__invoker = invoker
+ self.__recover_staged_deletes()
+
+ def save(
+ self,
+ source_path: Path,
+ video_name: str,
+ thumbnail_size: int = 256,
+ video_subfolder: str = "",
+ metadata: Optional[str] = None,
+ workflow: Optional[str] = None,
+ graph: Optional[str] = None,
+ ) -> None:
+ logger = InvokeAILogger.get_logger()
+ try:
+ self.__validate_storage_folders()
+ video_path = self.get_path(video_name, video_subfolder=video_subfolder)
+ video_path.parent.mkdir(parents=True, exist_ok=True)
+
+ # Move if the source is on the same filesystem; otherwise copy then unlink.
+ try:
+ shutil.move(str(source_path), str(video_path))
+ except Exception:
+ shutil.copy2(str(source_path), str(video_path))
+ try:
+ Path(source_path).unlink(missing_ok=True)
+ except Exception:
+ pass
+ logger.info(f"Video file written: {video_path}")
+
+ thumbnail_name = get_video_thumbnail_name(video_name)
+ thumbnail_path = self.get_path(thumbnail_name, thumbnail=True, video_subfolder=video_subfolder)
+ thumbnail_path.parent.mkdir(parents=True, exist_ok=True)
+
+ # Thumbnail extraction is best-effort — if both imageio and cv2 fail, we still want
+ # the video record + file in place and the invocation to complete. A missing
+ # thumbnail leaves the gallery with a broken-image placeholder for that item, which
+ # is annoying but not fatal.
+ try:
+ frame = extract_video_frame(video_path, frame_index=0)
+ except Exception as e:
+ logger.warning(f"Thumbnail extraction raised for {video_name}: {e}")
+ frame = None
+ if frame is not None:
+ thumbnail = make_thumbnail(frame, thumbnail_size)
+ thumbnail.save(thumbnail_path, "WEBP")
+ logger.info(f"Thumbnail written: {thumbnail_path}")
+ else:
+ logger.warning(
+ f"Could not extract a thumbnail frame for {video_name}; gallery thumbnail will be missing."
+ )
+
+ if metadata is not None or workflow is not None or graph is not None:
+ sidecar_path = self.__get_sidecar_path(video_name, video_subfolder=video_subfolder)
+ sidecar_path.parent.mkdir(parents=True, exist_ok=True)
+ sidecar = {
+ "invokeai_metadata": metadata,
+ "invokeai_workflow": workflow,
+ "invokeai_graph": graph,
+ }
+ with open(sidecar_path, "w", encoding="utf-8") as f:
+ json.dump(sidecar, f)
+ logger.info(f"Sidecar written: {sidecar_path}")
+ except Exception as e:
+ # By this point the source MP4 has usually already been moved into permanent
+ # storage, so bailing out without cleanup would orphan the video (and any
+ # partially written thumbnail/sidecar) on disk with no DB record through which
+ # it can be managed — the caller rolls the record back on this exception.
+ try:
+ self.delete(video_name, video_subfolder=video_subfolder)
+ except Exception as cleanup_err:
+ logger.error(f"Failed to clean up partially saved files for {video_name}: {cleanup_err}")
+ raise VideoFileSaveException from e
+
+ def delete(self, video_name: str, video_subfolder: str = "") -> None:
+ token = self.stage_delete(video_name, video_subfolder)
+ self.commit_delete(token)
+
+ def stage_delete(self, video_name: str, video_subfolder: str = "") -> _StagedDelete:
+ staging_dir = Path(tempfile.mkdtemp(prefix=".delete_", dir=self.__output_folder))
+ candidates = [
+ self.get_path(video_name, video_subfolder=video_subfolder),
+ self.get_path(video_name, thumbnail=True, video_subfolder=video_subfolder),
+ self.__get_sidecar_path(video_name, video_subfolder=video_subfolder),
+ ]
+ staged: list[tuple[Path, Path]] = []
+ try:
+ with open(staging_dir / "manifest.json", "w", encoding="utf-8") as manifest:
+ manifest.write(json.dumps({"video_name": video_name, "video_subfolder": video_subfolder}))
+ manifest.flush()
+ os.fsync(manifest.fileno())
+ for index, source in enumerate(candidates):
+ if source.exists():
+ destination = staging_dir / str(index)
+ source.replace(destination)
+ staged.append((source, destination))
+ return _StagedDelete(directory=staging_dir, files=staged)
+ except Exception as e:
+ for source, destination in reversed(staged):
+ if destination.exists():
+ source.parent.mkdir(parents=True, exist_ok=True)
+ destination.replace(source)
+ shutil.rmtree(staging_dir, ignore_errors=True)
+ raise VideoFileDeleteException from e
+
+ def commit_delete(self, token: object) -> None:
+ if not isinstance(token, _StagedDelete):
+ raise VideoFileDeleteException("Invalid staged-delete token")
+ shutil.rmtree(token.directory)
+
+ def rollback_delete(self, token: object) -> None:
+ if not isinstance(token, _StagedDelete):
+ raise VideoFileDeleteException("Invalid staged-delete token")
+ try:
+ for source, destination in reversed(token.files):
+ if destination.exists():
+ source.parent.mkdir(parents=True, exist_ok=True)
+ destination.replace(source)
+ shutil.rmtree(token.directory, ignore_errors=True)
+ except Exception as e:
+ raise VideoFileDeleteException from e
+
+ def get_path(self, video_name: str, thumbnail: bool = False, video_subfolder: str = "") -> Path:
+ base_folder = self.__thumbnails_folder if thumbnail else self.__output_folder
+ filename = get_video_thumbnail_name(video_name) if thumbnail else video_name
+
+ basename = Path(filename).name
+ if basename != filename:
+ raise ValueError("Invalid video name, potential directory traversal detected")
+
+ if video_subfolder:
+ self._validate_subfolder(video_subfolder)
+ video_path = base_folder / video_subfolder / basename
+ else:
+ video_path = base_folder / basename
+
+ resolved_base = base_folder.resolve()
+ resolved_video_path = video_path.resolve()
+ if not resolved_video_path.is_relative_to(resolved_base):
+ raise ValueError("Video path outside outputs folder, potential directory traversal detected")
+ return resolved_video_path
+
+ def get_workflow(self, video_name: str, video_subfolder: str = "") -> Optional[str]:
+ sidecar = self.__read_sidecar(video_name, video_subfolder)
+ if sidecar is None:
+ return None
+ workflow = sidecar.get("invokeai_workflow")
+ return workflow if isinstance(workflow, str) else None
+
+ def get_graph(self, video_name: str, video_subfolder: str = "") -> Optional[str]:
+ sidecar = self.__read_sidecar(video_name, video_subfolder)
+ if sidecar is None:
+ return None
+ graph = sidecar.get("invokeai_graph")
+ return graph if isinstance(graph, str) else None
+
+ def validate_path(self, path: Union[str, Path]) -> bool:
+ path = path if isinstance(path, Path) else Path(path)
+ return path.exists()
+
+ @staticmethod
+ def _validate_subfolder(subfolder: str) -> None:
+ """Validates a subfolder path to prevent directory traversal."""
+ if not subfolder:
+ return
+ if "\\" in subfolder:
+ raise ValueError("Backslashes not allowed in subfolder path")
+ if subfolder.startswith("/"):
+ raise ValueError("Absolute paths not allowed in subfolder path")
+ for part in subfolder.split("/"):
+ if part == "..":
+ raise ValueError("Parent directory references not allowed in subfolder path")
+ if part == "":
+ raise ValueError("Empty path segments not allowed in subfolder path")
+
+ def __get_sidecar_path(self, video_name: str, video_subfolder: str = "") -> Path:
+ sidecar_name = Path(video_name).stem + ".json"
+ if video_subfolder:
+ self._validate_subfolder(video_subfolder)
+ sidecar_path = self.__sidecars_folder / video_subfolder / sidecar_name
+ else:
+ sidecar_path = self.__sidecars_folder / sidecar_name
+ resolved_base = self.__sidecars_folder.resolve()
+ resolved_sidecar_path = sidecar_path.resolve()
+ if not resolved_sidecar_path.is_relative_to(resolved_base):
+ raise ValueError("Sidecar path outside outputs folder, potential directory traversal detected")
+ return resolved_sidecar_path
+
+ def __read_sidecar(self, video_name: str, video_subfolder: str = "") -> Optional[dict]:
+ path = self.__get_sidecar_path(video_name, video_subfolder=video_subfolder)
+ if not path.exists():
+ return None
+ try:
+ with open(path, encoding="utf-8") as f:
+ return json.load(f)
+ except Exception as e:
+ raise VideoFileNotFoundException from e
+
+ def __validate_storage_folders(self) -> None:
+ for folder in (self.__output_folder, self.__thumbnails_folder, self.__sidecars_folder):
+ folder.mkdir(parents=True, exist_ok=True)
+
+ def __recover_staged_deletes(self) -> None:
+ logger = InvokeAILogger.get_logger()
+ for staging_dir in self.__output_folder.glob(".delete_*"):
+ manifest_path = staging_dir / "manifest.json"
+ if not manifest_path.is_file():
+ if not any(staging_dir.iterdir()):
+ staging_dir.rmdir()
+ continue
+ try:
+ with open(manifest_path, encoding="utf-8") as manifest:
+ data = json.load(manifest)
+ video_name = data["video_name"]
+ video_subfolder = data.get("video_subfolder", "")
+ candidates = [
+ self.get_path(video_name, video_subfolder=video_subfolder),
+ self.get_path(video_name, thumbnail=True, video_subfolder=video_subfolder),
+ self.__get_sidecar_path(video_name, video_subfolder=video_subfolder),
+ ]
+ token = _StagedDelete(
+ directory=staging_dir,
+ files=[(source, staging_dir / str(index)) for index, source in enumerate(candidates)],
+ )
+ if self.__invoker.services.video_records.get(video_name) is None:
+ self.commit_delete(token)
+ else:
+ self.rollback_delete(token)
+ except Exception as error:
+ from invokeai.app.services.video_records.video_records_common import VideoRecordNotFoundException
+
+ if isinstance(error, VideoRecordNotFoundException):
+ shutil.rmtree(staging_dir, ignore_errors=True)
+ else:
+ logger.error(f"Failed to recover staged video deletion {staging_dir}: {error}")
diff --git a/invokeai/app/services/video_records/__init__.py b/invokeai/app/services/video_records/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/invokeai/app/services/video_records/video_records_base.py b/invokeai/app/services/video_records/video_records_base.py
new file mode 100644
index 00000000000..8492e4c4bf0
--- /dev/null
+++ b/invokeai/app/services/video_records/video_records_base.py
@@ -0,0 +1,108 @@
+from abc import ABC, abstractmethod
+from datetime import datetime
+from typing import Optional
+
+from invokeai.app.invocations.fields import MetadataField
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.video_records.video_records_common import (
+ VideoNamesResult,
+ VideoRecord,
+ VideoRecordChanges,
+)
+
+
+class VideoRecordStorageBase(ABC):
+ """Low-level service responsible for interfacing with the video record store."""
+
+ @abstractmethod
+ def get(self, video_name: str) -> VideoRecord:
+ """Gets a video record."""
+ pass
+
+ @abstractmethod
+ def get_metadata(self, video_name: str) -> Optional[MetadataField]:
+ """Gets a video's metadata."""
+ pass
+
+ @abstractmethod
+ def update(self, video_name: str, changes: VideoRecordChanges) -> None:
+ """Updates a video record."""
+ pass
+
+ @abstractmethod
+ def get_many(
+ self,
+ offset: int = 0,
+ limit: int = 10,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> OffsetPaginatedResults[VideoRecord]:
+ """Gets a page of video records."""
+ pass
+
+ @abstractmethod
+ def delete(self, video_name: str) -> None:
+ """Deletes a video record."""
+ pass
+
+ @abstractmethod
+ def delete_many(self, video_names: list[str]) -> None:
+ """Deletes many video records."""
+ pass
+
+ @abstractmethod
+ def save(
+ self,
+ video_name: str,
+ video_origin: ResourceOrigin,
+ video_category: ImageCategory,
+ width: int,
+ height: int,
+ duration: float,
+ fps: Optional[float],
+ has_workflow: bool,
+ is_intermediate: Optional[bool] = False,
+ starred: Optional[bool] = False,
+ session_id: Optional[str] = None,
+ node_id: Optional[str] = None,
+ metadata: Optional[str] = None,
+ user_id: Optional[str] = None,
+ video_subfolder: str = "",
+ ) -> datetime:
+ """Saves a video record."""
+ pass
+
+ @abstractmethod
+ def get_user_id(self, video_name: str) -> Optional[str]:
+ """Gets the user_id of the video owner. Returns None if video not found."""
+ pass
+
+ @abstractmethod
+ def get_most_recent_video_for_board(self, board_id: str) -> Optional[VideoRecord]:
+ """Gets the most recent non-intermediate video on a board, preferring starred videos."""
+ pass
+
+ @abstractmethod
+ def get_video_names(
+ self,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> VideoNamesResult:
+ """Gets ordered list of video names with metadata for optimistic updates."""
+ pass
diff --git a/invokeai/app/services/video_records/video_records_common.py b/invokeai/app/services/video_records/video_records_common.py
new file mode 100644
index 00000000000..d67ae657ecb
--- /dev/null
+++ b/invokeai/app/services/video_records/video_records_common.py
@@ -0,0 +1,137 @@
+import datetime
+from typing import Optional, Union
+
+from pydantic import BaseModel, Field, StrictBool, StrictStr
+
+from invokeai.app.services.image_records.image_records_common import (
+ ImageCategory,
+ ResourceOrigin,
+)
+from invokeai.app.util.misc import get_iso_timestamp
+from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
+
+
+class VideoRecordNotFoundException(Exception):
+ """Raised when a video record is not found."""
+
+ def __init__(self, message="Video record not found"):
+ super().__init__(message)
+
+
+class VideoRecordSaveException(Exception):
+ """Raised when a video record cannot be saved."""
+
+ def __init__(self, message="Video record not saved"):
+ super().__init__(message)
+
+
+class VideoRecordDeleteException(Exception):
+ """Raised when a video record cannot be deleted."""
+
+ def __init__(self, message="Video record not deleted"):
+ super().__init__(message)
+
+
+VIDEO_DTO_COLS = ", ".join(
+ [
+ "videos." + c
+ for c in [
+ "video_name",
+ "video_origin",
+ "video_category",
+ "width",
+ "height",
+ "duration",
+ "fps",
+ "session_id",
+ "node_id",
+ "has_workflow",
+ "is_intermediate",
+ "created_at",
+ "updated_at",
+ "deleted_at",
+ "starred",
+ "video_subfolder",
+ ]
+ ]
+)
+
+
+class VideoRecord(BaseModelExcludeNull):
+ """Deserialized video record without metadata."""
+
+ video_name: str = Field(description="The unique name of the video.")
+ video_origin: ResourceOrigin = Field(description="The origin of the video.")
+ video_category: ImageCategory = Field(description="The category of the video (reuses ImageCategory).")
+ width: int = Field(description="The pixel width of the video.")
+ height: int = Field(description="The pixel height of the video.")
+ duration: float = Field(description="The duration of the video in seconds.")
+ fps: Optional[float] = Field(default=None, description="The frames-per-second of the video, if known.")
+ created_at: Union[datetime.datetime, str] = Field(description="The created timestamp of the video.")
+ updated_at: Union[datetime.datetime, str] = Field(description="The updated timestamp of the video.")
+ deleted_at: Optional[Union[datetime.datetime, str]] = Field(
+ default=None, description="The deleted timestamp of the video."
+ )
+ is_intermediate: bool = Field(description="Whether this is an intermediate video.")
+ session_id: Optional[str] = Field(default=None, description="The session ID that produced this video, if any.")
+ node_id: Optional[str] = Field(default=None, description="The node ID that produced this video, if any.")
+ starred: bool = Field(description="Whether this video is starred.")
+ has_workflow: bool = Field(description="Whether this video has a workflow associated.")
+ video_subfolder: str = Field(default="", description="The subfolder where the video is stored on disk.")
+
+
+class VideoRecordChanges(BaseModelExcludeNull, extra="allow"):
+ """Allowed mutations on a video record."""
+
+ video_category: Optional[ImageCategory] = Field(default=None, description="The video's new category.")
+ session_id: Optional[StrictStr] = Field(default=None, description="The video's new session ID.")
+ is_intermediate: Optional[StrictBool] = Field(default=None, description="The video's new `is_intermediate` flag.")
+ starred: Optional[StrictBool] = Field(default=None, description="The video's new `starred` state.")
+
+
+def deserialize_video_record(video_dict: dict) -> VideoRecord:
+ """Deserializes a video record from a sqlite row dict."""
+ video_name = video_dict.get("video_name", "unknown")
+ video_origin = ResourceOrigin(video_dict.get("video_origin", ResourceOrigin.INTERNAL.value))
+ video_category = ImageCategory(video_dict.get("video_category", ImageCategory.GENERAL.value))
+ width = video_dict.get("width", 0)
+ height = video_dict.get("height", 0)
+ duration = video_dict.get("duration", 0.0)
+ fps_raw = video_dict.get("fps", None)
+ fps = float(fps_raw) if fps_raw is not None else None
+ session_id = video_dict.get("session_id", None)
+ node_id = video_dict.get("node_id", None)
+ created_at = video_dict.get("created_at", get_iso_timestamp())
+ updated_at = video_dict.get("updated_at", get_iso_timestamp())
+ deleted_at = video_dict.get("deleted_at", None)
+ is_intermediate = video_dict.get("is_intermediate", False)
+ starred = video_dict.get("starred", False)
+ has_workflow = video_dict.get("has_workflow", False)
+ video_subfolder = video_dict.get("video_subfolder", "")
+
+ return VideoRecord(
+ video_name=video_name,
+ video_origin=video_origin,
+ video_category=video_category,
+ width=width,
+ height=height,
+ duration=float(duration),
+ fps=fps,
+ session_id=session_id,
+ node_id=node_id,
+ created_at=created_at,
+ updated_at=updated_at,
+ deleted_at=deleted_at,
+ is_intermediate=is_intermediate,
+ starred=starred,
+ has_workflow=has_workflow,
+ video_subfolder=video_subfolder,
+ )
+
+
+class VideoNamesResult(BaseModel):
+ """Response containing ordered video names with metadata for optimistic updates."""
+
+ video_names: list[str] = Field(description="Ordered list of video names")
+ starred_count: int = Field(description="Number of starred videos (when starred_first=True)")
+ total_count: int = Field(description="Total number of videos matching the query")
diff --git a/invokeai/app/services/video_records/video_records_sqlite.py b/invokeai/app/services/video_records/video_records_sqlite.py
new file mode 100644
index 00000000000..c7e7214fe13
--- /dev/null
+++ b/invokeai/app/services/video_records/video_records_sqlite.py
@@ -0,0 +1,375 @@
+import sqlite3
+from datetime import datetime
+from typing import Optional, Union, cast
+
+from invokeai.app.invocations.fields import MetadataField, MetadataFieldValidator
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
+from invokeai.app.services.video_records.video_records_base import VideoRecordStorageBase
+from invokeai.app.services.video_records.video_records_common import (
+ VIDEO_DTO_COLS,
+ VideoNamesResult,
+ VideoRecord,
+ VideoRecordChanges,
+ VideoRecordDeleteException,
+ VideoRecordNotFoundException,
+ VideoRecordSaveException,
+ deserialize_video_record,
+)
+
+
+class SqliteVideoRecordStorage(VideoRecordStorageBase):
+ def __init__(self, db: SqliteDatabase) -> None:
+ super().__init__()
+ self._db = db
+
+ def get(self, video_name: str) -> VideoRecord:
+ with self._db.transaction() as cursor:
+ try:
+ cursor.execute(
+ f"""--sql
+ SELECT {VIDEO_DTO_COLS} FROM videos
+ WHERE video_name = ?;
+ """,
+ (video_name,),
+ )
+ result = cast(Optional[sqlite3.Row], cursor.fetchone())
+ except sqlite3.Error as e:
+ raise VideoRecordNotFoundException from e
+
+ if not result:
+ raise VideoRecordNotFoundException
+ return deserialize_video_record(dict(result))
+
+ def get_user_id(self, video_name: str) -> Optional[str]:
+ with self._db.transaction() as cursor:
+ cursor.execute(
+ """--sql
+ SELECT user_id FROM videos
+ WHERE video_name = ?;
+ """,
+ (video_name,),
+ )
+ result = cast(Optional[sqlite3.Row], cursor.fetchone())
+ if not result:
+ return None
+ return cast(Optional[str], dict(result).get("user_id"))
+
+ def get_most_recent_video_for_board(self, board_id: str) -> Optional[VideoRecord]:
+ with self._db.transaction() as cursor:
+ cursor.execute(
+ f"""--sql
+ SELECT {VIDEO_DTO_COLS}
+ FROM videos
+ JOIN board_videos ON videos.video_name = board_videos.video_name
+ WHERE board_videos.board_id = ?
+ AND videos.is_intermediate = FALSE
+ ORDER BY videos.starred DESC, videos.created_at DESC
+ LIMIT 1;
+ """,
+ (board_id,),
+ )
+ result = cast(Optional[sqlite3.Row], cursor.fetchone())
+ if result is None:
+ return None
+ return deserialize_video_record(dict(result))
+
+ def get_metadata(self, video_name: str) -> Optional[MetadataField]:
+ with self._db.transaction() as cursor:
+ try:
+ cursor.execute(
+ """--sql
+ SELECT metadata FROM videos
+ WHERE video_name = ?;
+ """,
+ (video_name,),
+ )
+ result = cast(Optional[sqlite3.Row], cursor.fetchone())
+ except sqlite3.Error as e:
+ raise VideoRecordNotFoundException from e
+
+ if not result:
+ raise VideoRecordNotFoundException
+
+ as_dict = dict(result)
+ metadata_raw = cast(Optional[str], as_dict.get("metadata", None))
+ return MetadataFieldValidator.validate_json(metadata_raw) if metadata_raw is not None else None
+
+ def update(self, video_name: str, changes: VideoRecordChanges) -> None:
+ with self._db.transaction() as cursor:
+ try:
+ if changes.video_category is not None:
+ cursor.execute(
+ "UPDATE videos SET video_category = ? WHERE video_name = ?;",
+ (changes.video_category.value, video_name),
+ )
+ if changes.session_id is not None:
+ cursor.execute(
+ "UPDATE videos SET session_id = ? WHERE video_name = ?;",
+ (changes.session_id, video_name),
+ )
+ if changes.is_intermediate is not None:
+ cursor.execute(
+ "UPDATE videos SET is_intermediate = ? WHERE video_name = ?;",
+ (changes.is_intermediate, video_name),
+ )
+ if changes.starred is not None:
+ cursor.execute(
+ "UPDATE videos SET starred = ? WHERE video_name = ?;",
+ (changes.starred, video_name),
+ )
+ except sqlite3.Error as e:
+ raise VideoRecordSaveException from e
+
+ def get_many(
+ self,
+ offset: int = 0,
+ limit: int = 10,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> OffsetPaginatedResults[VideoRecord]:
+ with self._db.transaction() as cursor:
+ count_query = """--sql
+ SELECT COUNT(*)
+ FROM videos
+ LEFT JOIN board_videos ON board_videos.video_name = videos.video_name
+ WHERE 1=1
+ """
+ videos_query = f"""--sql
+ SELECT {VIDEO_DTO_COLS}
+ FROM videos
+ LEFT JOIN board_videos ON board_videos.video_name = videos.video_name
+ WHERE 1=1
+ """
+
+ query_conditions = ""
+ query_params: list[Union[int, str, bool]] = []
+
+ if video_origin is not None:
+ query_conditions += " AND videos.video_origin = ? "
+ query_params.append(video_origin.value)
+
+ if categories is not None:
+ category_strings = [c.value for c in set(categories)]
+ placeholders = ",".join("?" * len(category_strings))
+ query_conditions += f" AND videos.video_category IN ( {placeholders} ) "
+ for c in category_strings:
+ query_params.append(c)
+
+ if is_intermediate is not None:
+ query_conditions += " AND videos.is_intermediate = ? "
+ query_params.append(is_intermediate)
+
+ if board_id == "none":
+ query_conditions += " AND board_videos.board_id IS NULL "
+ if user_id is not None and not is_admin:
+ query_conditions += " AND videos.user_id = ? "
+ query_params.append(user_id)
+ elif board_id is not None:
+ query_conditions += " AND board_videos.board_id = ? "
+ query_params.append(board_id)
+ elif user_id is not None and not is_admin:
+ # No board_id supplied — still enforce per-user isolation so
+ # non-admin callers cannot enumerate other users' videos.
+ query_conditions += " AND videos.user_id = ? "
+ query_params.append(user_id)
+
+ if search_term:
+ query_conditions += " AND (videos.metadata LIKE ? OR videos.created_at LIKE ?) "
+ query_params.append(f"%{search_term.lower()}%")
+ query_params.append(f"%{search_term.lower()}%")
+
+ if starred_first:
+ query_pagination = (
+ f" ORDER BY videos.starred DESC, videos.created_at {order_dir.value} LIMIT ? OFFSET ? "
+ )
+ else:
+ query_pagination = f" ORDER BY videos.created_at {order_dir.value} LIMIT ? OFFSET ? "
+
+ videos_query += query_conditions + query_pagination + ";"
+ videos_params = query_params.copy()
+ videos_params.extend([limit, offset])
+ cursor.execute(videos_query, videos_params)
+ result = cast(list[sqlite3.Row], cursor.fetchall())
+ videos = [deserialize_video_record(dict(r)) for r in result]
+
+ count_query += query_conditions + ";"
+ cursor.execute(count_query, query_params.copy())
+ count = cast(int, cursor.fetchone()[0])
+
+ return OffsetPaginatedResults(items=videos, offset=offset, limit=limit, total=count)
+
+ def delete(self, video_name: str) -> None:
+ with self._db.transaction() as cursor:
+ try:
+ cursor.execute("DELETE FROM videos WHERE video_name = ?;", (video_name,))
+ except sqlite3.Error as e:
+ raise VideoRecordDeleteException from e
+
+ def delete_many(self, video_names: list[str]) -> None:
+ with self._db.transaction() as cursor:
+ try:
+ placeholders = ",".join("?" for _ in video_names)
+ cursor.execute(f"DELETE FROM videos WHERE video_name IN ({placeholders})", video_names)
+ except sqlite3.Error as e:
+ raise VideoRecordDeleteException from e
+
+ def save(
+ self,
+ video_name: str,
+ video_origin: ResourceOrigin,
+ video_category: ImageCategory,
+ width: int,
+ height: int,
+ duration: float,
+ fps: Optional[float],
+ has_workflow: bool,
+ is_intermediate: Optional[bool] = False,
+ starred: Optional[bool] = False,
+ session_id: Optional[str] = None,
+ node_id: Optional[str] = None,
+ metadata: Optional[str] = None,
+ user_id: Optional[str] = None,
+ video_subfolder: str = "",
+ ) -> datetime:
+ with self._db.transaction() as cursor:
+ try:
+ cursor.execute(
+ """--sql
+ INSERT OR IGNORE INTO videos (
+ video_name,
+ video_origin,
+ video_category,
+ width,
+ height,
+ duration,
+ fps,
+ node_id,
+ session_id,
+ metadata,
+ is_intermediate,
+ starred,
+ has_workflow,
+ user_id,
+ video_subfolder
+ )
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
+ """,
+ (
+ video_name,
+ video_origin.value,
+ video_category.value,
+ width,
+ height,
+ float(duration),
+ float(fps) if fps is not None else None,
+ node_id,
+ session_id,
+ metadata,
+ is_intermediate,
+ starred,
+ has_workflow,
+ user_id or "system",
+ video_subfolder,
+ ),
+ )
+
+ cursor.execute(
+ "SELECT created_at FROM videos WHERE video_name = ?;",
+ (video_name,),
+ )
+ created_at = datetime.fromisoformat(cursor.fetchone()[0])
+ except sqlite3.Error as e:
+ raise VideoRecordSaveException from e
+ return created_at
+
+ def get_video_names(
+ self,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> VideoNamesResult:
+ with self._db.transaction() as cursor:
+ query_conditions = ""
+ query_params: list[Union[int, str, bool]] = []
+
+ if video_origin is not None:
+ query_conditions += " AND videos.video_origin = ? "
+ query_params.append(video_origin.value)
+
+ if categories is not None:
+ category_strings = [c.value for c in set(categories)]
+ placeholders = ",".join("?" * len(category_strings))
+ query_conditions += f" AND videos.video_category IN ( {placeholders} ) "
+ for c in category_strings:
+ query_params.append(c)
+
+ if is_intermediate is not None:
+ query_conditions += " AND videos.is_intermediate = ? "
+ query_params.append(is_intermediate)
+
+ if board_id == "none":
+ query_conditions += " AND board_videos.board_id IS NULL "
+ if user_id is not None and not is_admin:
+ query_conditions += " AND videos.user_id = ? "
+ query_params.append(user_id)
+ elif board_id is not None:
+ query_conditions += " AND board_videos.board_id = ? "
+ query_params.append(board_id)
+ elif user_id is not None and not is_admin:
+ # No board_id supplied — still enforce per-user isolation so
+ # non-admin callers cannot enumerate other users' videos.
+ query_conditions += " AND videos.user_id = ? "
+ query_params.append(user_id)
+
+ if search_term:
+ query_conditions += " AND (videos.metadata LIKE ? OR videos.created_at LIKE ?) "
+ query_params.append(f"%{search_term.lower()}%")
+ query_params.append(f"%{search_term.lower()}%")
+
+ starred_count = 0
+ if starred_first:
+ cursor.execute(
+ f"""--sql
+ SELECT COUNT(*)
+ FROM videos
+ LEFT JOIN board_videos ON board_videos.video_name = videos.video_name
+ WHERE videos.starred = TRUE AND (1=1{query_conditions})
+ """,
+ query_params,
+ )
+ starred_count = cast(int, cursor.fetchone()[0])
+
+ order_clause = (
+ f" ORDER BY videos.starred DESC, videos.created_at {order_dir.value} "
+ if starred_first
+ else f" ORDER BY videos.created_at {order_dir.value} "
+ )
+ cursor.execute(
+ f"""--sql
+ SELECT videos.video_name
+ FROM videos
+ LEFT JOIN board_videos ON board_videos.video_name = videos.video_name
+ WHERE 1=1{query_conditions}
+ {order_clause}
+ """,
+ query_params,
+ )
+ result = cast(list[sqlite3.Row], cursor.fetchall())
+ video_names = [row[0] for row in result]
+ return VideoNamesResult(video_names=video_names, starred_count=starred_count, total_count=len(video_names))
diff --git a/invokeai/app/services/videos/__init__.py b/invokeai/app/services/videos/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/invokeai/app/services/videos/videos_base.py b/invokeai/app/services/videos/videos_base.py
new file mode 100644
index 00000000000..295025f9b25
--- /dev/null
+++ b/invokeai/app/services/videos/videos_base.py
@@ -0,0 +1,156 @@
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import Callable, Optional
+
+from invokeai.app.invocations.fields import MetadataField
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.video_records.video_records_common import (
+ VideoNamesResult,
+ VideoRecord,
+ VideoRecordChanges,
+)
+from invokeai.app.services.videos.videos_common import VideoDTO
+
+
+class VideoServiceABC(ABC):
+ """High-level service for video management."""
+
+ _on_changed_callbacks: list[Callable[[VideoDTO], None]]
+ _on_deleted_callbacks: list[Callable[[str], None]]
+
+ def __init__(self) -> None:
+ self._on_changed_callbacks = []
+ self._on_deleted_callbacks = []
+
+ def on_changed(self, on_changed: Callable[[VideoDTO], None]) -> None:
+ """Register a callback for when a video is changed."""
+ self._on_changed_callbacks.append(on_changed)
+
+ def on_deleted(self, on_deleted: Callable[[str], None]) -> None:
+ """Register a callback for when a video is deleted."""
+ self._on_deleted_callbacks.append(on_deleted)
+
+ def _on_changed(self, item: VideoDTO) -> None:
+ for callback in self._on_changed_callbacks:
+ callback(item)
+
+ def _on_deleted(self, item_id: str) -> None:
+ for callback in self._on_deleted_callbacks:
+ callback(item_id)
+
+ @abstractmethod
+ def create(
+ self,
+ source_path: Path,
+ width: int,
+ height: int,
+ duration: float,
+ fps: Optional[float],
+ video_origin: ResourceOrigin,
+ video_category: ImageCategory,
+ node_id: Optional[str] = None,
+ session_id: Optional[str] = None,
+ board_id: Optional[str] = None,
+ is_intermediate: Optional[bool] = False,
+ metadata: Optional[str] = None,
+ workflow: Optional[str] = None,
+ graph: Optional[str] = None,
+ user_id: Optional[str] = None,
+ ) -> VideoDTO:
+ """Creates a video by moving/copying the file at `source_path` into storage and recording it."""
+ pass
+
+ @abstractmethod
+ def update(self, video_name: str, changes: VideoRecordChanges) -> VideoDTO:
+ """Updates a video."""
+ pass
+
+ @abstractmethod
+ def get_record(self, video_name: str) -> VideoRecord:
+ """Gets a video record."""
+ pass
+
+ @abstractmethod
+ def get_dto(self, video_name: str) -> VideoDTO:
+ """Gets a video DTO."""
+ pass
+
+ @abstractmethod
+ def get_metadata(self, video_name: str) -> Optional[MetadataField]:
+ """Gets a video's metadata."""
+ pass
+
+ @abstractmethod
+ def get_workflow(self, video_name: str) -> Optional[str]:
+ """Gets a video's workflow."""
+ pass
+
+ @abstractmethod
+ def get_graph(self, video_name: str) -> Optional[str]:
+ """Gets a video's graph."""
+ pass
+
+ @abstractmethod
+ def get_path(self, video_name: str, thumbnail: bool = False) -> str:
+ """Gets a video's on-disk path."""
+ pass
+
+ @abstractmethod
+ def get_url(self, video_name: str, thumbnail: bool = False) -> str:
+ """Gets a video's URL."""
+ pass
+
+ @abstractmethod
+ def get_many(
+ self,
+ offset: int = 0,
+ limit: int = 10,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> OffsetPaginatedResults[VideoDTO]:
+ """Gets a paginated list of video DTOs."""
+ pass
+
+ @abstractmethod
+ def delete(self, video_name: str) -> None:
+ """Deletes a video."""
+ pass
+
+ @abstractmethod
+ def delete_videos_on_board(self, board_id: str, user_id: Optional[str] = None) -> list[str]:
+ """Deletes all videos on a board and returns the names that were actually removed.
+
+ When ``user_id`` is provided, only videos owned by that user are deleted (other users'
+ contributions to a public/shared board are preserved). Pass ``None`` for the admin
+ path to delete every video on the board regardless of uploader.
+
+ Videos whose backing file deletion fails are intentionally retained (their DB records
+ survive and cascade to "uncategorized" via the board_videos FK), so the returned list
+ is the authoritative ``deleted_videos`` for the caller's response.
+ """
+ pass
+
+ @abstractmethod
+ def get_video_names(
+ self,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> VideoNamesResult:
+ """Gets ordered list of video names."""
+ pass
diff --git a/invokeai/app/services/videos/videos_common.py b/invokeai/app/services/videos/videos_common.py
new file mode 100644
index 00000000000..bd37bd0366c
--- /dev/null
+++ b/invokeai/app/services/videos/videos_common.py
@@ -0,0 +1,61 @@
+from typing import Optional
+
+from pydantic import BaseModel, Field
+
+from invokeai.app.services.video_records.video_records_common import VideoRecord
+from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
+
+
+class VideoUrlsDTO(BaseModelExcludeNull):
+ """The URLs for a video and its thumbnail."""
+
+ video_name: str = Field(description="The unique name of the video.")
+ video_url: str = Field(description="The URL of the video file (MP4).")
+ thumbnail_url: str = Field(description="The URL of the video's first-frame thumbnail (WebP).")
+
+
+class VideoDTO(VideoRecord, VideoUrlsDTO):
+ """Deserialized video record, enriched for the frontend."""
+
+ board_id: Optional[str] = Field(
+ default=None, description="The id of the board the video belongs to, if one exists."
+ )
+
+
+def video_record_to_dto(
+ video_record: VideoRecord,
+ video_url: str,
+ thumbnail_url: str,
+ board_id: Optional[str],
+) -> VideoDTO:
+ """Converts a video record to a video DTO."""
+ return VideoDTO(
+ **video_record.model_dump(),
+ video_url=video_url,
+ thumbnail_url=thumbnail_url,
+ board_id=board_id,
+ )
+
+
+class VideoResultWithAffectedBoards(BaseModel):
+ affected_boards: list[str] = Field(description="The ids of boards affected by the operation")
+
+
+class DeleteVideosResult(VideoResultWithAffectedBoards):
+ deleted_videos: list[str] = Field(description="The names of the videos that were deleted")
+
+
+class StarredVideosResult(VideoResultWithAffectedBoards):
+ starred_videos: list[str] = Field(description="The names of the videos that were starred")
+
+
+class UnstarredVideosResult(VideoResultWithAffectedBoards):
+ unstarred_videos: list[str] = Field(description="The names of the videos that were unstarred")
+
+
+class AddVideosToBoardResult(VideoResultWithAffectedBoards):
+ added_videos: list[str] = Field(description="The video names that were added to the board")
+
+
+class RemoveVideosFromBoardResult(VideoResultWithAffectedBoards):
+ removed_videos: list[str] = Field(description="The video names that were removed from their board")
diff --git a/invokeai/app/services/videos/videos_default.py b/invokeai/app/services/videos/videos_default.py
new file mode 100644
index 00000000000..1da613fd18f
--- /dev/null
+++ b/invokeai/app/services/videos/videos_default.py
@@ -0,0 +1,395 @@
+from pathlib import Path
+from typing import Optional
+
+from invokeai.app.invocations.fields import MetadataField
+from invokeai.app.services.image_records.image_records_common import (
+ ImageCategory,
+ InvalidImageCategoryException,
+ InvalidOriginException,
+ ResourceOrigin,
+)
+from invokeai.app.services.invoker import Invoker
+from invokeai.app.services.shared.pagination import OffsetPaginatedResults
+from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
+from invokeai.app.services.video_files.video_files_common import (
+ VideoFileDeleteException,
+ VideoFileNotFoundException,
+ VideoFileSaveException,
+)
+from invokeai.app.services.video_records.video_records_common import (
+ VideoNamesResult,
+ VideoRecord,
+ VideoRecordChanges,
+ VideoRecordDeleteException,
+ VideoRecordNotFoundException,
+ VideoRecordSaveException,
+)
+from invokeai.app.services.videos.videos_base import VideoServiceABC
+from invokeai.app.services.videos.videos_common import VideoDTO, video_record_to_dto
+
+
+class VideoService(VideoServiceABC):
+ __invoker: Invoker
+
+ def start(self, invoker: Invoker) -> None:
+ self.__invoker = invoker
+
+ def create(
+ self,
+ source_path: Path,
+ width: int,
+ height: int,
+ duration: float,
+ fps: Optional[float],
+ video_origin: ResourceOrigin,
+ video_category: ImageCategory,
+ node_id: Optional[str] = None,
+ session_id: Optional[str] = None,
+ board_id: Optional[str] = None,
+ is_intermediate: Optional[bool] = False,
+ metadata: Optional[str] = None,
+ workflow: Optional[str] = None,
+ graph: Optional[str] = None,
+ user_id: Optional[str] = None,
+ ) -> VideoDTO:
+ if video_origin not in ResourceOrigin:
+ raise InvalidOriginException
+ if video_category not in ImageCategory:
+ raise InvalidImageCategoryException
+
+ video_name = self.__invoker.services.names.create_video_name()
+
+ # Reuse the image subfolder strategy for video organization.
+ from invokeai.app.services.image_files.image_subfolder_strategy import create_subfolder_strategy
+
+ strategy_name = self.__invoker.services.configuration.image_subfolder_strategy
+ strategy = create_subfolder_strategy(strategy_name)
+ video_subfolder = strategy.get_subfolder(video_name, video_category, is_intermediate or False)
+
+ record_saved = False
+ board_attached = False
+ try:
+ self.__invoker.services.video_records.save(
+ video_name=video_name,
+ video_origin=video_origin,
+ video_category=video_category,
+ width=width,
+ height=height,
+ duration=duration,
+ fps=fps,
+ has_workflow=workflow is not None or graph is not None,
+ is_intermediate=is_intermediate,
+ node_id=node_id,
+ metadata=metadata,
+ session_id=session_id,
+ user_id=user_id,
+ video_subfolder=video_subfolder,
+ )
+ record_saved = True
+ if board_id is not None:
+ # Board attachment is deliberately best-effort, mirroring ImageService.create:
+ # this is reachable when the board is deleted between the caller's access
+ # check and this insert, and failing the whole create here would destroy a
+ # just-generated video over a cosmetic categorization problem. The returned
+ # DTO reports the video's *actual* board (None on fallback), so callers are
+ # not told the attachment succeeded.
+ try:
+ self.__invoker.services.board_video_records.add_video_to_board(
+ board_id=board_id, video_name=video_name
+ )
+ board_attached = True
+ except Exception as e:
+ self.__invoker.services.logger.warning(f"Failed to add video to board {board_id}: {str(e)}")
+
+ self.__invoker.services.video_files.save(
+ source_path=source_path,
+ video_name=video_name,
+ video_subfolder=video_subfolder,
+ metadata=metadata,
+ workflow=workflow,
+ graph=graph,
+ )
+
+ video_dto = self.get_dto(video_name)
+ self._on_changed(video_dto)
+ return video_dto
+ except VideoRecordSaveException:
+ self.__invoker.services.logger.error("Failed to save video record")
+ raise
+ except Exception as e:
+ # Roll back any DB-side state we created so the gallery doesn't end up with a
+ # ghost record whose file endpoints 404. Most commonly triggered by
+ # VideoFileSaveException (disk save or sidecar write failure), but we also
+ # need to unwind on any unexpected post-record failure.
+ if board_attached:
+ try:
+ self.__invoker.services.board_video_records.remove_video_from_board(video_name=video_name)
+ except Exception as rollback_err:
+ self.__invoker.services.logger.error(
+ f"Failed to roll back board attachment for {video_name}: {str(rollback_err)}"
+ )
+ if record_saved:
+ try:
+ self.__invoker.services.video_records.delete(video_name)
+ except Exception as rollback_err:
+ self.__invoker.services.logger.error(
+ f"Failed to roll back video record for {video_name}: {str(rollback_err)}"
+ )
+ # The disk layer cleans up after itself when the save fails, but a failure
+ # after a successful file save (e.g. building the DTO) would still leave the
+ # files on disk with no record pointing at them. delete() skips files that
+ # don't exist, so this is a no-op when nothing was written.
+ try:
+ self.__invoker.services.video_files.delete(video_name, video_subfolder=video_subfolder)
+ except Exception as rollback_err:
+ self.__invoker.services.logger.error(
+ f"Failed to roll back video files for {video_name}: {str(rollback_err)}"
+ )
+ if isinstance(e, VideoFileSaveException):
+ self.__invoker.services.logger.error("Failed to save video file")
+ else:
+ self.__invoker.services.logger.error(f"Problem saving video record and file: {str(e)}")
+ raise
+
+ def update(self, video_name: str, changes: VideoRecordChanges) -> VideoDTO:
+ try:
+ self.__invoker.services.video_records.update(video_name, changes)
+ video_dto = self.get_dto(video_name)
+ self._on_changed(video_dto)
+ return video_dto
+ except VideoRecordSaveException:
+ self.__invoker.services.logger.error("Failed to update video record")
+ raise
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem updating video record")
+ raise e
+
+ def get_record(self, video_name: str) -> VideoRecord:
+ try:
+ return self.__invoker.services.video_records.get(video_name)
+ except VideoRecordNotFoundException:
+ self.__invoker.services.logger.error("Video record not found")
+ raise
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem getting video record")
+ raise e
+
+ def get_dto(self, video_name: str) -> VideoDTO:
+ try:
+ video_record = self.__invoker.services.video_records.get(video_name)
+ return video_record_to_dto(
+ video_record=video_record,
+ video_url=self.__invoker.services.urls.get_video_url(video_name),
+ thumbnail_url=self.__invoker.services.urls.get_video_url(video_name, thumbnail=True),
+ board_id=self.__invoker.services.board_video_records.get_board_for_video(video_name),
+ )
+ except VideoRecordNotFoundException:
+ self.__invoker.services.logger.error("Video record not found")
+ raise
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem getting video DTO")
+ raise e
+
+ def get_metadata(self, video_name: str) -> Optional[MetadataField]:
+ try:
+ return self.__invoker.services.video_records.get_metadata(video_name)
+ except VideoRecordNotFoundException:
+ self.__invoker.services.logger.error("Video record not found")
+ raise
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem getting video metadata")
+ raise e
+
+ def get_workflow(self, video_name: str) -> Optional[str]:
+ try:
+ record = self.__invoker.services.video_records.get(video_name)
+ return self.__invoker.services.video_files.get_workflow(video_name, video_subfolder=record.video_subfolder)
+ except VideoFileNotFoundException:
+ self.__invoker.services.logger.error("Video file not found")
+ raise
+ except Exception:
+ self.__invoker.services.logger.error("Problem getting video workflow")
+ raise
+
+ def get_graph(self, video_name: str) -> Optional[str]:
+ try:
+ record = self.__invoker.services.video_records.get(video_name)
+ return self.__invoker.services.video_files.get_graph(video_name, video_subfolder=record.video_subfolder)
+ except VideoFileNotFoundException:
+ self.__invoker.services.logger.error("Video file not found")
+ raise
+ except Exception:
+ self.__invoker.services.logger.error("Problem getting video graph")
+ raise
+
+ def get_path(self, video_name: str, thumbnail: bool = False) -> str:
+ try:
+ record = self.__invoker.services.video_records.get(video_name)
+ return str(
+ self.__invoker.services.video_files.get_path(
+ video_name, thumbnail=thumbnail, video_subfolder=record.video_subfolder
+ )
+ )
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem getting video path")
+ raise e
+
+ def get_url(self, video_name: str, thumbnail: bool = False) -> str:
+ try:
+ return self.__invoker.services.urls.get_video_url(video_name, thumbnail=thumbnail)
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem getting video URL")
+ raise e
+
+ def get_many(
+ self,
+ offset: int = 0,
+ limit: int = 10,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> OffsetPaginatedResults[VideoDTO]:
+ try:
+ results = self.__invoker.services.video_records.get_many(
+ offset,
+ limit,
+ starred_first,
+ order_dir,
+ video_origin,
+ categories,
+ is_intermediate,
+ board_id,
+ search_term,
+ user_id,
+ is_admin,
+ )
+ video_dtos = [
+ video_record_to_dto(
+ video_record=r,
+ video_url=self.__invoker.services.urls.get_video_url(r.video_name),
+ thumbnail_url=self.__invoker.services.urls.get_video_url(r.video_name, thumbnail=True),
+ board_id=self.__invoker.services.board_video_records.get_board_for_video(r.video_name),
+ )
+ for r in results.items
+ ]
+ return OffsetPaginatedResults[VideoDTO](
+ items=video_dtos, offset=results.offset, limit=results.limit, total=results.total
+ )
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem getting paginated video DTOs")
+ raise e
+
+ def delete(self, video_name: str) -> None:
+ token: object | None = None
+ record_deleted = False
+ try:
+ record = self.__invoker.services.video_records.get(video_name)
+ token = self.__invoker.services.video_files.stage_delete(video_name, video_subfolder=record.video_subfolder)
+ self.__invoker.services.video_records.delete(video_name)
+ record_deleted = True
+ try:
+ self.__invoker.services.video_files.commit_delete(token)
+ except Exception as cleanup_error:
+ self.__invoker.services.logger.error(f"Failed to purge staged video files: {cleanup_error}")
+ self._on_deleted(video_name)
+ except VideoRecordDeleteException:
+ if token is not None:
+ self.__invoker.services.video_files.rollback_delete(token)
+ self.__invoker.services.logger.error("Failed to delete video record")
+ raise
+ except VideoFileDeleteException:
+ self.__invoker.services.logger.error("Failed to delete video file")
+ raise
+ except Exception as e:
+ if token is not None and not record_deleted:
+ try:
+ self.__invoker.services.video_files.rollback_delete(token)
+ except Exception as rollback_error:
+ self.__invoker.services.logger.error(f"Failed to restore video files: {rollback_error}")
+ self.__invoker.services.logger.error("Problem deleting video record and file")
+ raise e
+
+ def delete_videos_on_board(self, board_id: str, user_id: Optional[str] = None) -> list[str]:
+ try:
+ # When ``user_id`` is set the lookup filters to videos owned by that user so the
+ # cascade doesn't destroy other users' contributions to a public/shared board.
+ video_names = self.__invoker.services.board_video_records.get_all_board_video_names_for_board(
+ board_id, categories=None, is_intermediate=None, user_id=user_id
+ )
+ # Only delete records for files we actually managed to remove. Otherwise a
+ # transient FS error would leave the file orphaned on disk with no record
+ # pointing at it — the API would report success and the user would have no
+ # way to clean up the leak. The board itself will still be deleted by the
+ # caller, so any preserved records cascade to "uncategorized" via the
+ # board_videos FK.
+ deleted_video_names: list[str] = []
+ staged_deletes: list[tuple[str, object]] = []
+ for video_name in video_names:
+ try:
+ record = self.__invoker.services.video_records.get(video_name)
+ token = self.__invoker.services.video_files.stage_delete(
+ video_name, video_subfolder=record.video_subfolder
+ )
+ staged_deletes.append((video_name, token))
+ deleted_video_names.append(video_name)
+ except Exception as e:
+ self.__invoker.services.logger.error(
+ f"Failed to delete video file {video_name}; keeping record: {str(e)}"
+ )
+ try:
+ self.__invoker.services.video_records.delete_many(deleted_video_names)
+ except Exception:
+ for _, token in staged_deletes:
+ self.__invoker.services.video_files.rollback_delete(token)
+ raise
+ for _, token in staged_deletes:
+ try:
+ self.__invoker.services.video_files.commit_delete(token)
+ except Exception as cleanup_error:
+ self.__invoker.services.logger.error(f"Failed to purge staged video files: {cleanup_error}")
+ for video_name in deleted_video_names:
+ self._on_deleted(video_name)
+ return deleted_video_names
+ except VideoRecordDeleteException:
+ self.__invoker.services.logger.error("Failed to delete video records")
+ raise
+ except VideoFileDeleteException:
+ self.__invoker.services.logger.error("Failed to delete video files")
+ raise
+ except Exception as e:
+ self.__invoker.services.logger.error(f"Problem deleting video records and files: {str(e)}")
+ raise e
+
+ def get_video_names(
+ self,
+ starred_first: bool = True,
+ order_dir: SQLiteDirection = SQLiteDirection.Descending,
+ video_origin: Optional[ResourceOrigin] = None,
+ categories: Optional[list[ImageCategory]] = None,
+ is_intermediate: Optional[bool] = None,
+ board_id: Optional[str] = None,
+ search_term: Optional[str] = None,
+ user_id: Optional[str] = None,
+ is_admin: bool = False,
+ ) -> VideoNamesResult:
+ try:
+ return self.__invoker.services.video_records.get_video_names(
+ starred_first=starred_first,
+ order_dir=order_dir,
+ video_origin=video_origin,
+ categories=categories,
+ is_intermediate=is_intermediate,
+ board_id=board_id,
+ search_term=search_term,
+ user_id=user_id,
+ is_admin=is_admin,
+ )
+ except Exception as e:
+ self.__invoker.services.logger.error("Problem getting video names")
+ raise e
diff --git a/invokeai/app/services/virtual_boards/virtual_boards_common.py b/invokeai/app/services/virtual_boards/virtual_boards_common.py
index e1df5a81ca5..9b396ecf802 100644
--- a/invokeai/app/services/virtual_boards/virtual_boards_common.py
+++ b/invokeai/app/services/virtual_boards/virtual_boards_common.py
@@ -4,11 +4,16 @@
class VirtualSubBoardDTO(BaseModel):
- """A virtual sub-board computed from image metadata, not stored in the database."""
+ """A virtual sub-board computed from image/video metadata, not stored in the database."""
virtual_board_id: str = Field(description="The virtual board ID, e.g. 'by_date:2026-03-18'.")
board_name: str = Field(description="The display name of the virtual sub-board, e.g. '2026-03-18'.")
date: str = Field(description="The ISO date string, e.g. '2026-03-18'.")
image_count: int = Field(description="The number of general images for this date.")
asset_count: int = Field(description="The number of asset images for this date.")
+ video_count: int = Field(default=0, description="The number of videos for this date.")
cover_image_name: Optional[str] = Field(default=None, description="The most recent image name for this date.")
+ cover_video_name: Optional[str] = Field(
+ default=None,
+ description="The most recent video name for this date. Set instead of cover_image_name when the newest item for the date is a video.",
+ )
diff --git a/invokeai/app/services/workflow_records/default_workflows/Extend Video - Wan 2.2 Lightning w_ Concept LoRAs.json b/invokeai/app/services/workflow_records/default_workflows/Extend Video - Wan 2.2 Lightning w_ Concept LoRAs.json
new file mode 100644
index 00000000000..ba7c073742d
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Extend Video - Wan 2.2 Lightning w_ Concept LoRAs.json
@@ -0,0 +1,1795 @@
+{
+ "name": "Extend Video - Wan 2.2 Lightning w/ Concept LoRAs",
+ "author": "InvokeAI",
+ "description": "This workflow adds frames to an existing video. Provide a starting video and use the sliders to select the starting and ending frames. The workflow will create a new video starting from the selected ending frame and then concatenate the new video to the end of the first video, performing a quick cross-fade at the selected ending frame to generate a smooth transition.\n\nInput slots for a concept LoRA pair (high and low noise) are provided.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, video to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 I2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 I2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation, I2V variant):\n- Apply Lightning LoRA (High Noise): Wan 2.2 I2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 I2V Lightning Low Noise (4-step)\n\nOptional concept LoRAs (your own style/subject LoRAs):\n- Apply Concept LoRA (High Noise) / (Low Noise): leave empty, or select a high/low-noise concept LoRA pair.\n\nManually edit the \"Concatenate Videos\" node to fine-tune the transition type and number of transition frames.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 350,
+ "y": 367.5
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 579.5
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 759.5
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 1050,
+ "y": 939.5
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1511.9383824221495,
+ "y": 1036.079759854583
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1750,
+ "y": 400.5
+ }
+ },
+ {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "type": "invocation",
+ "data": {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Lightning LoRA (High Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 529.5242169371062,
+ "y": 1151.2120766989774
+ }
+ },
+ {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "type": "invocation",
+ "data": {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Lightning LoRA (Low Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 886.0309262143157,
+ "y": 1148.6093929880935
+ }
+ },
+ {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "type": "invocation",
+ "data": {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ref_image_encoder",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "",
+ "description": "",
+ "value": 1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1042.1919488673484,
+ "y": 1629.6878597618713
+ }
+ },
+ {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "type": "invocation",
+ "data": {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_i2v_ideal_dimensions",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "Video Resolution",
+ "description": "",
+ "value": "480p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 681.7812140238128,
+ "y": 1608.6053038826165
+ }
+ },
+ {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "type": "invocation",
+ "data": {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "Num Frames",
+ "notes": "",
+ "type": "integer",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 690.8906070119064,
+ "y": 1873.1811504846617
+ }
+ },
+ {
+ "id": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "type": "invocation",
+ "data": {
+ "id": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "video_concat",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "videos": {
+ "name": "videos",
+ "label": "",
+ "description": ""
+ },
+ "transition": {
+ "name": "transition",
+ "label": "",
+ "description": "",
+ "value": "crossfade"
+ },
+ "transition_frames": {
+ "name": "transition_frames",
+ "label": "",
+ "description": "",
+ "value": 2
+ },
+ "fps": {
+ "name": "fps",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 2450,
+ "y": 108.5
+ }
+ },
+ {
+ "id": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "type": "invocation",
+ "data": {
+ "id": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "collect",
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": "",
+ "description": ""
+ },
+ "collection": {
+ "name": "collection",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1750,
+ "y": 280.5
+ }
+ },
+ {
+ "id": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "type": "invocation",
+ "data": {
+ "id": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "collect",
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": "",
+ "description": ""
+ },
+ "collection": {
+ "name": "collection",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 2100,
+ "y": 280.5
+ }
+ },
+ {
+ "id": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "type": "invocation",
+ "data": {
+ "id": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "float_to_int",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "multiple": {
+ "name": "multiple",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "method": {
+ "name": "method",
+ "label": "",
+ "description": "",
+ "value": "Nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 463.36245980839465,
+ "y": 105.04759620704448
+ }
+ },
+ {
+ "id": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "type": "invocation",
+ "data": {
+ "id": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "extract_video_range",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "video": {
+ "name": "video",
+ "label": "Initial Video",
+ "description": "The video so far. Specify the start and end positions."
+ },
+ "start_frame": {
+ "name": "start_frame",
+ "label": "",
+ "description": "First frame to keep.",
+ "value": 0
+ },
+ "end_frame": {
+ "name": "end_frame",
+ "label": "",
+ "description": "Last frame to keep.",
+ "value": -2
+ },
+ "fps": {
+ "name": "fps",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -210.7591703842118,
+ "y": 102.45237449232519
+ }
+ },
+ {
+ "id": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "type": "invocation",
+ "data": {
+ "id": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "video_frame_extract",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "video": {
+ "name": "video",
+ "label": "",
+ "description": ""
+ },
+ "frame_index": {
+ "name": "frame_index",
+ "label": "",
+ "description": "",
+ "value": -1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 279.7075855472698,
+ "y": 1622.9052864414045
+ }
+ },
+ {
+ "id": "8da34144-8931-4a61-adb7-b3f772866f98",
+ "type": "invocation",
+ "data": {
+ "id": "8da34144-8931-4a61-adb7-b3f772866f98",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Concept LoRA (High Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Concept LoRA (High Noise)",
+ "description": ""
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 0.75
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 535.4274779696555,
+ "y": 1384.3634046282455
+ }
+ },
+ {
+ "id": "5d602b68-c4f6-4bd1-a939-5a00c9892e18",
+ "type": "invocation",
+ "data": {
+ "id": "5d602b68-c4f6-4bd1-a939-5a00c9892e18",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Concept LoRA (Low Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Concept LoRA (Low Noise)",
+ "description": ""
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 0.75
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 894.5978300716305,
+ "y": 1386.9660883391293
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-291abec8-b996-4c7b-959c-360f56a3ed60transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-291abec8-b996-4c7b-959c-360f56a3ed60transformer-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer",
+ "type": "default",
+ "source": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "target": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-97c6fecb-8354-4ace-b337-e544b0ba9982ref_image-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabref_image",
+ "type": "default",
+ "source": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7evae-97c6fecb-8354-4ace-b337-e544b0ba9982vae",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-97c6fecb-8354-4ace-b337-e544b0ba9982width",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-97c6fecb-8354-4ace-b337-e544b0ba9982height",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabnum_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-97c6fecb-8354-4ace-b337-e544b0ba9982num_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73awidth-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73aheight-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73aimage-97c6fecb-8354-4ace-b337-e544b0ba9982image",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189video-c2b5afb3-d079-4afb-9354-4c1a7aeae73avideo",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "sourceHandle": "video",
+ "targetHandle": "video"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189video-6030b112-b669-4626-86ab-b1bffa96f9d6item",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "sourceHandle": "video",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-6030b112-b669-4626-86ab-b1bffa96f9d6collection-9d72df22-9794-4c8b-af0b-a01548b5f852collection",
+ "type": "default",
+ "source": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "target": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "sourceHandle": "collection",
+ "targetHandle": "collection"
+ },
+ {
+ "id": "reactflow__edge-6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbdvideo-9d72df22-9794-4c8b-af0b-a01548b5f852item",
+ "type": "default",
+ "source": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "target": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "sourceHandle": "video",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-9d72df22-9794-4c8b-af0b-a01548b5f852collection-4b32c22b-679d-4d5e-a167-c3309265fcd9videos",
+ "type": "default",
+ "source": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "target": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "sourceHandle": "collection",
+ "targetHandle": "videos"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189fps-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "sourceHandle": "fps",
+ "targetHandle": "value"
+ },
+ {
+ "id": "reactflow__edge-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue-4b32c22b-679d-4d5e-a167-c3309265fcd9fps",
+ "type": "default",
+ "source": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "target": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "sourceHandle": "value",
+ "targetHandle": "fps"
+ },
+ {
+ "id": "reactflow__edge-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue-6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbdfps",
+ "type": "default",
+ "source": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "value",
+ "targetHandle": "fps"
+ },
+ {
+ "id": "reactflow__edge-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer-8da34144-8931-4a61-adb7-b3f772866f98transformer",
+ "type": "default",
+ "source": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "target": "8da34144-8931-4a61-adb7-b3f772866f98",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-8da34144-8931-4a61-adb7-b3f772866f98transformer-5d602b68-c4f6-4bd1-a939-5a00c9892e18transformer",
+ "type": "default",
+ "source": "8da34144-8931-4a61-adb7-b3f772866f98",
+ "target": "5d602b68-c4f6-4bd1-a939-5a00c9892e18",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-5d602b68-c4f6-4bd1-a939-5a00c9892e18transformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "5d602b68-c4f6-4bd1-a939-5a00c9892e18",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-1lpcsDWjRx",
+ "container-i8V1oJeQCP",
+ "container-mUeBcw2tYf",
+ "divider-2T3tRpIIJO",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "divider-w19S1gIyNJ",
+ "heading-7ZKUFfihCo",
+ "container-GnMzsFf3bp",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "divider-FKc8lld0Eb",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-FiH14mitcM"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-DonE1WRSA6"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "divider-dzmORn21WD",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-a8nZxgz9u9": {
+ "id": "node-field-a8nZxgz9u9",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-NwR9yOugEz": {
+ "id": "node-field-NwR9yOugEz",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-a8nZxgz9u9",
+ "node-field-0al3LtpcGU"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-NwR9yOugEz",
+ "node-field-lgLAjS4nks"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-0al3LtpcGU": {
+ "id": "node-field-0al3LtpcGU",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-lgLAjS4nks": {
+ "id": "node-field-lgLAjS4nks",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ },
+ "node-field-DonE1WRSA6": {
+ "id": "node-field-DonE1WRSA6",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "fieldName": "target_resolution"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "divider-2T3tRpIIJO": {
+ "id": "divider-2T3tRpIIJO",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "divider-FKc8lld0Eb": {
+ "id": "divider-FKc8lld0Eb",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "node-field-FiH14mitcM": {
+ "id": "node-field-FiH14mitcM",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "fieldName": "value"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-kHqCwpoZZg": {
+ "id": "node-field-kHqCwpoZZg",
+ "parentId": "container-1lpcsDWjRx",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "video"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-1lpcsDWjRx": {
+ "id": "container-1lpcsDWjRx",
+ "parentId": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-kHqCwpoZZg",
+ "container-hQbats2gzR"
+ ]
+ },
+ "type": "container"
+ },
+ "container-hQbats2gzR": {
+ "id": "container-hQbats2gzR",
+ "parentId": "container-1lpcsDWjRx",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-syuTRDo0YY",
+ "node-field-KvKUkKmRGN"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-syuTRDo0YY": {
+ "id": "node-field-syuTRDo0YY",
+ "parentId": "container-hQbats2gzR",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "start_frame"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KvKUkKmRGN": {
+ "id": "node-field-KvKUkKmRGN",
+ "parentId": "container-hQbats2gzR",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "end_frame"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "divider-dzmORn21WD": {
+ "id": "divider-dzmORn21WD",
+ "parentId": "container-AkpUH1WtID",
+ "type": "divider"
+ },
+ "divider-w19S1gIyNJ": {
+ "id": "divider-w19S1gIyNJ",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "heading-7ZKUFfihCo": {
+ "id": "heading-7ZKUFfihCo",
+ "parentId": "root",
+ "data": {
+ "content": "Concept LoRAs"
+ },
+ "type": "heading"
+ },
+ "container-GnMzsFf3bp": {
+ "id": "container-GnMzsFf3bp",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-HiZyrp0zop",
+ "container-aeDJhcU5Zr"
+ ]
+ },
+ "type": "container"
+ },
+ "container-aeDJhcU5Zr": {
+ "id": "container-aeDJhcU5Zr",
+ "parentId": "container-GnMzsFf3bp",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-1R7Lgckwoe"
+ ]
+ },
+ "type": "container"
+ },
+ "container-HiZyrp0zop": {
+ "id": "container-HiZyrp0zop",
+ "parentId": "container-GnMzsFf3bp",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rAvu5j575m"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rAvu5j575m": {
+ "id": "container-rAvu5j575m",
+ "parentId": "container-HiZyrp0zop",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-vtfoXGJjAB",
+ "node-field-jzBwpMn21l"
+ ]
+ },
+ "type": "container"
+ },
+ "container-1R7Lgckwoe": {
+ "id": "container-1R7Lgckwoe",
+ "parentId": "container-aeDJhcU5Zr",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-m9Vo9LXub9",
+ "node-field-njICXCR5l0"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-vtfoXGJjAB": {
+ "id": "node-field-vtfoXGJjAB",
+ "parentId": "container-rAvu5j575m",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "8da34144-8931-4a61-adb7-b3f772866f98",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-jzBwpMn21l": {
+ "id": "node-field-jzBwpMn21l",
+ "parentId": "container-rAvu5j575m",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "8da34144-8931-4a61-adb7-b3f772866f98",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-m9Vo9LXub9": {
+ "id": "node-field-m9Vo9LXub9",
+ "parentId": "container-1R7Lgckwoe",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "5d602b68-c4f6-4bd1-a939-5a00c9892e18",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-njICXCR5l0": {
+ "id": "node-field-njICXCR5l0",
+ "parentId": "container-1R7Lgckwoe",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "5d602b68-c4f6-4bd1-a939-5a00c9892e18",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_extend_video_lightning_concept"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Extend Video - Wan 2.2 Lightning.json b/invokeai/app/services/workflow_records/default_workflows/Extend Video - Wan 2.2 Lightning.json
new file mode 100644
index 00000000000..46ddbb760e6
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Extend Video - Wan 2.2 Lightning.json
@@ -0,0 +1,1555 @@
+{
+ "name": "Extend Video - Wan 2.2 Lightning",
+ "author": "InvokeAI",
+ "description": "This workflow adds frames to an existing video. Provide a starting video and use the sliders to select the starting and ending frames. The workflow will create a new video starting from the selected ending frame and then concatenate the new video to the end of the first video, performing a quick cross-fade at the selected ending frame to generate a smooth transition.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, video to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 I2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 I2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation, I2V variant):\n- Apply Lightning LoRA (High Noise): Wan 2.2 I2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 I2V Lightning Low Noise (4-step)\n\nEdit the \"Concatenate Videos\" node to adjust the transition and number of transition frames.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 350,
+ "y": 367.5
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 579.5
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 759.5
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 1050,
+ "y": 939.5
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1511.9383824221495,
+ "y": 1036.079759854583
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1750,
+ "y": 400.5
+ }
+ },
+ {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "type": "invocation",
+ "data": {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Lightning LoRA (High Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 1139.5
+ }
+ },
+ {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "type": "invocation",
+ "data": {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 1139.5
+ }
+ },
+ {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "type": "invocation",
+ "data": {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ref_image_encoder",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "",
+ "description": "",
+ "value": 1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 1447.5
+ }
+ },
+ {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "type": "invocation",
+ "data": {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_i2v_ideal_dimensions",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "Video Resolution",
+ "description": "",
+ "value": "480p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 1409.5
+ }
+ },
+ {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "type": "invocation",
+ "data": {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "Num Frames",
+ "notes": "",
+ "type": "integer",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 1697.5
+ }
+ },
+ {
+ "id": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "type": "invocation",
+ "data": {
+ "id": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "video_concat",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "videos": {
+ "name": "videos",
+ "label": "",
+ "description": ""
+ },
+ "transition": {
+ "name": "transition",
+ "label": "",
+ "description": "",
+ "value": "crossfade"
+ },
+ "transition_frames": {
+ "name": "transition_frames",
+ "label": "",
+ "description": "",
+ "value": 2
+ },
+ "fps": {
+ "name": "fps",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 2450,
+ "y": 108.5
+ }
+ },
+ {
+ "id": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "type": "invocation",
+ "data": {
+ "id": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "collect",
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": "",
+ "description": ""
+ },
+ "collection": {
+ "name": "collection",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1750,
+ "y": 280.5
+ }
+ },
+ {
+ "id": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "type": "invocation",
+ "data": {
+ "id": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "collect",
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": "",
+ "description": ""
+ },
+ "collection": {
+ "name": "collection",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 2100,
+ "y": 280.5
+ }
+ },
+ {
+ "id": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "type": "invocation",
+ "data": {
+ "id": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "float_to_int",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "multiple": {
+ "name": "multiple",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "method": {
+ "name": "method",
+ "label": "",
+ "description": "",
+ "value": "Nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 463.36245980839465,
+ "y": 105.04759620704448
+ }
+ },
+ {
+ "id": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "type": "invocation",
+ "data": {
+ "id": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "extract_video_range",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "video": {
+ "name": "video",
+ "label": "Initial Video",
+ "description": "The video so far. Specify the start and end positions."
+ },
+ "start_frame": {
+ "name": "start_frame",
+ "label": "",
+ "description": "First frame to keep.",
+ "value": 0
+ },
+ "end_frame": {
+ "name": "end_frame",
+ "label": "",
+ "description": "Last frame to keep.",
+ "value": -2
+ },
+ "fps": {
+ "name": "fps",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -210.7591703842118,
+ "y": 102.45237449232519
+ }
+ },
+ {
+ "id": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "type": "invocation",
+ "data": {
+ "id": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "video_frame_extract",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "video": {
+ "name": "video",
+ "label": "",
+ "description": ""
+ },
+ "frame_index": {
+ "name": "frame_index",
+ "label": "",
+ "description": "",
+ "value": -1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 287.5156366799214,
+ "y": 1401.677171016275
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-291abec8-b996-4c7b-959c-360f56a3ed60transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-291abec8-b996-4c7b-959c-360f56a3ed60transformer-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer",
+ "type": "default",
+ "source": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "target": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-97c6fecb-8354-4ace-b337-e544b0ba9982ref_image-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabref_image",
+ "type": "default",
+ "source": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7evae-97c6fecb-8354-4ace-b337-e544b0ba9982vae",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-97c6fecb-8354-4ace-b337-e544b0ba9982width",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-97c6fecb-8354-4ace-b337-e544b0ba9982height",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabnum_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-97c6fecb-8354-4ace-b337-e544b0ba9982num_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73awidth-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73aheight-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73aimage-97c6fecb-8354-4ace-b337-e544b0ba9982image",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189video-c2b5afb3-d079-4afb-9354-4c1a7aeae73avideo",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "sourceHandle": "video",
+ "targetHandle": "video"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189video-6030b112-b669-4626-86ab-b1bffa96f9d6item",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "sourceHandle": "video",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-6030b112-b669-4626-86ab-b1bffa96f9d6collection-9d72df22-9794-4c8b-af0b-a01548b5f852collection",
+ "type": "default",
+ "source": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "target": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "sourceHandle": "collection",
+ "targetHandle": "collection"
+ },
+ {
+ "id": "reactflow__edge-6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbdvideo-9d72df22-9794-4c8b-af0b-a01548b5f852item",
+ "type": "default",
+ "source": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "target": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "sourceHandle": "video",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-9d72df22-9794-4c8b-af0b-a01548b5f852collection-4b32c22b-679d-4d5e-a167-c3309265fcd9videos",
+ "type": "default",
+ "source": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "target": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "sourceHandle": "collection",
+ "targetHandle": "videos"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189fps-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "sourceHandle": "fps",
+ "targetHandle": "value"
+ },
+ {
+ "id": "reactflow__edge-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue-4b32c22b-679d-4d5e-a167-c3309265fcd9fps",
+ "type": "default",
+ "source": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "target": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "sourceHandle": "value",
+ "targetHandle": "fps"
+ },
+ {
+ "id": "reactflow__edge-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue-6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbdfps",
+ "type": "default",
+ "source": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "value",
+ "targetHandle": "fps"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-1lpcsDWjRx",
+ "container-i8V1oJeQCP",
+ "container-mUeBcw2tYf",
+ "divider-2T3tRpIIJO",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "divider-FKc8lld0Eb",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-FiH14mitcM"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-DonE1WRSA6"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-a8nZxgz9u9": {
+ "id": "node-field-a8nZxgz9u9",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-NwR9yOugEz": {
+ "id": "node-field-NwR9yOugEz",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-a8nZxgz9u9",
+ "node-field-0al3LtpcGU"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-NwR9yOugEz",
+ "node-field-lgLAjS4nks"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-0al3LtpcGU": {
+ "id": "node-field-0al3LtpcGU",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-lgLAjS4nks": {
+ "id": "node-field-lgLAjS4nks",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ },
+ "node-field-DonE1WRSA6": {
+ "id": "node-field-DonE1WRSA6",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "fieldName": "target_resolution"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "divider-2T3tRpIIJO": {
+ "id": "divider-2T3tRpIIJO",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "divider-FKc8lld0Eb": {
+ "id": "divider-FKc8lld0Eb",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "node-field-FiH14mitcM": {
+ "id": "node-field-FiH14mitcM",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "fieldName": "value"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-kHqCwpoZZg": {
+ "id": "node-field-kHqCwpoZZg",
+ "parentId": "container-1lpcsDWjRx",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "video"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-1lpcsDWjRx": {
+ "id": "container-1lpcsDWjRx",
+ "parentId": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-kHqCwpoZZg",
+ "container-hQbats2gzR"
+ ]
+ },
+ "type": "container"
+ },
+ "container-hQbats2gzR": {
+ "id": "container-hQbats2gzR",
+ "parentId": "container-1lpcsDWjRx",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-syuTRDo0YY",
+ "node-field-KvKUkKmRGN"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-syuTRDo0YY": {
+ "id": "node-field-syuTRDo0YY",
+ "parentId": "container-hQbats2gzR",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "start_frame"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KvKUkKmRGN": {
+ "id": "node-field-KvKUkKmRGN",
+ "parentId": "container-hQbats2gzR",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "end_frame"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_extend_video_lightning"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Extend Video to Image - Wan 2.2 Lightning.json b/invokeai/app/services/workflow_records/default_workflows/Extend Video to Image - Wan 2.2 Lightning.json
new file mode 100644
index 00000000000..433f19788a6
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Extend Video to Image - Wan 2.2 Lightning.json
@@ -0,0 +1,1686 @@
+{
+ "name": "Extend Video to Image - Wan 2.2 Lightning",
+ "author": "InvokeAI",
+ "description": "This workflow adds frames to an existing video and interpolates the new video to a selected end image. Provide a starting video and use the sliders to select the starting and ending frames. Provide a destination image to specify how the last frame of the extended video should look. The workflow will create a new video starting from the selected ending frame and then concatenate the new video to the end of the first video, performing a quick cross-fade at the selected ending frame to generate a smooth transition.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, video to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 I2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 I2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation, I2V variant):\n- Apply Lightning LoRA (High Noise): Wan 2.2 I2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 I2V Lightning Low Noise (4-step)\n\nEdit the \"Concatenate Videos\" node to adjust the transition and number of transition frames.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "category": "default",
+ "version": "4.0.0"
+ },
+ "id": "default_wan22_extend_to_image_lightning",
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-1lpcsDWjRx",
+ "node-field-rKS7w1UNbR",
+ "container-i8V1oJeQCP",
+ "container-mUeBcw2tYf",
+ "divider-2T3tRpIIJO",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "divider-FKc8lld0Eb",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-FiH14mitcM"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-DonE1WRSA6"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-a8nZxgz9u9": {
+ "id": "node-field-a8nZxgz9u9",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-NwR9yOugEz": {
+ "id": "node-field-NwR9yOugEz",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-a8nZxgz9u9",
+ "node-field-0al3LtpcGU"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-NwR9yOugEz",
+ "node-field-lgLAjS4nks"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-0al3LtpcGU": {
+ "id": "node-field-0al3LtpcGU",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-lgLAjS4nks": {
+ "id": "node-field-lgLAjS4nks",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ },
+ "node-field-DonE1WRSA6": {
+ "id": "node-field-DonE1WRSA6",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "fieldName": "target_resolution"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "divider-2T3tRpIIJO": {
+ "id": "divider-2T3tRpIIJO",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "divider-FKc8lld0Eb": {
+ "id": "divider-FKc8lld0Eb",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "node-field-FiH14mitcM": {
+ "id": "node-field-FiH14mitcM",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "fieldName": "value"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-kHqCwpoZZg": {
+ "id": "node-field-kHqCwpoZZg",
+ "parentId": "container-1lpcsDWjRx",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "video"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-1lpcsDWjRx": {
+ "id": "container-1lpcsDWjRx",
+ "parentId": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-kHqCwpoZZg",
+ "container-hQbats2gzR"
+ ]
+ },
+ "type": "container"
+ },
+ "container-hQbats2gzR": {
+ "id": "container-hQbats2gzR",
+ "parentId": "container-1lpcsDWjRx",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-syuTRDo0YY",
+ "node-field-KvKUkKmRGN"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-syuTRDo0YY": {
+ "id": "node-field-syuTRDo0YY",
+ "parentId": "container-hQbats2gzR",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "start_frame"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KvKUkKmRGN": {
+ "id": "node-field-KvKUkKmRGN",
+ "parentId": "container-hQbats2gzR",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "fieldName": "end_frame"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-rKS7w1UNbR": {
+ "id": "node-field-rKS7w1UNbR",
+ "type": "node-field",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "f93806c9-a708-48dd-bbe8-83f56f8e9a1b",
+ "fieldName": "image"
+ },
+ "showDescription": true
+ }
+ }
+ },
+ "rootElementId": "root"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 350,
+ "y": 367.5
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 579.5
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 759.5
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 1050,
+ "y": 939.5
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1548.5043663570775,
+ "y": 979.0368249160955
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1750,
+ "y": 400.5
+ }
+ },
+ {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "type": "invocation",
+ "data": {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Lightning LoRA (High Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 1139.5
+ }
+ },
+ {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "type": "invocation",
+ "data": {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 1139.5
+ }
+ },
+ {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "type": "invocation",
+ "data": {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "version": "1.2.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ref_image_encoder",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "end_image": {
+ "name": "end_image",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1130.4451646568414,
+ "y": 1519.1693285124582
+ }
+ },
+ {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "type": "invocation",
+ "data": {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_i2v_ideal_dimensions",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "Video Resolution",
+ "description": "",
+ "value": "480p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 416.24796466495997,
+ "y": 1403.6494425704113
+ }
+ },
+ {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "type": "invocation",
+ "data": {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "Num Frames",
+ "notes": "",
+ "type": "integer",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 1697.5
+ }
+ },
+ {
+ "id": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "type": "invocation",
+ "data": {
+ "id": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "video_concat",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "videos": {
+ "name": "videos",
+ "label": "",
+ "description": ""
+ },
+ "transition": {
+ "name": "transition",
+ "label": "",
+ "description": "",
+ "value": "crossfade"
+ },
+ "transition_frames": {
+ "name": "transition_frames",
+ "label": "",
+ "description": "",
+ "value": 2
+ },
+ "fps": {
+ "name": "fps",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 2450,
+ "y": 108.5
+ }
+ },
+ {
+ "id": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "type": "invocation",
+ "data": {
+ "id": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "collect",
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": "",
+ "description": ""
+ },
+ "collection": {
+ "name": "collection",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1750,
+ "y": 280.5
+ }
+ },
+ {
+ "id": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "type": "invocation",
+ "data": {
+ "id": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "collect",
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": "",
+ "description": ""
+ },
+ "collection": {
+ "name": "collection",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 2100,
+ "y": 280.5
+ }
+ },
+ {
+ "id": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "type": "invocation",
+ "data": {
+ "id": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "float_to_int",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "multiple": {
+ "name": "multiple",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "method": {
+ "name": "method",
+ "label": "",
+ "description": "",
+ "value": "Nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 463.36245980839465,
+ "y": 105.04759620704448
+ }
+ },
+ {
+ "id": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "type": "invocation",
+ "data": {
+ "id": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "extract_video_range",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "video": {
+ "name": "video",
+ "label": "Initial Video",
+ "description": "The video so far. Specify the start and end positions."
+ },
+ "start_frame": {
+ "name": "start_frame",
+ "label": "",
+ "description": "First frame to keep.",
+ "value": 0
+ },
+ "end_frame": {
+ "name": "end_frame",
+ "label": "",
+ "description": "Last frame to keep.",
+ "value": -2
+ },
+ "fps": {
+ "name": "fps",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -210.7591703842118,
+ "y": 102.45237449232519
+ }
+ },
+ {
+ "id": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "type": "invocation",
+ "data": {
+ "id": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "video_frame_extract",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "video": {
+ "name": "video",
+ "label": "",
+ "description": ""
+ },
+ "frame_index": {
+ "name": "frame_index",
+ "label": "",
+ "description": "",
+ "value": -1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -67.90572716757725,
+ "y": 1373.8870232257298
+ }
+ },
+ {
+ "id": "f93806c9-a708-48dd-bbe8-83f56f8e9a1b",
+ "type": "invocation",
+ "data": {
+ "id": "f93806c9-a708-48dd-bbe8-83f56f8e9a1b",
+ "type": "image",
+ "version": "1.0.2",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "nodePack": "invokeai",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "Destination Image",
+ "description": ""
+ }
+ }
+ },
+ "position": {
+ "x": -253.99629537551792,
+ "y": 1000.8575532061163
+ }
+ },
+ {
+ "id": "85041165-5a9f-474d-ba63-08b8de4c119d",
+ "type": "invocation",
+ "data": {
+ "id": "85041165-5a9f-474d-ba63-08b8de4c119d",
+ "type": "img_resize",
+ "version": "1.2.2",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "nodePack": "invokeai",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 512
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 512
+ },
+ "resample_mode": {
+ "name": "resample_mode",
+ "label": "",
+ "description": "",
+ "value": "bicubic"
+ }
+ }
+ },
+ "position": {
+ "x": 321.0254123778118,
+ "y": 923.0288200694315
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-291abec8-b996-4c7b-959c-360f56a3ed60transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-291abec8-b996-4c7b-959c-360f56a3ed60transformer-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer",
+ "type": "default",
+ "source": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "target": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-97c6fecb-8354-4ace-b337-e544b0ba9982ref_image-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabref_image",
+ "type": "default",
+ "source": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7evae-97c6fecb-8354-4ace-b337-e544b0ba9982vae",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-97c6fecb-8354-4ace-b337-e544b0ba9982width",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-97c6fecb-8354-4ace-b337-e544b0ba9982height",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabnum_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-97c6fecb-8354-4ace-b337-e544b0ba9982num_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73awidth-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73aheight-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c2b5afb3-d079-4afb-9354-4c1a7aeae73aimage-97c6fecb-8354-4ace-b337-e544b0ba9982image",
+ "type": "default",
+ "source": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189video-c2b5afb3-d079-4afb-9354-4c1a7aeae73avideo",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "c2b5afb3-d079-4afb-9354-4c1a7aeae73a",
+ "sourceHandle": "video",
+ "targetHandle": "video"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189video-6030b112-b669-4626-86ab-b1bffa96f9d6item",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "sourceHandle": "video",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-6030b112-b669-4626-86ab-b1bffa96f9d6collection-9d72df22-9794-4c8b-af0b-a01548b5f852collection",
+ "type": "default",
+ "source": "6030b112-b669-4626-86ab-b1bffa96f9d6",
+ "target": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "sourceHandle": "collection",
+ "targetHandle": "collection"
+ },
+ {
+ "id": "reactflow__edge-6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbdvideo-9d72df22-9794-4c8b-af0b-a01548b5f852item",
+ "type": "default",
+ "source": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "target": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "sourceHandle": "video",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-9d72df22-9794-4c8b-af0b-a01548b5f852collection-4b32c22b-679d-4d5e-a167-c3309265fcd9videos",
+ "type": "default",
+ "source": "9d72df22-9794-4c8b-af0b-a01548b5f852",
+ "target": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "sourceHandle": "collection",
+ "targetHandle": "videos"
+ },
+ {
+ "id": "reactflow__edge-52e0f43e-0593-4ba6-bb50-df63464a6189fps-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue",
+ "type": "default",
+ "source": "52e0f43e-0593-4ba6-bb50-df63464a6189",
+ "target": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "sourceHandle": "fps",
+ "targetHandle": "value"
+ },
+ {
+ "id": "reactflow__edge-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue-4b32c22b-679d-4d5e-a167-c3309265fcd9fps",
+ "type": "default",
+ "source": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "target": "4b32c22b-679d-4d5e-a167-c3309265fcd9",
+ "sourceHandle": "value",
+ "targetHandle": "fps"
+ },
+ {
+ "id": "reactflow__edge-bdba5c5c-d0c2-4bfa-a910-3f4405b25cdcvalue-6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbdfps",
+ "type": "default",
+ "source": "bdba5c5c-d0c2-4bfa-a910-3f4405b25cdc",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "value",
+ "targetHandle": "fps"
+ },
+ {
+ "id": "reactflow__edge-f93806c9-a708-48dd-bbe8-83f56f8e9a1bimage-85041165-5a9f-474d-ba63-08b8de4c119dimage",
+ "type": "default",
+ "source": "f93806c9-a708-48dd-bbe8-83f56f8e9a1b",
+ "target": "85041165-5a9f-474d-ba63-08b8de4c119d",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-85041165-5a9f-474d-ba63-08b8de4c119dwidth",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "85041165-5a9f-474d-ba63-08b8de4c119d",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-85041165-5a9f-474d-ba63-08b8de4c119dheight",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "85041165-5a9f-474d-ba63-08b8de4c119d",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-85041165-5a9f-474d-ba63-08b8de4c119dimage-97c6fecb-8354-4ace-b337-e544b0ba9982end_image",
+ "type": "default",
+ "source": "85041165-5a9f-474d-ba63-08b8de4c119d",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "end_image"
+ }
+ ]
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 Lightning w_ Concept LoRAs.json b/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 Lightning w_ Concept LoRAs.json
new file mode 100644
index 00000000000..463d0e718e6
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 Lightning w_ Concept LoRAs.json
@@ -0,0 +1,1449 @@
+{
+ "name": "Image to Video - Wan 2.2 Lightning w/ Concept LoRAs",
+ "author": "InvokeAI",
+ "description": "Fast image-to-video generation with Wan 2.2 I2V A14B + the Lightning LoRA pair. Provide an initial image and specify the resolution of the resulting video. The resolution will be applied to the shorter dimension of the image, and the other dimension will be scaled accordingly. Two inputs are provided for concept LoRAs (high noise and low noise pair).",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, image to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 I2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 I2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation, I2V variant):\n- Apply Lightning LoRA (High Noise): Wan 2.2 I2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 I2V Lightning Low Noise (4-step)\n\nOptional concept LoRAs (your own style/subject LoRAs):\n- Apply Concept LoRA (High Noise) / (Low Noise): leave empty, or select a high/low-noise concept LoRA pair.\n\nVRAM: for <=16 GB, use the 480p resolution (on the \"Ideal Dimensions\" node); 720p and 1080p need more VRAM.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 212
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 392
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 700,
+ "y": 572
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1074.3058275334374,
+ "y": 448
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 33
+ }
+ },
+ {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "type": "invocation",
+ "data": {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ref_image_encoder",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "",
+ "description": "",
+ "value": 1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 793.5169473342651,
+ "y": 1028.2344631556177
+ }
+ },
+ {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "type": "invocation",
+ "data": {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_i2v_ideal_dimensions",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "Video Resolution",
+ "description": "",
+ "value": "480p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 350,
+ "y": 1142
+ }
+ },
+ {
+ "id": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "type": "invocation",
+ "data": {
+ "id": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "version": "1.0.2",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "image",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "Starting Image",
+ "description": "First frame of the video"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -99.75141048988274,
+ "y": 1150.408659586883
+ }
+ },
+ {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "type": "invocation",
+ "data": {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "Num Frames",
+ "notes": "",
+ "type": "integer",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 211.2124370188066,
+ "y": 485.4909803666693
+ }
+ },
+ {
+ "id": "b33950d9-fbb2-449b-8669-bb550819debe",
+ "type": "invocation",
+ "data": {
+ "id": "b33950d9-fbb2-449b-8669-bb550819debe",
+ "version": "1.0.3",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "lora_selector",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -532.5193431121381,
+ "y": 842.2721310208782
+ }
+ },
+ {
+ "id": "659d84b3-6285-4454-b9b4-27867fe021d1",
+ "type": "invocation",
+ "data": {
+ "id": "659d84b3-6285-4454-b9b4-27867fe021d1",
+ "version": "1.0.3",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "lora_selector",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -535.636574689947,
+ "y": 665.6290082783777
+ }
+ },
+ {
+ "id": "43bebe21-dc56-4055-ad08-8c449d5412b8",
+ "type": "invocation",
+ "data": {
+ "id": "43bebe21-dc56-4055-ad08-8c449d5412b8",
+ "version": "1.0.3",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "lora_selector",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "LoRA (High Noise)",
+ "description": ""
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 0.75
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -193.78017832357807,
+ "y": 667.7071626635835
+ }
+ },
+ {
+ "id": "0c7f1216-4f62-424e-9a44-5de690334304",
+ "type": "invocation",
+ "data": {
+ "id": "0c7f1216-4f62-424e-9a44-5de690334304",
+ "version": "1.0.3",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "lora_selector",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "LoRA (Low Noise)",
+ "description": ""
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 0.75
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -189.62386955316634,
+ "y": 848.5065941764958
+ }
+ },
+ {
+ "id": "372a0951-3778-4dec-bdf3-5501720729e9",
+ "type": "invocation",
+ "data": {
+ "id": "372a0951-3778-4dec-bdf3-5501720729e9",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "collect",
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": "",
+ "description": ""
+ },
+ "collection": {
+ "name": "collection",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 186.3293016699355,
+ "y": 788.7040416253931
+ }
+ },
+ {
+ "id": "1814ecfc-99d9-47fe-b5f4-8aa518f65226",
+ "type": "invocation",
+ "data": {
+ "id": "1814ecfc-99d9-47fe-b5f4-8aa518f65226",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_collection_loader",
+ "inputs": {
+ "loras": {
+ "name": "loras",
+ "label": "",
+ "description": ""
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 299.61542102518126,
+ "y": 930.9273762507288
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-97c6fecb-8354-4ace-b337-e544b0ba9982ref_image-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabref_image",
+ "type": "default",
+ "source": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954image-97c6fecb-8354-4ace-b337-e544b0ba9982image",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7evae-97c6fecb-8354-4ace-b337-e544b0ba9982vae",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954width-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954height-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-97c6fecb-8354-4ace-b337-e544b0ba9982width",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-97c6fecb-8354-4ace-b337-e544b0ba9982height",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabnum_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-97c6fecb-8354-4ace-b337-e544b0ba9982num_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-659d84b3-6285-4454-b9b4-27867fe021d1lora-372a0951-3778-4dec-bdf3-5501720729e9item",
+ "type": "default",
+ "source": "659d84b3-6285-4454-b9b4-27867fe021d1",
+ "target": "372a0951-3778-4dec-bdf3-5501720729e9",
+ "sourceHandle": "lora",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-b33950d9-fbb2-449b-8669-bb550819debelora-372a0951-3778-4dec-bdf3-5501720729e9item",
+ "type": "default",
+ "source": "b33950d9-fbb2-449b-8669-bb550819debe",
+ "target": "372a0951-3778-4dec-bdf3-5501720729e9",
+ "sourceHandle": "lora",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-43bebe21-dc56-4055-ad08-8c449d5412b8lora-372a0951-3778-4dec-bdf3-5501720729e9item",
+ "type": "default",
+ "source": "43bebe21-dc56-4055-ad08-8c449d5412b8",
+ "target": "372a0951-3778-4dec-bdf3-5501720729e9",
+ "sourceHandle": "lora",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-0c7f1216-4f62-424e-9a44-5de690334304lora-372a0951-3778-4dec-bdf3-5501720729e9item",
+ "type": "default",
+ "source": "0c7f1216-4f62-424e-9a44-5de690334304",
+ "target": "372a0951-3778-4dec-bdf3-5501720729e9",
+ "sourceHandle": "lora",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-372a0951-3778-4dec-bdf3-5501720729e9collection-1814ecfc-99d9-47fe-b5f4-8aa518f65226loras",
+ "type": "default",
+ "source": "372a0951-3778-4dec-bdf3-5501720729e9",
+ "target": "1814ecfc-99d9-47fe-b5f4-8aa518f65226",
+ "sourceHandle": "collection",
+ "targetHandle": "loras"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-1814ecfc-99d9-47fe-b5f4-8aa518f65226transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "1814ecfc-99d9-47fe-b5f4-8aa518f65226",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-1814ecfc-99d9-47fe-b5f4-8aa518f65226transformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "1814ecfc-99d9-47fe-b5f4-8aa518f65226",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-i8V1oJeQCP",
+ "container-mUeBcw2tYf",
+ "divider-2T3tRpIIJO",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-GY6f00b3mJ",
+ "container-UVQRPgKi2B",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "divider-FKc8lld0Eb",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-zQA9XuRZRW": {
+ "id": "node-field-zQA9XuRZRW",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-FiH14mitcM",
+ "node-field-zQA9XuRZRW"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-dKizHbAltw",
+ "node-field-DonE1WRSA6"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-1YdNd0z10n"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-jLze5y1Ro6"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ },
+ "node-field-dKizHbAltw": {
+ "id": "node-field-dKizHbAltw",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "fieldName": "image"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-DonE1WRSA6": {
+ "id": "node-field-DonE1WRSA6",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "fieldName": "target_resolution"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "divider-2T3tRpIIJO": {
+ "id": "divider-2T3tRpIIJO",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "divider-FKc8lld0Eb": {
+ "id": "divider-FKc8lld0Eb",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "node-field-FiH14mitcM": {
+ "id": "node-field-FiH14mitcM",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "fieldName": "value"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-1YdNd0z10n": {
+ "id": "node-field-1YdNd0z10n",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "659d84b3-6285-4454-b9b4-27867fe021d1",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-jLze5y1Ro6": {
+ "id": "node-field-jLze5y1Ro6",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "b33950d9-fbb2-449b-8669-bb550819debe",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "heading-GY6f00b3mJ": {
+ "id": "heading-GY6f00b3mJ",
+ "parentId": "root",
+ "data": {
+ "content": "Concept LoRAs"
+ },
+ "type": "heading"
+ },
+ "container-UVQRPgKi2B": {
+ "id": "container-UVQRPgKi2B",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-3m4H6SLjXa",
+ "container-bYftAJOlcN"
+ ]
+ },
+ "type": "container"
+ },
+ "container-3m4H6SLjXa": {
+ "id": "container-3m4H6SLjXa",
+ "parentId": "container-UVQRPgKi2B",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Fgg4erL6iX"
+ ]
+ },
+ "type": "container"
+ },
+ "container-bYftAJOlcN": {
+ "id": "container-bYftAJOlcN",
+ "parentId": "container-UVQRPgKi2B",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-PwQ182RDMg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Fgg4erL6iX": {
+ "id": "container-Fgg4erL6iX",
+ "parentId": "container-3m4H6SLjXa",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-d3xPtCmNEi",
+ "node-field-cYnEuuoBNV"
+ ]
+ },
+ "type": "container"
+ },
+ "container-PwQ182RDMg": {
+ "id": "container-PwQ182RDMg",
+ "parentId": "container-bYftAJOlcN",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-bewJl9uAHX",
+ "node-field-g0DGEy8UTD"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-d3xPtCmNEi": {
+ "id": "node-field-d3xPtCmNEi",
+ "parentId": "container-Fgg4erL6iX",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "43bebe21-dc56-4055-ad08-8c449d5412b8",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-cYnEuuoBNV": {
+ "id": "node-field-cYnEuuoBNV",
+ "parentId": "container-Fgg4erL6iX",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "43bebe21-dc56-4055-ad08-8c449d5412b8",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-bewJl9uAHX": {
+ "id": "node-field-bewJl9uAHX",
+ "parentId": "container-PwQ182RDMg",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "0c7f1216-4f62-424e-9a44-5de690334304",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-g0DGEy8UTD": {
+ "id": "node-field-g0DGEy8UTD",
+ "parentId": "container-PwQ182RDMg",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "0c7f1216-4f62-424e-9a44-5de690334304",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_i2v_lightning_concept"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 Lightning.json b/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 Lightning.json
new file mode 100644
index 00000000000..d949853e894
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 Lightning.json
@@ -0,0 +1,1223 @@
+{
+ "name": "Image to Video - Wan 2.2 Lightning",
+ "author": "InvokeAI",
+ "description": "Fast image-to-video generation with Wan 2.2 I2V A14B + the Lightning LoRA pair. Provide an initial image and specify the resolution of the resulting video. The resolution will be applied to the shorter dimension of the image, and the other dimension will be scaled accordingly. ",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, image to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 I2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 I2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation, I2V variant):\n- Apply Lightning LoRA (High Noise): Wan 2.2 I2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 I2V Lightning Low Noise (4-step)\n\nVRAM: for <=16 GB, keep the resolution at 480p (on the \"Ideal Dimensions\" node); 720p and 1080p need more VRAM.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 212
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 392
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 700,
+ "y": 572
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1074.3058275334374,
+ "y": 448
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 33
+ }
+ },
+ {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "type": "invocation",
+ "data": {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Lightning LoRA (High Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 350,
+ "y": 772
+ }
+ },
+ {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "type": "invocation",
+ "data": {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 772
+ }
+ },
+ {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "type": "invocation",
+ "data": {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ref_image_encoder",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "",
+ "description": "",
+ "value": 1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 793.5169473342651,
+ "y": 1028.2344631556177
+ }
+ },
+ {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "type": "invocation",
+ "data": {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_i2v_ideal_dimensions",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "Video Resolution",
+ "description": "",
+ "value": "480p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 350,
+ "y": 1142
+ }
+ },
+ {
+ "id": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "type": "invocation",
+ "data": {
+ "id": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "version": "1.0.2",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "image",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "Starting Image",
+ "description": "First frame of the video"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 0,
+ "y": 980
+ }
+ },
+ {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "type": "invocation",
+ "data": {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "Num Frames",
+ "notes": "",
+ "type": "integer",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -42.07490820354809,
+ "y": 751.8724152644219
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-291abec8-b996-4c7b-959c-360f56a3ed60transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-291abec8-b996-4c7b-959c-360f56a3ed60transformer-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer",
+ "type": "default",
+ "source": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "target": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-97c6fecb-8354-4ace-b337-e544b0ba9982ref_image-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabref_image",
+ "type": "default",
+ "source": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954image-97c6fecb-8354-4ace-b337-e544b0ba9982image",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7evae-97c6fecb-8354-4ace-b337-e544b0ba9982vae",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954width-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954height-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-97c6fecb-8354-4ace-b337-e544b0ba9982width",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-97c6fecb-8354-4ace-b337-e544b0ba9982height",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabnum_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-97c6fecb-8354-4ace-b337-e544b0ba9982num_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-i8V1oJeQCP",
+ "container-mUeBcw2tYf",
+ "divider-2T3tRpIIJO",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "divider-FKc8lld0Eb",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-zQA9XuRZRW": {
+ "id": "node-field-zQA9XuRZRW",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-FiH14mitcM",
+ "node-field-zQA9XuRZRW"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-dKizHbAltw",
+ "node-field-DonE1WRSA6"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-a8nZxgz9u9": {
+ "id": "node-field-a8nZxgz9u9",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-NwR9yOugEz": {
+ "id": "node-field-NwR9yOugEz",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-a8nZxgz9u9",
+ "node-field-0al3LtpcGU"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-NwR9yOugEz",
+ "node-field-lgLAjS4nks"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-0al3LtpcGU": {
+ "id": "node-field-0al3LtpcGU",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-lgLAjS4nks": {
+ "id": "node-field-lgLAjS4nks",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ },
+ "node-field-dKizHbAltw": {
+ "id": "node-field-dKizHbAltw",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "fieldName": "image"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-DonE1WRSA6": {
+ "id": "node-field-DonE1WRSA6",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "fieldName": "target_resolution"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "divider-2T3tRpIIJO": {
+ "id": "divider-2T3tRpIIJO",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "divider-FKc8lld0Eb": {
+ "id": "divider-FKc8lld0Eb",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "node-field-FiH14mitcM": {
+ "id": "node-field-FiH14mitcM",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "fieldName": "value"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_i2v_lightning"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 TI2V-5B (Low Quality).json b/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 TI2V-5B (Low Quality).json
new file mode 100644
index 00000000000..35e92a8cdf7
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Image to Video - Wan 2.2 TI2V-5B (Low Quality).json
@@ -0,0 +1,871 @@
+{
+ "name": "Image to Video - Wan 2.2 TI2V (Low Quality)",
+ "author": "InvokeAI",
+ "description": "Low quality but fast image-to-video generation with Wan 2.2 TI2V 5B model. Provide an initial image and a target resolution for the video.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, image to video, video",
+ "notes": "This workflow needs the Wan 2.2 TI2V-5B model. Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 TI2V-5B (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): leave empty (TI2V-5B is a single-expert model)\n- VAE: Wan 2.2 TI2V-5B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\n720p should work on GPUs with 16 GB VRAM and up.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": ""
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 686.9799980678728,
+ "y": 210.13999972398182
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 392
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 700,
+ "y": 572
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 20
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 230
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 33
+ }
+ },
+ {
+ "id": "1473db93-45eb-4468-bba1-d0f9d1c21aed",
+ "type": "invocation",
+ "data": {
+ "id": "1473db93-45eb-4468-bba1-d0f9d1c21aed",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ref_image_encoder",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "",
+ "description": "",
+ "value": 1
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 669.4858247907592,
+ "y": -383.75896955550337
+ }
+ },
+ {
+ "id": "0771d3a4-7f0b-45bd-9c73-a99b40e24b7b",
+ "type": "invocation",
+ "data": {
+ "id": "0771d3a4-7f0b-45bd-9c73-a99b40e24b7b",
+ "version": "1.0.2",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "image",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "Initial Image",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -165.5308813883526,
+ "y": -364.6304014144227
+ }
+ },
+ {
+ "id": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "type": "invocation",
+ "data": {
+ "id": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ti2v_ideal_dimensions",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "",
+ "description": "",
+ "value": "720p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 261.9930762062073,
+ "y": -602.4138188363532
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-1473db93-45eb-4468-bba1-d0f9d1c21aedref_image-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabref_image",
+ "type": "default",
+ "source": "1473db93-45eb-4468-bba1-d0f9d1c21aed",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7evae-1473db93-45eb-4468-bba1-d0f9d1c21aedvae",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "1473db93-45eb-4468-bba1-d0f9d1c21aed",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-0771d3a4-7f0b-45bd-9c73-a99b40e24b7bimage-1473db93-45eb-4468-bba1-d0f9d1c21aedimage",
+ "type": "default",
+ "source": "0771d3a4-7f0b-45bd-9c73-a99b40e24b7b",
+ "target": "1473db93-45eb-4468-bba1-d0f9d1c21aed",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-0771d3a4-7f0b-45bd-9c73-a99b40e24b7bwidth-55cb8b4f-b7fc-4d37-9861-d5c84a57353bwidth",
+ "type": "default",
+ "source": "0771d3a4-7f0b-45bd-9c73-a99b40e24b7b",
+ "target": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-0771d3a4-7f0b-45bd-9c73-a99b40e24b7bheight-55cb8b4f-b7fc-4d37-9861-d5c84a57353bheight",
+ "type": "default",
+ "source": "0771d3a4-7f0b-45bd-9c73-a99b40e24b7b",
+ "target": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-55cb8b4f-b7fc-4d37-9861-d5c84a57353bwidth-1473db93-45eb-4468-bba1-d0f9d1c21aedwidth",
+ "type": "default",
+ "source": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "target": "1473db93-45eb-4468-bba1-d0f9d1c21aed",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-55cb8b4f-b7fc-4d37-9861-d5c84a57353bheight-1473db93-45eb-4468-bba1-d0f9d1c21aedheight",
+ "type": "default",
+ "source": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "target": "1473db93-45eb-4468-bba1-d0f9d1c21aed",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-55cb8b4f-b7fc-4d37-9861-d5c84a57353bwidth-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-55cb8b4f-b7fc-4d37-9861-d5c84a57353bheight-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "55cb8b4f-b7fc-4d37-9861-d5c84a57353b",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "node-field-e3hxuqgTgS",
+ "container-mUeBcw2tYf",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-zQA9XuRZRW": {
+ "id": "node-field-zQA9XuRZRW",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-ok9GStueKC": {
+ "id": "node-field-ok9GStueKC",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-ok9GStueKC",
+ "node-field-zQA9XuRZRW"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "node-field-e3hxuqgTgS": {
+ "id": "node-field-e3hxuqgTgS",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "0771d3a4-7f0b-45bd-9c73-a99b40e24b7b",
+ "fieldName": "image"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_i2v_ti2v5b"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Interpolate 2 Images to Video - Wan 2.2 Lightning.json b/invokeai/app/services/workflow_records/default_workflows/Interpolate 2 Images to Video - Wan 2.2 Lightning.json
new file mode 100644
index 00000000000..a6eba1aa40c
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Interpolate 2 Images to Video - Wan 2.2 Lightning.json
@@ -0,0 +1,1354 @@
+{
+ "name": "Interpolate 2 Images to Video - Wan 2.2 Lightning",
+ "author": "InvokeAI",
+ "description": "Interpolate a video between two images with Wan 2.2 I2V A14B (first-last-frame / FLF2V). Provide a starting image and an ending image; the model generates a clip that begins on the first image and animates smoothly to the second. Uses the Lightning LoRA pair for fast 4-step generation. The chosen resolution is applied to the shorter dimension and the other dimension is scaled to preserve aspect ratio.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, image to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 I2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 I2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation, I2V variant):\n- Apply Lightning LoRA (High Noise): Wan 2.2 I2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 I2V Lightning Low Noise (4-step)\n\nProvide BOTH a start image and an end image. VRAM: for <=16 GB, keep the resolution at 480p; 720p and 1080p need more VRAM.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "category": "default",
+ "version": "4.0.0"
+ },
+ "id": "default_wan22_interpolate_lightning",
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-i8V1oJeQCP",
+ "node-field-DonE1WRSA6",
+ "container-mUeBcw2tYf",
+ "divider-2T3tRpIIJO",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "divider-FKc8lld0Eb",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-zQA9XuRZRW": {
+ "id": "node-field-zQA9XuRZRW",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-FiH14mitcM",
+ "node-field-zQA9XuRZRW"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-dKizHbAltw",
+ "node-field-yH0i6aSLQ4"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-a8nZxgz9u9": {
+ "id": "node-field-a8nZxgz9u9",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-NwR9yOugEz": {
+ "id": "node-field-NwR9yOugEz",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-a8nZxgz9u9",
+ "node-field-0al3LtpcGU"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-NwR9yOugEz",
+ "node-field-lgLAjS4nks"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-0al3LtpcGU": {
+ "id": "node-field-0al3LtpcGU",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-lgLAjS4nks": {
+ "id": "node-field-lgLAjS4nks",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ },
+ "node-field-dKizHbAltw": {
+ "id": "node-field-dKizHbAltw",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "fieldName": "image"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-DonE1WRSA6": {
+ "id": "node-field-DonE1WRSA6",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "fieldName": "target_resolution"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "divider-2T3tRpIIJO": {
+ "id": "divider-2T3tRpIIJO",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "divider-FKc8lld0Eb": {
+ "id": "divider-FKc8lld0Eb",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "node-field-FiH14mitcM": {
+ "id": "node-field-FiH14mitcM",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "fieldName": "value"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-yH0i6aSLQ4": {
+ "id": "node-field-yH0i6aSLQ4",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "c845a30f-7d6a-4351-9dcd-ea3df4ebdc6e",
+ "fieldName": "image"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 212
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 392
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 700,
+ "y": 572
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1160.9641121313055,
+ "y": 461.16328373638504
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 33
+ }
+ },
+ {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "type": "invocation",
+ "data": {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Lightning LoRA (High Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 350,
+ "y": 772
+ }
+ },
+ {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "type": "invocation",
+ "data": {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 772
+ }
+ },
+ {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "type": "invocation",
+ "data": {
+ "id": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "version": "1.1.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_i2v_ideal_dimensions",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "Video Resolution",
+ "description": "",
+ "value": "480p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 182.69055978741454,
+ "y": 1039.2661332027988
+ }
+ },
+ {
+ "id": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "type": "invocation",
+ "data": {
+ "id": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "version": "1.0.2",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "image",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "Starting Image",
+ "description": "First frame of the video"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -270.0433070097871,
+ "y": 1057.784213432167
+ }
+ },
+ {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "type": "invocation",
+ "data": {
+ "id": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "Num Frames",
+ "notes": "",
+ "type": "integer",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -42.07490820354809,
+ "y": 751.8724152644219
+ }
+ },
+ {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "type": "invocation",
+ "data": {
+ "id": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "version": "1.2.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_ref_image_encoder",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "end_image": {
+ "name": "end_image",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 793.5169473342651,
+ "y": 1028.2344631556177
+ }
+ },
+ {
+ "id": "c845a30f-7d6a-4351-9dcd-ea3df4ebdc6e",
+ "type": "invocation",
+ "data": {
+ "id": "c845a30f-7d6a-4351-9dcd-ea3df4ebdc6e",
+ "version": "1.0.2",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "image",
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": "Ending Image",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": -270.4807620633504,
+ "y": 1327.9487214548142
+ }
+ },
+ {
+ "id": "0d821e80-76ad-47a4-b050-d55b3d5084bc",
+ "type": "invocation",
+ "data": {
+ "id": "0d821e80-76ad-47a4-b050-d55b3d5084bc",
+ "version": "1.2.2",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "img_resize",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": "",
+ "value": "auto"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "image": {
+ "name": "image",
+ "label": "",
+ "description": ""
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 512
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 512
+ },
+ "resample_mode": {
+ "name": "resample_mode",
+ "label": "",
+ "description": "",
+ "value": "bicubic"
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 247.59145192825213,
+ "y": 1381.3336415226458
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-291abec8-b996-4c7b-959c-360f56a3ed60transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-291abec8-b996-4c7b-959c-360f56a3ed60transformer-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer",
+ "type": "default",
+ "source": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "target": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-97c6fecb-8354-4ace-b337-e544b0ba9982ref_image-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabref_image",
+ "type": "default",
+ "source": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954image-97c6fecb-8354-4ace-b337-e544b0ba9982image",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7evae-97c6fecb-8354-4ace-b337-e544b0ba9982vae",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954width-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954height-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61width-97c6fecb-8354-4ace-b337-e544b0ba9982width",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-d27f29bf-6f26-4088-b1cc-c722ecd9fd61height-97c6fecb-8354-4ace-b337-e544b0ba9982height",
+ "type": "default",
+ "source": "d27f29bf-6f26-4088-b1cc-c722ecd9fd61",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabnum_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c857e3e8-334f-4a6e-9013-3326316e4281value-97c6fecb-8354-4ace-b337-e544b0ba9982num_frames",
+ "type": "default",
+ "source": "c857e3e8-334f-4a6e-9013-3326316e4281",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "value",
+ "targetHandle": "num_frames"
+ },
+ {
+ "id": "reactflow__edge-c845a30f-7d6a-4351-9dcd-ea3df4ebdc6eimage-0d821e80-76ad-47a4-b050-d55b3d5084bcimage",
+ "type": "default",
+ "source": "c845a30f-7d6a-4351-9dcd-ea3df4ebdc6e",
+ "target": "0d821e80-76ad-47a4-b050-d55b3d5084bc",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954width-0d821e80-76ad-47a4-b050-d55b3d5084bcwidth",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "0d821e80-76ad-47a4-b050-d55b3d5084bc",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-738ec1dd-416e-471d-acf0-ca324acd0954height-0d821e80-76ad-47a4-b050-d55b3d5084bcheight",
+ "type": "default",
+ "source": "738ec1dd-416e-471d-acf0-ca324acd0954",
+ "target": "0d821e80-76ad-47a4-b050-d55b3d5084bc",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-0d821e80-76ad-47a4-b050-d55b3d5084bcimage-97c6fecb-8354-4ace-b337-e544b0ba9982end_image",
+ "type": "default",
+ "source": "0d821e80-76ad-47a4-b050-d55b3d5084bc",
+ "target": "97c6fecb-8354-4ace-b337-e544b0ba9982",
+ "sourceHandle": "image",
+ "targetHandle": "end_image"
+ }
+ ]
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 Lightning w_ Concept LoRAs.json b/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 Lightning w_ Concept LoRAs.json
new file mode 100644
index 00000000000..f5585f9c4d5
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 Lightning w_ Concept LoRAs.json
@@ -0,0 +1,1178 @@
+{
+ "name": "Text to Video - Wan 2.2 Lightning w/ Concept LoRAs",
+ "author": "InvokeAI",
+ "description": "Fast text-to-video generation with Wan 2.2 T2V A14B + the Lightning LoRA pair. Distillation lets the model converge in 4 steps with CFG 1.0 (no negative branch), roughly 20x faster than the standard T2V workflow at the cost of some image quality. Pick the high-noise Lightning LoRA on 'Apply LoRA (High)' and the low-noise one on 'Apply LoRA (Low)' — the 'auto' target routes each to the right expert when the LoRAs are tagged. Defaults: 832x480, 81 frames @ 16 FPS (~5 s).",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, text to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 T2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 T2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation - use the T2V variant, NOT I2V):\n- Apply Lightning LoRA (High Noise): Wan 2.2 T2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 T2V Lightning Low Noise (4-step)\n\nThis workflow also has two optional concept-LoRA loaders (routed via 'target' = high/low) -- leave them empty or select your own high/low-noise concept LoRA pair.\n\nThe Lightning LoRAs are A14B-specific and distinct for T2V vs I2V. If your LoRAs are untagged (no 'expert' field), use each LoRA loader's 'target' dropdown to route the high-noise LoRA to 'high' and the low-noise to 'low'. CFG=1.0 skips the negative-conditioning branch, so the Negative Prompt is ignored at runtime.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 212
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 392
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 1400,
+ "y": 572
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1750,
+ "y": 230
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 2100,
+ "y": 33
+ }
+ },
+ {
+ "id": "db11f579-9c71-4e2e-8e4b-8d271a30befb",
+ "type": "invocation",
+ "data": {
+ "id": "db11f579-9c71-4e2e-8e4b-8d271a30befb",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Concept LoRA (Low Noise)",
+ "description": ""
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 0.75
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 772
+ }
+ },
+ {
+ "id": "acd771f6-19e4-4bfb-a443-3d613268ce80",
+ "type": "invocation",
+ "data": {
+ "id": "acd771f6-19e4-4bfb-a443-3d613268ce80",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Concept LoRA (High Noise)",
+ "description": ""
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 0.75
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 772
+ }
+ },
+ {
+ "id": "5d18cf53-905d-4eb6-afd4-6c19f790b374",
+ "type": "invocation",
+ "data": {
+ "id": "5d18cf53-905d-4eb6-afd4-6c19f790b374",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 350,
+ "y": 772
+ }
+ },
+ {
+ "id": "eb6b4c66-0bfb-4e0d-bc39-9d8814f3cd75",
+ "type": "invocation",
+ "data": {
+ "id": "eb6b4c66-0bfb-4e0d-bc39-9d8814f3cd75",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 772
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-5d18cf53-905d-4eb6-afd4-6c19f790b374transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "5d18cf53-905d-4eb6-afd4-6c19f790b374",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-5d18cf53-905d-4eb6-afd4-6c19f790b374transformer-eb6b4c66-0bfb-4e0d-bc39-9d8814f3cd75transformer",
+ "type": "default",
+ "source": "5d18cf53-905d-4eb6-afd4-6c19f790b374",
+ "target": "eb6b4c66-0bfb-4e0d-bc39-9d8814f3cd75",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-eb6b4c66-0bfb-4e0d-bc39-9d8814f3cd75transformer-acd771f6-19e4-4bfb-a443-3d613268ce80transformer",
+ "type": "default",
+ "source": "eb6b4c66-0bfb-4e0d-bc39-9d8814f3cd75",
+ "target": "acd771f6-19e4-4bfb-a443-3d613268ce80",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-acd771f6-19e4-4bfb-a443-3d613268ce80transformer-db11f579-9c71-4e2e-8e4b-8d271a30befbtransformer",
+ "type": "default",
+ "source": "acd771f6-19e4-4bfb-a443-3d613268ce80",
+ "target": "db11f579-9c71-4e2e-8e4b-8d271a30befb",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-db11f579-9c71-4e2e-8e4b-8d271a30befbtransformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "db11f579-9c71-4e2e-8e4b-8d271a30befb",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-i8V1oJeQCP",
+ "container-mUeBcw2tYf",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-vSOD5q5gvL",
+ "container-EeeKKz36Ba",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-zQA9XuRZRW": {
+ "id": "node-field-zQA9XuRZRW",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-ok9GStueKC": {
+ "id": "node-field-ok9GStueKC",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-58JRDH0dfF": {
+ "id": "node-field-58JRDH0dfF",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-jIfJ3RUDyT": {
+ "id": "node-field-jIfJ3RUDyT",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-ok9GStueKC",
+ "node-field-zQA9XuRZRW"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-jIfJ3RUDyT",
+ "node-field-58JRDH0dfF"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-tpqKlLmMtv"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-yuWwWH1ucK"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ },
+ "heading-vSOD5q5gvL": {
+ "id": "heading-vSOD5q5gvL",
+ "parentId": "root",
+ "data": {
+ "content": "Concept LoRAs"
+ },
+ "type": "heading"
+ },
+ "container-EeeKKz36Ba": {
+ "id": "container-EeeKKz36Ba",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Qxormn51Mx",
+ "container-N0tnTlpnZl"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Qxormn51Mx": {
+ "id": "container-Qxormn51Mx",
+ "parentId": "container-EeeKKz36Ba",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-8vzmVHxNM3"
+ ]
+ },
+ "type": "container"
+ },
+ "container-N0tnTlpnZl": {
+ "id": "container-N0tnTlpnZl",
+ "parentId": "container-EeeKKz36Ba",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-HWoz5KuI1T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-8vzmVHxNM3": {
+ "id": "container-8vzmVHxNM3",
+ "parentId": "container-Qxormn51Mx",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-L0YRkfsKLf",
+ "node-field-1dC61dfAnb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-HWoz5KuI1T": {
+ "id": "container-HWoz5KuI1T",
+ "parentId": "container-N0tnTlpnZl",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-vJThkvS9Ky",
+ "node-field-33PaXUUXpM"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-tpqKlLmMtv": {
+ "id": "node-field-tpqKlLmMtv",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "5d18cf53-905d-4eb6-afd4-6c19f790b374",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-yuWwWH1ucK": {
+ "id": "node-field-yuWwWH1ucK",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "eb6b4c66-0bfb-4e0d-bc39-9d8814f3cd75",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-L0YRkfsKLf": {
+ "id": "node-field-L0YRkfsKLf",
+ "parentId": "container-8vzmVHxNM3",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "acd771f6-19e4-4bfb-a443-3d613268ce80",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-1dC61dfAnb": {
+ "id": "node-field-1dC61dfAnb",
+ "parentId": "container-8vzmVHxNM3",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "acd771f6-19e4-4bfb-a443-3d613268ce80",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-vJThkvS9Ky": {
+ "id": "node-field-vJThkvS9Ky",
+ "parentId": "container-HWoz5KuI1T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "db11f579-9c71-4e2e-8e4b-8d271a30befb",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-33PaXUUXpM": {
+ "id": "node-field-33PaXUUXpM",
+ "parentId": "container-HWoz5KuI1T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "db11f579-9c71-4e2e-8e4b-8d271a30befb",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_t2v_lightning_concept"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 Lightning.json b/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 Lightning.json
new file mode 100644
index 00000000000..df61ff0c6f0
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 Lightning.json
@@ -0,0 +1,986 @@
+{
+ "name": "Text to Video - Wan 2.2 Lightning",
+ "author": "InvokeAI",
+ "description": "Fast text-to-video generation with Wan 2.2 T2V A14B + the Lightning LoRA pair. Distillation lets the model converge in 4 steps with CFG 1.0 (no negative branch), roughly 20x faster than the standard T2V workflow at the cost of some image quality. Pick the high-noise Lightning LoRA on 'Apply LoRA (High)' and the low-noise one on 'Apply LoRA (Low)' — the 'auto' target routes each to the right expert when the LoRAs are tagged. Defaults: 832x480, 81 frames @ 16 FPS (~5 s).",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, text to video, video, lightning, lora",
+ "notes": "Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 T2V A14B High Noise (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): Wan 2.2 T2V A14B Low Noise (Q4_K_M)\n- VAE: Wan 2.2 A14B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nLightning LoRAs (4-step distillation - use the T2V variant, NOT I2V):\n- Apply Lightning LoRA (High Noise): Wan 2.2 T2V Lightning High Noise (4-step)\n- Apply Lightning LoRA (Low Noise): Wan 2.2 T2V Lightning Low Noise (4-step)\n\nThe Lightning LoRAs are A14B-specific and distinct for T2V vs I2V. If your LoRAs are untagged (no 'expert' field), use each LoRA loader's 'target' dropdown to route the first to 'high' and the second to 'low'. CFG=1.0 skips the negative-conditioning branch, so the Negative Prompt is ignored at runtime.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "3466ad11-a931-4adc-b394-9ec287f0c23b",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "lora"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "weight"
+ },
+ {
+ "nodeId": "9b4cde65-699f-42c1-8889-76ac91fcb62f",
+ "fieldName": "target"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "Transformer (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 212
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt (unused at CFG=1.0)",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "",
+ "description": "",
+ "value": " "
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 392
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 700,
+ "y": 572
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG (High Noise)",
+ "description": "",
+ "value": 1
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low Noise)",
+ "description": "",
+ "value": 1
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 832
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 4
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1050,
+ "y": 230
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1400,
+ "y": 33
+ }
+ },
+ {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "type": "invocation",
+ "data": {
+ "id": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Apply Lightning LoRA (High Noise)",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (High Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "high"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 350,
+ "y": 772
+ }
+ },
+ {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "type": "invocation",
+ "data": {
+ "id": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_lora_loader",
+ "inputs": {
+ "lora": {
+ "name": "lora",
+ "label": "Lightning LoRA (Low Noise)",
+ "description": "",
+ "value": null
+ },
+ "weight": {
+ "name": "weight",
+ "label": "",
+ "description": "",
+ "value": 1
+ },
+ "target": {
+ "name": "target",
+ "label": "",
+ "description": "",
+ "value": "low"
+ },
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": 772
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7etransformer-291abec8-b996-4c7b-959c-360f56a3ed60transformer",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-291abec8-b996-4c7b-959c-360f56a3ed60transformer-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer",
+ "type": "default",
+ "source": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "target": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "reactflow__edge-ae8f115d-a3b8-4f76-b280-7313366a94adtransformer-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabtransformer",
+ "type": "default",
+ "source": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-XDPaQofdEE",
+ "node-field-KcT0glAFDK",
+ "container-i8V1oJeQCP",
+ "container-mUeBcw2tYf",
+ "heading-wuPtY8B4vR",
+ "container-AkpUH1WtID",
+ "heading-TwsoE3wQi9",
+ "container-0u7wyxEdNa",
+ "heading-10PuWMKJg9",
+ "container-C0z6ru33to"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-zQA9XuRZRW": {
+ "id": "node-field-zQA9XuRZRW",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-ok9GStueKC": {
+ "id": "node-field-ok9GStueKC",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-58JRDH0dfF": {
+ "id": "node-field-58JRDH0dfF",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-jIfJ3RUDyT": {
+ "id": "node-field-jIfJ3RUDyT",
+ "parentId": "container-i8V1oJeQCP",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-QDqQ4XOKvg": {
+ "id": "node-field-QDqQ4XOKvg",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-PdU0Sz2m5z": {
+ "id": "node-field-PdU0Sz2m5z",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-UiQ7MaseDR": {
+ "id": "node-field-UiQ7MaseDR",
+ "parentId": "container-mUeBcw2tYf",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-KcT0glAFDK": {
+ "id": "node-field-KcT0glAFDK",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-N9mexEKkYg": {
+ "id": "node-field-N9mexEKkYg",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-2u9lAbMBnD": {
+ "id": "node-field-2u9lAbMBnD",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-CTGqrO3x8o": {
+ "id": "node-field-CTGqrO3x8o",
+ "parentId": "container-C0z6ru33to",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-7G9srQT2Uw": {
+ "id": "node-field-7G9srQT2Uw",
+ "parentId": "container-7g1Xedgg8T",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Q345WyNzc4": {
+ "id": "node-field-Q345WyNzc4",
+ "parentId": "container-y0w7TfJC7e",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "container-mUeBcw2tYf": {
+ "id": "container-mUeBcw2tYf",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-UiQ7MaseDR",
+ "node-field-ok9GStueKC",
+ "node-field-zQA9XuRZRW"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-XDPaQofdEE": {
+ "id": "heading-XDPaQofdEE",
+ "parentId": "root",
+ "data": {
+ "content": "Generation"
+ },
+ "type": "heading"
+ },
+ "container-i8V1oJeQCP": {
+ "id": "container-i8V1oJeQCP",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-jIfJ3RUDyT",
+ "node-field-58JRDH0dfF"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-wuPtY8B4vR": {
+ "id": "heading-wuPtY8B4vR",
+ "parentId": "root",
+ "data": {
+ "content": "Model"
+ },
+ "type": "heading"
+ },
+ "container-AkpUH1WtID": {
+ "id": "container-AkpUH1WtID",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-y0w7TfJC7e",
+ "container-7g1Xedgg8T"
+ ]
+ },
+ "type": "container"
+ },
+ "container-y0w7TfJC7e": {
+ "id": "container-y0w7TfJC7e",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-Q345WyNzc4",
+ "node-field-PdU0Sz2m5z"
+ ]
+ },
+ "type": "container"
+ },
+ "container-7g1Xedgg8T": {
+ "id": "container-7g1Xedgg8T",
+ "parentId": "container-AkpUH1WtID",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-7G9srQT2Uw",
+ "node-field-QDqQ4XOKvg"
+ ]
+ },
+ "type": "container"
+ },
+ "container-C0z6ru33to": {
+ "id": "container-C0z6ru33to",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-N9mexEKkYg",
+ "node-field-2u9lAbMBnD",
+ "node-field-CTGqrO3x8o"
+ ]
+ },
+ "type": "container"
+ },
+ "heading-10PuWMKJg9": {
+ "id": "heading-10PuWMKJg9",
+ "parentId": "root",
+ "data": {
+ "content": "Advanced"
+ },
+ "type": "heading"
+ },
+ "container-0u7wyxEdNa": {
+ "id": "container-0u7wyxEdNa",
+ "parentId": "root",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-Um9gcFv0lo",
+ "container-4owWd0thWk"
+ ]
+ },
+ "type": "container"
+ },
+ "container-Um9gcFv0lo": {
+ "id": "container-Um9gcFv0lo",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-lF8o60KuVb"
+ ]
+ },
+ "type": "container"
+ },
+ "container-4owWd0thWk": {
+ "id": "container-4owWd0thWk",
+ "parentId": "container-0u7wyxEdNa",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-rvE0oy63qL"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-a8nZxgz9u9": {
+ "id": "node-field-a8nZxgz9u9",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "node-field-NwR9yOugEz": {
+ "id": "node-field-NwR9yOugEz",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "lora"
+ },
+ "showDescription": true
+ },
+ "type": "node-field"
+ },
+ "container-lF8o60KuVb": {
+ "id": "container-lF8o60KuVb",
+ "parentId": "container-Um9gcFv0lo",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-a8nZxgz9u9",
+ "node-field-0al3LtpcGU"
+ ]
+ },
+ "type": "container"
+ },
+ "container-rvE0oy63qL": {
+ "id": "container-rvE0oy63qL",
+ "parentId": "container-4owWd0thWk",
+ "data": {
+ "layout": "column",
+ "children": [
+ "node-field-NwR9yOugEz",
+ "node-field-lgLAjS4nks"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-0al3LtpcGU": {
+ "id": "node-field-0al3LtpcGU",
+ "parentId": "container-lF8o60KuVb",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "291abec8-b996-4c7b-959c-360f56a3ed60",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-lgLAjS4nks": {
+ "id": "node-field-lgLAjS4nks",
+ "parentId": "container-rvE0oy63qL",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "ae8f115d-a3b8-4f76-b280-7313366a94ad",
+ "fieldName": "weight"
+ },
+ "showDescription": true,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "heading-TwsoE3wQi9": {
+ "id": "heading-TwsoE3wQi9",
+ "parentId": "root",
+ "data": {
+ "content": "Lightning LoRAs"
+ },
+ "type": "heading"
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_t2v_lightning"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 TI2V-5B (Low Quality).json b/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 TI2V-5B (Low Quality).json
new file mode 100644
index 00000000000..1f9dd58b907
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Text to Video - Wan 2.2 TI2V-5B (Low Quality).json
@@ -0,0 +1,869 @@
+{
+ "name": "Text to Video - Wan 2.2 TI2V-5B (Low Quality)",
+ "author": "InvokeAI",
+ "description": "Text-to-video generation with Wan 2.2 TI2V-5B. Produces an MP4 of `num_frames` frames at `fps` frames-per-second (defaults: 81 frames @ 16 FPS = ~5 s, 720p ~1248x704). The output lands in the gallery alongside images and plays inline in the viewer.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, text to video, video",
+ "notes": "This workflow needs the Wan 2.2 TI2V-5B model. Each InvokeAI install assigns its own internal model IDs, so the model fields are left blank -- select these in the form after loading:\n\nMain Model node:\n- Main Model: Wan 2.2 TI2V-5B (Q4_K_M GGUF recommended for <=16 GB VRAM)\n- Transformer (Low Noise): leave empty (TI2V-5B is a single-expert model)\n- VAE: Wan 2.2 TI2V-5B VAE\n- T5 Encoder: Wan T5 Encoder (UMT5-XXL)\n\nDefault output is 720p (~1248x704); reduce the \"Ideal Dimensions\" preset to 480p for less VRAM.",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ }
+ ],
+ "meta": {
+ "version": "4.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_model_loader",
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "transformer_low_noise_model": {
+ "name": "transformer_low_noise_model",
+ "label": "",
+ "description": ""
+ },
+ "vae_model": {
+ "name": "vae_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "wan_t5_encoder_model": {
+ "name": "wan_t5_encoder_model",
+ "label": "",
+ "description": "",
+ "value": null
+ },
+ "component_source": {
+ "name": "component_source",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 200,
+ "y": 0
+ }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Positive Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "Positive Prompt",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 700,
+ "y": -200
+ }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "Negative Prompt",
+ "notes": "",
+ "type": "wan_text_encoder",
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "Negative Prompt",
+ "description": "",
+ "value": ""
+ },
+ "wan_t5_encoder": {
+ "name": "wan_t5_encoder",
+ "label": "",
+ "description": ""
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 683.3585813389434,
+ "y": 19.353124950263965
+ }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "version": "1.0.1",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "rand_int",
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "description": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "description": "",
+ "value": 2147483647
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false
+ },
+ "position": {
+ "x": 382.5329363121502,
+ "y": 709.7864089212082
+ }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_video_denoise",
+ "inputs": {
+ "transformer": {
+ "name": "transformer",
+ "label": "",
+ "description": ""
+ },
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": "",
+ "description": ""
+ },
+ "ref_image": {
+ "name": "ref_image",
+ "label": "",
+ "description": ""
+ },
+ "guidance_scale": {
+ "name": "guidance_scale",
+ "label": "CFG",
+ "description": "",
+ "value": 5
+ },
+ "guidance_scale_low_noise": {
+ "name": "guidance_scale_low_noise",
+ "label": "CFG (Low)",
+ "description": "",
+ "value": 4
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 720
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "num_frames": {
+ "name": "num_frames",
+ "label": "Frames",
+ "description": "",
+ "value": 81
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "description": "",
+ "value": 20
+ },
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "description": "",
+ "value": 0
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true
+ },
+ "position": {
+ "x": 1165.285565516453,
+ "y": -39.7591269778113
+ }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "version": "1.0.0",
+ "nodePack": "invokeai",
+ "label": "",
+ "notes": "",
+ "type": "wan_l2v",
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "",
+ "description": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": "",
+ "description": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": "",
+ "description": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": "",
+ "description": ""
+ },
+ "fps": {
+ "name": "fps",
+ "label": "FPS",
+ "description": "",
+ "value": 16
+ }
+ },
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true
+ },
+ "position": {
+ "x": 1531.0370931112793,
+ "y": -63.90613171839515
+ }
+ },
+ {
+ "id": "0cc328ea-b3bf-4ac1-98f3-844de0e072c8",
+ "type": "invocation",
+ "data": {
+ "id": "0cc328ea-b3bf-4ac1-98f3-844de0e072c8",
+ "type": "integer",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "nodePack": "invokeai",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Width",
+ "description": "Width of the video. Will be adjusted to meet the constraints of the selected target resolution.",
+ "value": 832
+ }
+ }
+ },
+ "position": {
+ "x": 370.6719737658655,
+ "y": 436.76341584866606
+ }
+ },
+ {
+ "id": "e20a326b-a312-4cc4-89cf-cfc13887cd4a",
+ "type": "invocation",
+ "data": {
+ "id": "e20a326b-a312-4cc4-89cf-cfc13887cd4a",
+ "type": "integer",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "nodePack": "invokeai",
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Height",
+ "description": "Height of the video. Will be adjusted to meet the constraints of the selected target resolution.",
+ "value": 480
+ }
+ }
+ },
+ "position": {
+ "x": 373.58106252303446,
+ "y": 567.1240792266004
+ }
+ },
+ {
+ "id": "0f05c4e0-f92f-4b23-9a57-c69ccc50c2cd",
+ "type": "invocation",
+ "data": {
+ "id": "0f05c4e0-f92f-4b23-9a57-c69ccc50c2cd",
+ "type": "wan_ti2v_ideal_dimensions",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "nodePack": "invokeai",
+ "inputs": {
+ "width": {
+ "name": "width",
+ "label": "",
+ "description": "",
+ "value": 1024
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "description": "",
+ "value": 480
+ },
+ "target_resolution": {
+ "name": "target_resolution",
+ "label": "",
+ "description": "",
+ "value": "720p"
+ },
+ "rounding": {
+ "name": "rounding",
+ "label": "",
+ "description": "",
+ "value": "nearest"
+ }
+ }
+ },
+ "position": {
+ "x": 773.8083794848407,
+ "y": 420.1537431373451
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-transformer-denoise",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2v",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2v",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-0cc328ea-b3bf-4ac1-98f3-844de0e072c8value-0f05c4e0-f92f-4b23-9a57-c69ccc50c2cdwidth",
+ "type": "default",
+ "source": "0cc328ea-b3bf-4ac1-98f3-844de0e072c8",
+ "target": "0f05c4e0-f92f-4b23-9a57-c69ccc50c2cd",
+ "sourceHandle": "value",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-0f05c4e0-f92f-4b23-9a57-c69ccc50c2cdwidth-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabwidth",
+ "type": "default",
+ "source": "0f05c4e0-f92f-4b23-9a57-c69ccc50c2cd",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-e20a326b-a312-4cc4-89cf-cfc13887cd4avalue-0f05c4e0-f92f-4b23-9a57-c69ccc50c2cdheight",
+ "type": "default",
+ "source": "e20a326b-a312-4cc4-89cf-cfc13887cd4a",
+ "target": "0f05c4e0-f92f-4b23-9a57-c69ccc50c2cd",
+ "sourceHandle": "value",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-0f05c4e0-f92f-4b23-9a57-c69ccc50c2cdheight-4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dabheight",
+ "type": "default",
+ "source": "0f05c4e0-f92f-4b23-9a57-c69ccc50c2cd",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ }
+ ],
+ "form": {
+ "elements": {
+ "root": {
+ "id": "root",
+ "data": {
+ "layout": "column",
+ "children": [
+ "heading-NVsTvi64mE",
+ "node-field-LCSk79Cykw",
+ "node-field-8eaiK9h1E6",
+ "node-field-z8AjtazZ1C",
+ "container-yTCxecjcVW",
+ "container-tL8WPBUtAl",
+ "container-WfDQ0EqgcT",
+ "divider-dWeIfMmqLs",
+ "heading-4KzzU0571E",
+ "container-ZPG1DkfsBF",
+ "container-LtEu7q4UHX"
+ ]
+ },
+ "type": "container"
+ },
+ "node-field-mz64Xfk59R": {
+ "id": "node-field-mz64Xfk59R",
+ "parentId": "container-tL8WPBUtAl",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "fieldName": "fps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-dADhnNIONx": {
+ "id": "node-field-dADhnNIONx",
+ "parentId": "container-tL8WPBUtAl",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "num_frames"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-GM02XkVswM": {
+ "id": "node-field-GM02XkVswM",
+ "parentId": "container-WfDQ0EqgcT",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "float-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-RN6aZuubTt": {
+ "id": "node-field-RN6aZuubTt",
+ "parentId": "container-WfDQ0EqgcT",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-8eaiK9h1E6": {
+ "id": "node-field-8eaiK9h1E6",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-LCSk79Cykw": {
+ "id": "node-field-LCSk79Cykw",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ "showDescription": false,
+ "settings": {
+ "type": "string-field-config",
+ "component": "textarea"
+ }
+ },
+ "type": "node-field"
+ },
+ "node-field-tmweV5koO8": {
+ "id": "node-field-tmweV5koO8",
+ "parentId": "container-YUjkEQOaZH",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-Ny6XBv2KCR": {
+ "id": "node-field-Ny6XBv2KCR",
+ "parentId": "container-LtEu7q4UHX",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-zmxixZsriN": {
+ "id": "node-field-zmxixZsriN",
+ "parentId": "container-LtEu7q4UHX",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "node-field-FnSN39ojJs": {
+ "id": "node-field-FnSN39ojJs",
+ "parentId": "container-YUjkEQOaZH",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ "showDescription": false
+ },
+ "type": "node-field"
+ },
+ "heading-NVsTvi64mE": {
+ "id": "heading-NVsTvi64mE",
+ "parentId": "root",
+ "type": "heading",
+ "data": {
+ "content": "Generation"
+ }
+ },
+ "container-WfDQ0EqgcT": {
+ "id": "container-WfDQ0EqgcT",
+ "parentId": "root",
+ "type": "container",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-RN6aZuubTt",
+ "node-field-GM02XkVswM"
+ ]
+ }
+ },
+ "container-bOWWWaSIsY": {
+ "id": "container-bOWWWaSIsY",
+ "parentId": "container-yTCxecjcVW",
+ "type": "container",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-41iwUAOdbd",
+ "node-field-C8Yua39ccI"
+ ]
+ }
+ },
+ "container-yTCxecjcVW": {
+ "id": "container-yTCxecjcVW",
+ "parentId": "root",
+ "type": "container",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-bOWWWaSIsY"
+ ]
+ }
+ },
+ "container-tL8WPBUtAl": {
+ "id": "container-tL8WPBUtAl",
+ "parentId": "root",
+ "type": "container",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-dADhnNIONx",
+ "node-field-mz64Xfk59R"
+ ]
+ }
+ },
+ "heading-4KzzU0571E": {
+ "id": "heading-4KzzU0571E",
+ "parentId": "root",
+ "type": "heading",
+ "data": {
+ "content": "Model"
+ }
+ },
+ "divider-dWeIfMmqLs": {
+ "id": "divider-dWeIfMmqLs",
+ "parentId": "root",
+ "type": "divider"
+ },
+ "container-YUjkEQOaZH": {
+ "id": "container-YUjkEQOaZH",
+ "parentId": "container-ZPG1DkfsBF",
+ "type": "container",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-FnSN39ojJs",
+ "node-field-tmweV5koO8"
+ ]
+ }
+ },
+ "container-ZPG1DkfsBF": {
+ "id": "container-ZPG1DkfsBF",
+ "parentId": "root",
+ "type": "container",
+ "data": {
+ "layout": "row",
+ "children": [
+ "container-YUjkEQOaZH"
+ ]
+ }
+ },
+ "container-LtEu7q4UHX": {
+ "id": "container-LtEu7q4UHX",
+ "parentId": "root",
+ "type": "container",
+ "data": {
+ "layout": "row",
+ "children": [
+ "node-field-Ny6XBv2KCR",
+ "node-field-zmxixZsriN"
+ ]
+ }
+ },
+ "node-field-z8AjtazZ1C": {
+ "id": "node-field-z8AjtazZ1C",
+ "type": "node-field",
+ "parentId": "root",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "0f05c4e0-f92f-4b23-9a57-c69ccc50c2cd",
+ "fieldName": "target_resolution"
+ },
+ "showDescription": true
+ }
+ },
+ "node-field-41iwUAOdbd": {
+ "id": "node-field-41iwUAOdbd",
+ "type": "node-field",
+ "parentId": "container-bOWWWaSIsY",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "0cc328ea-b3bf-4ac1-98f3-844de0e072c8",
+ "fieldName": "value"
+ },
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ },
+ "showDescription": true
+ }
+ },
+ "node-field-C8Yua39ccI": {
+ "id": "node-field-C8Yua39ccI",
+ "type": "node-field",
+ "parentId": "container-bOWWWaSIsY",
+ "data": {
+ "fieldIdentifier": {
+ "nodeId": "e20a326b-a312-4cc4-89cf-cfc13887cd4a",
+ "fieldName": "value"
+ },
+ "settings": {
+ "type": "integer-field-config",
+ "showShuffle": false,
+ "component": "number-input"
+ },
+ "showDescription": true
+ }
+ }
+ },
+ "rootElementId": "root"
+ },
+ "id": "default_wan22_t2v_ti2v5b"
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Wan 2.2 Image to Image.json b/invokeai/app/services/workflow_records/default_workflows/Wan 2.2 Image to Image.json
new file mode 100644
index 00000000000..7975e2c0a56
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Wan 2.2 Image to Image.json
@@ -0,0 +1,305 @@
+{
+ "id": "default_wan22_i2v_c2d5e1b3-3e4f-5b6c-af7d-8e9f0a1b2c3e",
+ "name": "Image to Image - Wan 2.2",
+ "author": "InvokeAI",
+ "description": "Image-to-image generation with Wan 2.2 I2V A14B. The reference image is VAE-encoded and concatenated to the noise latents each step (the I2V transformer has in_channels=36). Drop a reference image into the 'Reference Image' input, then invoke. Only the I2V A14B variant is supported — T2V and TI2V-5B don't consume reference images.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, image to image",
+ "notes": "Prerequisite model downloads: a Wan 2.2 I2V A14B main (Diffusers or GGUF expert pair). For GGUF mains, also install the Component Source (Diffusers Wan I2V) OR the standalone Wan VAE + UMT5-XXL encoder. Wan 2.2 I2V was trained for video — at single-frame inference it tends to anchor strongly to the reference. Recommended settings: 30-40 steps and CFG 5-7 (or 4 steps and CFG 1 with the Wan I2V Lightning LoRA pair).",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "7a6edc2d-f38e-a0c1-e27a-8f9dcbb20fce",
+ "fieldName": "image"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ }
+ ],
+ "meta": {
+ "version": "3.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "wan_model_loader",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false,
+ "inputs": {
+ "model": { "name": "model", "label": "" },
+ "transformer_low_noise_model": { "name": "transformer_low_noise_model", "label": "" },
+ "vae_model": { "name": "vae_model", "label": "" },
+ "wan_t5_encoder_model": { "name": "wan_t5_encoder_model", "label": "" },
+ "component_source": { "name": "component_source", "label": "" }
+ }
+ },
+ "position": { "x": 200, "y": 0 }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "wan_text_encoder",
+ "version": "1.0.0",
+ "label": "Positive Prompt",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "prompt": { "name": "prompt", "label": "", "value": "" },
+ "wan_t5_encoder": { "name": "wan_t5_encoder", "label": "" }
+ }
+ },
+ "position": { "x": 700, "y": -200 }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "wan_text_encoder",
+ "version": "1.0.0",
+ "label": "Negative Prompt",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "prompt": { "name": "prompt", "label": "", "value": " " },
+ "wan_t5_encoder": { "name": "wan_t5_encoder", "label": "" }
+ }
+ },
+ "position": { "x": 700, "y": 100 }
+ },
+ {
+ "id": "7a6edc2d-f38e-a0c1-e27a-8f9dcbb20fce",
+ "type": "invocation",
+ "data": {
+ "id": "7a6edc2d-f38e-a0c1-e27a-8f9dcbb20fce",
+ "type": "wan_ref_image_encoder",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "image": { "name": "image", "label": "Reference Image" },
+ "vae": { "name": "vae", "label": "" },
+ "width": { "name": "width", "label": "", "value": 1024 },
+ "height": { "name": "height", "label": "", "value": 1024 }
+ }
+ },
+ "position": { "x": 700, "y": 300 }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "rand_int",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false,
+ "inputs": {
+ "low": { "name": "low", "label": "", "value": 0 },
+ "high": { "name": "high", "label": "", "value": 2147483647 }
+ }
+ },
+ "position": { "x": 700, "y": 550 }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "wan_denoise",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "transformer": { "name": "transformer", "label": "" },
+ "positive_conditioning": { "name": "positive_conditioning", "label": "" },
+ "negative_conditioning": { "name": "negative_conditioning", "label": "" },
+ "ref_image": { "name": "ref_image", "label": "" },
+ "latents": { "name": "latents", "label": "" },
+ "denoise_mask": { "name": "denoise_mask", "label": "" },
+ "denoising_start": { "name": "denoising_start", "label": "", "value": 0 },
+ "denoising_end": { "name": "denoising_end", "label": "", "value": 1 },
+ "add_noise": { "name": "add_noise", "label": "", "value": true },
+ "guidance_scale": { "name": "guidance_scale", "label": "CFG", "value": 5.0 },
+ "guidance_scale_low_noise": { "name": "guidance_scale_low_noise", "label": "CFG (Low)" },
+ "width": { "name": "width", "label": "", "value": 1024 },
+ "height": { "name": "height", "label": "", "value": 1024 },
+ "steps": { "name": "steps", "label": "", "value": 30 },
+ "seed": { "name": "seed", "label": "", "value": 0 }
+ }
+ },
+ "position": { "x": 1100, "y": -50 }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "wan_l2i",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true,
+ "inputs": {
+ "board": { "name": "board", "label": "" },
+ "metadata": { "name": "metadata", "label": "" },
+ "latents": { "name": "latents", "label": "" },
+ "vae": { "name": "vae", "label": "" }
+ }
+ },
+ "position": { "x": 1550, "y": -50 }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-transformer-denoise",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2i",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-loader-vae-refenc",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "7a6edc2d-f38e-a0c1-e27a-8f9dcbb20fce",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-refenc-refimage-denoise",
+ "type": "default",
+ "source": "7a6edc2d-f38e-a0c1-e27a-8f9dcbb20fce",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "ref_image",
+ "targetHandle": "ref_image"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2i",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ }
+ ]
+}
diff --git a/invokeai/app/services/workflow_records/default_workflows/Wan 2.2 Text to Image.json b/invokeai/app/services/workflow_records/default_workflows/Wan 2.2 Text to Image.json
new file mode 100644
index 00000000000..3fc9395c232
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/Wan 2.2 Text to Image.json
@@ -0,0 +1,264 @@
+{
+ "id": "default_wan22_t2v_b1c4f0a2-2d3e-4a5b-9f6c-7d8e0a1b2c3d",
+ "name": "Text to Image - Wan 2.2",
+ "author": "InvokeAI",
+ "description": "Text-to-image generation with Wan 2.2 (T2V A14B or TI2V-5B). For A14B GGUFs, wire the second-expert transformer into 'Transformer (Low Noise)' and pick a Diffusers Wan as the Component Source (or use standalone VAE + UMT5-XXL encoder). TI2V-5B is a single-transformer model — leave the low-noise slot empty.",
+ "version": "1.0.0",
+ "contact": "",
+ "tags": "wan2.2, text to image",
+ "notes": "Prerequisite model downloads: a Wan 2.2 main model (Diffusers or GGUF). For GGUF mains, also install the Component Source (Diffusers Wan) OR the standalone Wan VAE + UMT5-XXL encoder. The Wan 2.2 starter bundle in the Model Manager pulls everything you need for T2V A14B Q4_K_M/Q8_0. Recommended settings: 30-40 steps and CFG 5-7 (or 4 steps and CFG 1 with the Wan Lightning LoRA pair).",
+ "exposedFields": [
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "transformer_low_noise_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "component_source"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "vae_model"
+ },
+ {
+ "nodeId": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "fieldName": "wan_t5_encoder_model"
+ },
+ {
+ "nodeId": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "steps"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "guidance_scale_low_noise"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "width"
+ },
+ {
+ "nodeId": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "fieldName": "height"
+ }
+ ],
+ "meta": {
+ "version": "3.0.0",
+ "category": "default"
+ },
+ "nodes": [
+ {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "invocation",
+ "data": {
+ "id": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "type": "wan_model_loader",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false,
+ "inputs": {
+ "model": { "name": "model", "label": "" },
+ "transformer_low_noise_model": { "name": "transformer_low_noise_model", "label": "" },
+ "vae_model": { "name": "vae_model", "label": "" },
+ "wan_t5_encoder_model": { "name": "wan_t5_encoder_model", "label": "" },
+ "component_source": { "name": "component_source", "label": "" }
+ }
+ },
+ "position": { "x": 200, "y": 0 }
+ },
+ {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "invocation",
+ "data": {
+ "id": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "type": "wan_text_encoder",
+ "version": "1.0.0",
+ "label": "Positive Prompt",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "prompt": { "name": "prompt", "label": "", "value": "a cat" },
+ "wan_t5_encoder": { "name": "wan_t5_encoder", "label": "" }
+ }
+ },
+ "position": { "x": 700, "y": -200 }
+ },
+ {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "invocation",
+ "data": {
+ "id": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "type": "wan_text_encoder",
+ "version": "1.0.0",
+ "label": "Negative Prompt",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "prompt": { "name": "prompt", "label": "", "value": " " },
+ "wan_t5_encoder": { "name": "wan_t5_encoder", "label": "" }
+ }
+ },
+ "position": { "x": 700, "y": 100 }
+ },
+ {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "invocation",
+ "data": {
+ "id": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "type": "rand_int",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": false,
+ "inputs": {
+ "low": { "name": "low", "label": "", "value": 0 },
+ "high": { "name": "high", "label": "", "value": 2147483647 }
+ }
+ },
+ "position": { "x": 700, "y": 400 }
+ },
+ {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "invocation",
+ "data": {
+ "id": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "type": "wan_denoise",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "transformer": { "name": "transformer", "label": "" },
+ "positive_conditioning": { "name": "positive_conditioning", "label": "" },
+ "negative_conditioning": { "name": "negative_conditioning", "label": "" },
+ "ref_image": { "name": "ref_image", "label": "" },
+ "latents": { "name": "latents", "label": "" },
+ "denoise_mask": { "name": "denoise_mask", "label": "" },
+ "denoising_start": { "name": "denoising_start", "label": "", "value": 0 },
+ "denoising_end": { "name": "denoising_end", "label": "", "value": 1 },
+ "add_noise": { "name": "add_noise", "label": "", "value": true },
+ "guidance_scale": { "name": "guidance_scale", "label": "CFG", "value": 5.0 },
+ "guidance_scale_low_noise": { "name": "guidance_scale_low_noise", "label": "CFG (Low)" },
+ "width": { "name": "width", "label": "", "value": 1024 },
+ "height": { "name": "height", "label": "", "value": 1024 },
+ "steps": { "name": "steps", "label": "", "value": 30 },
+ "seed": { "name": "seed", "label": "", "value": 0 }
+ }
+ },
+ "position": { "x": 1100, "y": -50 }
+ },
+ {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "invocation",
+ "data": {
+ "id": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "type": "wan_l2i",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true,
+ "inputs": {
+ "board": { "name": "board", "label": "" },
+ "metadata": { "name": "metadata", "label": "" },
+ "latents": { "name": "latents", "label": "" },
+ "vae": { "name": "vae", "label": "" }
+ }
+ },
+ "position": { "x": 1550, "y": -50 }
+ }
+ ],
+ "edges": [
+ {
+ "id": "edge-loader-transformer-denoise",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "transformer",
+ "targetHandle": "transformer"
+ },
+ {
+ "id": "edge-loader-t5-pos",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-t5-neg",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "sourceHandle": "wan_t5_encoder",
+ "targetHandle": "wan_t5_encoder"
+ },
+ {
+ "id": "edge-loader-vae-l2i",
+ "type": "default",
+ "source": "1a0e6c7b-9d2f-4b3c-8e1a-2f3d4c5b6a7e",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "edge-pos-cond-denoise",
+ "type": "default",
+ "source": "2b1f7d8c-ae3f-5c4d-9f2b-3a4e5d6c7b8f",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "edge-neg-cond-denoise",
+ "type": "default",
+ "source": "3c2a8e9d-bf4a-6d5e-af3c-4b5f6e7d8c9a",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "edge-rand-seed-denoise",
+ "type": "default",
+ "source": "5e4cab0b-d16c-8faf-c05e-6d7baf90ebbc",
+ "target": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "edge-denoise-latents-l2i",
+ "type": "default",
+ "source": "4d3b9faf-c05b-7e6f-bf4d-5c6a7f8e9dab",
+ "target": "6f5dcb1c-e27d-9fb0-d16f-7e8cbaa1fcbd",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ }
+ ]
+}
diff --git a/invokeai/app/util/step_callback.py b/invokeai/app/util/step_callback.py
index 08dc9a2265c..9364ec9b8ce 100644
--- a/invokeai/app/util/step_callback.py
+++ b/invokeai/app/util/step_callback.py
@@ -179,6 +179,84 @@
ANIMA_LATENT_RGB_BIAS = [-0.1835, -0.0868, -0.3360]
+# Wan 2.2 A14B uses the standard 16-channel Wan VAE.
+# Factors come from ComfyUI's Wan21 latent_format (same VAE as A14B).
+WAN_LATENT_RGB_FACTORS = [
+ [-0.1299, -0.1692, 0.2932],
+ [0.0671, 0.0406, 0.0442],
+ [0.3568, 0.2548, 0.1747],
+ [0.0372, 0.2344, 0.1420],
+ [0.0313, 0.0189, -0.0328],
+ [0.0296, -0.0956, -0.0665],
+ [-0.3477, -0.4059, -0.2925],
+ [0.0166, 0.1902, 0.1975],
+ [-0.0412, 0.0267, -0.1364],
+ [-0.1293, 0.0740, 0.1636],
+ [0.0680, 0.3019, 0.1128],
+ [0.0032, 0.0581, 0.0639],
+ [-0.1251, 0.0927, 0.1699],
+ [0.0060, -0.0633, 0.0005],
+ [0.3477, 0.2275, 0.2950],
+ [0.1984, 0.0913, 0.1861],
+]
+
+WAN_LATENT_RGB_BIAS = [-0.1835, -0.0868, -0.3360]
+
+# Wan 2.2 TI2V-5B uses Wan2.2-VAE with 48 latent channels and 16x spatial downscale.
+# Factors come from ComfyUI's Wan22 latent_format.
+WAN22_LATENT_RGB_FACTORS = [
+ [0.0119, 0.0103, 0.0046],
+ [-0.1062, -0.0504, 0.0165],
+ [0.0140, 0.0409, 0.0491],
+ [-0.0813, -0.0677, 0.0607],
+ [0.0656, 0.0851, 0.0808],
+ [0.0264, 0.0463, 0.0912],
+ [0.0295, 0.0326, 0.0590],
+ [-0.0244, -0.0270, 0.0025],
+ [0.0443, -0.0102, 0.0288],
+ [-0.0465, -0.0090, -0.0205],
+ [0.0359, 0.0236, 0.0082],
+ [-0.0776, 0.0854, 0.1048],
+ [0.0564, 0.0264, 0.0561],
+ [0.0006, 0.0594, 0.0418],
+ [-0.0319, -0.0542, -0.0637],
+ [-0.0268, 0.0024, 0.0260],
+ [0.0539, 0.0265, 0.0358],
+ [-0.0359, -0.0312, -0.0287],
+ [-0.0285, -0.1032, -0.1237],
+ [0.1041, 0.0537, 0.0622],
+ [-0.0086, -0.0374, -0.0051],
+ [0.0390, 0.0670, 0.2863],
+ [0.0069, 0.0144, 0.0082],
+ [0.0006, -0.0167, 0.0079],
+ [0.0313, -0.0574, -0.0232],
+ [-0.1454, -0.0902, -0.0481],
+ [0.0714, 0.0827, 0.0447],
+ [-0.0304, -0.0574, -0.0196],
+ [0.0401, 0.0384, 0.0204],
+ [-0.0758, -0.0297, -0.0014],
+ [0.0568, 0.1307, 0.1372],
+ [-0.0055, -0.0310, -0.0380],
+ [0.0239, -0.0305, 0.0325],
+ [-0.0663, -0.0673, -0.0140],
+ [-0.0416, -0.0047, -0.0023],
+ [0.0166, 0.0112, -0.0093],
+ [-0.0211, 0.0011, 0.0331],
+ [0.1833, 0.1466, 0.2250],
+ [-0.0368, 0.0370, 0.0295],
+ [-0.3441, -0.3543, -0.2008],
+ [-0.0479, -0.0489, -0.0420],
+ [-0.0660, -0.0153, 0.0800],
+ [-0.0101, 0.0068, 0.0156],
+ [-0.0690, -0.0452, -0.0927],
+ [-0.0145, 0.0041, 0.0015],
+ [0.0421, 0.0451, 0.0373],
+ [0.0504, -0.0483, -0.0356],
+ [-0.0837, 0.0168, 0.0055],
+]
+
+WAN22_LATENT_RGB_BIAS = [0.0317, -0.0878, -0.1388]
+
def sample_to_lowres_estimated_image(
samples: torch.Tensor,
@@ -270,6 +348,15 @@ def diffusion_step_callback(
# Anima uses Wan 2.1 VAE with 16 latent channels
latent_rgb_factors = ANIMA_LATENT_RGB_FACTORS
latent_rgb_bias = ANIMA_LATENT_RGB_BIAS
+ elif base_model == BaseModelType.Wan:
+ # A14B (16-ch standard Wan VAE, 8x spatial) vs TI2V-5B (48-ch Wan2.2-VAE,
+ # 16x spatial). The latent channel count uniquely identifies the variant.
+ if sample.shape[-3] == 48:
+ latent_rgb_factors = WAN22_LATENT_RGB_FACTORS
+ latent_rgb_bias = WAN22_LATENT_RGB_BIAS
+ else:
+ latent_rgb_factors = WAN_LATENT_RGB_FACTORS
+ latent_rgb_bias = WAN_LATENT_RGB_BIAS
else:
raise ValueError(f"Unsupported base model: {base_model}")
@@ -287,8 +374,13 @@ def diffusion_step_callback(
latent_rgb_bias=latent_rgb_bias_torch,
)
- width = image.width * 8
- height = image.height * 8
+ # Spatial downscale ratio: 8x is the SD/SDXL/FLUX/Wan-A14B default;
+ # Wan TI2V-5B's Wan2.2-VAE uses 16x.
+ spatial_scale = 8
+ if base_model == BaseModelType.Wan and sample.shape[-3] == 48:
+ spatial_scale = 16
+ width = image.width * spatial_scale
+ height = image.height * spatial_scale
percentage = calc_percentage(intermediate_state)
signal_progress("Denoising", percentage, image, (width, height))
diff --git a/invokeai/app/util/video_decode_worker.py b/invokeai/app/util/video_decode_worker.py
new file mode 100644
index 00000000000..887e81c43ac
--- /dev/null
+++ b/invokeai/app/util/video_decode_worker.py
@@ -0,0 +1,183 @@
+"""Standalone decode worker for untrusted video files.
+
+This script is executed as a short-lived child process by
+``invokeai.app.util.video_thumbnails`` — never imported by the server at runtime — so
+that a decoder hang on a crafted or malformed container can be bounded by a timeout and
+killed. cv2 wheels have historically hung on certain codec/container combinations, and
+a hung *thread* cannot be killed from Python, so process isolation is the only reliable
+bound. It is run by file path (not ``-m``) and deliberately imports only the stdlib plus
+imageio/PIL/cv2, so it starts quickly without pulling in the invokeai package or torch.
+
+Protocol: ``python video_decode_worker.py ``. On success a single
+JSON object is written to stdout and the exit code is 0; on any failure the exit code is
+non-zero with a message on stderr.
+
+Commands:
+ probe -> {"width", "height", "duration", "fps"}
+ frame -> {"ok": true}; the frame is written to out_path as PNG
+ count -> {"count": }
+ stream -> consecutive numpy arrays on stdout
+"""
+
+import io
+import json
+import math
+import struct
+import sys
+from pathlib import Path
+from typing import Optional
+
+import imageio.v3 as iio
+import numpy as np
+from PIL import Image
+
+
+def _extract_frame(video_path: Path, frame_index: int) -> Optional[Image.Image]:
+ """Extracts a single frame from a video file as a PIL Image. Returns None on failure.
+
+ Tries imageio's FFMPEG plugin first since it's the same encoder we use for output,
+ then falls back to cv2 — uploaded videos with unusual codecs may need that path.
+ """
+ try:
+ # iio.imread with index=N seeks to that frame directly. Returns RGB HxWxC uint8.
+ frame = iio.imread(video_path, plugin="FFMPEG", index=frame_index)
+ return Image.fromarray(frame)
+ except Exception:
+ pass
+
+ try:
+ import cv2 # local import so the imageio-only path doesn't pay the cv2 import cost
+
+ capture = cv2.VideoCapture(str(video_path))
+ if not capture.isOpened():
+ capture.release()
+ return None
+ try:
+ if frame_index > 0:
+ capture.set(cv2.CAP_PROP_POS_FRAMES, frame_index)
+ ok, frame_bgr = capture.read()
+ if not ok or frame_bgr is None:
+ return None
+ frame_rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
+ return Image.fromarray(frame_rgb)
+ finally:
+ capture.release()
+ except Exception:
+ return None
+
+
+def _probe(video_path: Path) -> tuple[int, int, float, Optional[float]]:
+ """Returns (width, height, duration_seconds, fps_or_none) for a video file.
+
+ Tries imageio's FFMPEG plugin first; falls back to cv2.VideoCapture. Raises if
+ neither backend can read the file.
+ """
+ try:
+ meta = iio.immeta(video_path, plugin="FFMPEG")
+ fps_raw = meta.get("fps")
+ duration = float(meta.get("duration", 0.0)) if meta.get("duration") is not None else 0.0
+ size = meta.get("size")
+ if size is None:
+ # Fall through to cv2 — imageio didn't give us dimensions.
+ raise ValueError("imageio probe missing 'size'")
+ width, height = int(size[0]), int(size[1])
+ fps: Optional[float] = float(fps_raw) if fps_raw and fps_raw > 0 else None
+ return width, height, duration, fps
+ except Exception:
+ pass
+
+ import cv2
+
+ capture = cv2.VideoCapture(str(video_path))
+ if not capture.isOpened():
+ capture.release()
+ raise FileNotFoundError(f"Unable to open video at {video_path}")
+ try:
+ width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
+ height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
+ frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
+ fps_raw = capture.get(cv2.CAP_PROP_FPS)
+ fps_v2: Optional[float] = float(fps_raw) if fps_raw and fps_raw > 0 else None
+ duration = (frame_count / fps_v2) if (fps_v2 and frame_count > 0) else 0.0
+ finally:
+ capture.release()
+ return width, height, duration, fps_v2
+
+
+def _count(video_path: Path) -> Optional[int]:
+ """Return the exact decoded frame count, or None if neither backend can determine it.
+
+ Tries imageio's improps first (works for a handful of codecs that expose nframes in
+ container metadata). For libx264 streams imageio reports ``inf``, so we fall through
+ to cv2's ``CAP_PROP_FRAME_COUNT`` which reads the actual packet count. Both sources
+ are preferred over a ``duration * fps`` estimate, which can overshoot by one on VFR
+ uploads or containers with imprecise metadata.
+ """
+ try:
+ props = iio.improps(video_path, plugin="FFMPEG")
+ except Exception:
+ props = None
+ shape = getattr(props, "shape", None) if props is not None else None
+ if shape:
+ n = shape[0]
+ if not (isinstance(n, float) and not math.isfinite(n)):
+ try:
+ return int(n)
+ except (TypeError, ValueError, OverflowError):
+ pass
+
+ try:
+ import cv2
+
+ capture = cv2.VideoCapture(str(video_path))
+ if not capture.isOpened():
+ capture.release()
+ return None
+ try:
+ count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
+ finally:
+ capture.release()
+ return count if count > 0 else None
+ except Exception:
+ return None
+
+
+def _stream(video_path: Path) -> None:
+ """Writes decoded frames as length-prefixed, non-pickled numpy records."""
+ for frame in iio.imiter(video_path, plugin="FFMPEG"):
+ record = io.BytesIO()
+ np.save(record, np.ascontiguousarray(frame), allow_pickle=False)
+ payload = record.getvalue()
+ sys.stdout.buffer.write(struct.pack(">Q", len(payload)))
+ sys.stdout.buffer.write(payload)
+ sys.stdout.buffer.flush()
+
+
+def main(argv: list[str]) -> int:
+ try:
+ command = argv[1]
+ if command == "stream":
+ _stream(Path(argv[2]))
+ elif command == "probe":
+ width, height, duration, fps = _probe(Path(argv[2]))
+ print(json.dumps({"width": width, "height": height, "duration": duration, "fps": fps}))
+ elif command == "frame":
+ image = _extract_frame(Path(argv[2]), int(argv[3]))
+ if image is None:
+ print("no frame decoded", file=sys.stderr)
+ return 1
+ image.save(argv[4], format="PNG")
+ print(json.dumps({"ok": True}))
+ elif command == "count":
+ print(json.dumps({"count": _count(Path(argv[2]))}))
+ else:
+ print(f"unknown command {command!r}", file=sys.stderr)
+ return 1
+ except Exception as e:
+ print(str(e) or repr(e), file=sys.stderr)
+ return 1
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/invokeai/app/util/video_thumbnails.py b/invokeai/app/util/video_thumbnails.py
new file mode 100644
index 00000000000..0cd321ac886
--- /dev/null
+++ b/invokeai/app/util/video_thumbnails.py
@@ -0,0 +1,257 @@
+"""Video frame/probe helpers used by the video file store, upload router, and video nodes.
+
+Decoding runs in a short-lived child process (``video_decode_worker.py``) with a hard
+timeout. Files reaching these helpers are user uploads, and both decode backends can
+hang indefinitely on crafted or malformed containers (cv2 wheels historically, ffmpeg in
+degenerate cases). An in-process hang would tie up the FastAPI request worker that
+called us — repeated crafted uploads could exhaust the worker pool — and a hung thread
+cannot be killed from Python, so process isolation is the only reliable bound.
+The parent explicitly terminates the worker and its descendants when the timeout
+expires, so a hostile file costs at most ``timeout`` seconds and cannot leak a stuck
+FFmpeg process.
+"""
+
+import io
+import json
+import os
+import queue
+import signal
+import struct
+import subprocess
+import sys
+import tempfile
+import threading
+import time
+from pathlib import Path
+from typing import Any, Callable, Iterator, Optional
+
+import numpy as np
+import psutil
+from PIL import Image
+
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+
+# Generous — a healthy decode of a single frame or of container metadata takes well
+# under a second even for large files. The timeout exists to bound adversarial or hung
+# decodes, not to police slow ones.
+VIDEO_DECODE_TIMEOUT_SECONDS = 30.0
+MAX_DECODED_FRAME_RECORD_BYTES = 256 * 1024 * 1024
+
+_WORKER_PATH = Path(__file__).parent / "video_decode_worker.py"
+
+
+def get_video_thumbnail_name(video_name: str) -> str:
+ """Given a video file name (e.g. .mp4), returns the matching thumbnail name (e.g. .webp)."""
+ return os.path.splitext(video_name)[0] + ".webp"
+
+
+def _worker_command(*args: str) -> list[str]:
+ """Command line for one decode-worker invocation (patchable in tests).
+
+ The worker is run by file path rather than ``-m`` so the child process doesn't
+ import the invokeai package (and transitively torch) just to decode a frame.
+ """
+ return [sys.executable, str(_WORKER_PATH), *args]
+
+
+def _spawn_worker(*args: str, **kwargs: Any) -> subprocess.Popen[Any]:
+ """Starts a worker in an independently killable process group on POSIX."""
+ if os.name != "nt":
+ kwargs["start_new_session"] = True
+ return subprocess.Popen(_worker_command(*args), **kwargs)
+
+
+def _is_process_running(pid: int) -> bool:
+ """Returns whether a process exists and is not a zombie."""
+ try:
+ process = psutil.Process(pid)
+ return process.is_running() and process.status() != psutil.STATUS_ZOMBIE
+ except psutil.Error:
+ return False
+
+
+def _terminate_process_tree(proc: subprocess.Popen[Any]) -> None:
+ """Kills a worker and every descendant it spawned."""
+ if os.name != "nt":
+ try:
+ os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
+ proc.wait(timeout=5)
+ return
+ except (OSError, subprocess.TimeoutExpired):
+ pass
+ try:
+ parent = psutil.Process(proc.pid)
+ processes = parent.children(recursive=True)
+ processes.append(parent)
+ for process in processes:
+ try:
+ process.kill()
+ except psutil.Error:
+ pass
+ psutil.wait_procs(processes, timeout=5)
+ except psutil.Error:
+ try:
+ proc.kill()
+ except OSError:
+ pass
+
+
+def _run_worker(args: list[str], timeout: float) -> Optional[dict[str, Any]]:
+ """Runs the decode worker; returns its parsed JSON output, or None on failure or timeout."""
+ try:
+ proc = _spawn_worker(*args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ stdout, _ = proc.communicate(timeout=timeout)
+ except subprocess.TimeoutExpired:
+ _terminate_process_tree(proc)
+ proc.communicate()
+ return None
+ except Exception:
+ return None
+ if proc.returncode != 0:
+ return None
+ try:
+ result = json.loads(stdout)
+ except ValueError:
+ return None
+ return result if isinstance(result, dict) else None
+
+
+def iter_video_frames(
+ video_path: Path,
+ timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS,
+ is_canceled: Optional[Callable[[], bool]] = None,
+) -> Iterator[np.ndarray]:
+ """Streams decoded frames from an isolated worker with bounded memory and wait time."""
+ proc = _spawn_worker(
+ "stream",
+ str(video_path),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL,
+ )
+ if proc.stdout is None:
+ _terminate_process_tree(proc)
+ raise RuntimeError("Unable to open video decoder output stream")
+
+ results: queue.Queue[tuple[str, object]] = queue.Queue(maxsize=1)
+ stopped = threading.Event()
+
+ def read_exactly(size: int) -> bytes:
+ chunks: list[bytes] = []
+ remaining = size
+ while remaining > 0:
+ chunk = proc.stdout.read(remaining)
+ if not chunk:
+ if remaining == size:
+ raise EOFError
+ raise OSError("Truncated frame record from video decoder")
+ chunks.append(chunk)
+ remaining -= len(chunk)
+ return b"".join(chunks)
+
+ def put_result(result: tuple[str, object]) -> None:
+ while not stopped.is_set():
+ try:
+ results.put(result, timeout=0.1)
+ return
+ except queue.Full:
+ continue
+
+ def read_frames() -> None:
+ try:
+ while not stopped.is_set():
+ record_size = struct.unpack(">Q", read_exactly(8))[0]
+ if record_size > MAX_DECODED_FRAME_RECORD_BYTES:
+ raise ValueError(f"Decoded frame record exceeds {MAX_DECODED_FRAME_RECORD_BYTES} bytes")
+ payload = read_exactly(record_size)
+ put_result(("frame", np.load(io.BytesIO(payload), allow_pickle=False)))
+ except (EOFError, ValueError, OSError) as error:
+ put_result(("done", error))
+
+ reader = threading.Thread(target=read_frames, name="video-frame-reader", daemon=True)
+ reader.start()
+ deadline = time.monotonic() + timeout
+ try:
+ while True:
+ if is_canceled is not None and is_canceled():
+ raise CanceledException
+ remaining = deadline - time.monotonic()
+ if remaining <= 0:
+ raise TimeoutError(f"Timed out decoding frames from {video_path}")
+ try:
+ kind, value = results.get(timeout=min(0.1, remaining))
+ except queue.Empty:
+ continue
+ if kind == "frame":
+ if not isinstance(value, np.ndarray):
+ raise ValueError(f"Decoder returned an invalid frame for {video_path}")
+ yield value
+ deadline = time.monotonic() + timeout
+ continue
+ return_code = proc.wait(timeout=1)
+ if return_code != 0:
+ raise ValueError(f"Unable to decode video at {video_path}") from value
+ return
+ finally:
+ stopped.set()
+ if proc.poll() is None:
+ _terminate_process_tree(proc)
+ proc.stdout.close()
+ proc.wait()
+ reader.join(timeout=1)
+
+
+def extract_video_frame(
+ video_path: Path, frame_index: int = 0, timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS
+) -> Optional[Image.Image]:
+ """Extracts a single frame from a video file as a PIL Image. Returns None on failure or timeout."""
+ fd, tmp_name = tempfile.mkstemp(prefix="invokeai_frame_", suffix=".png")
+ os.close(fd)
+ try:
+ result = _run_worker(["frame", str(video_path), str(frame_index), tmp_name], timeout)
+ if result is None:
+ return None
+ with Image.open(tmp_name) as image:
+ image.load()
+ return image
+ except Exception:
+ return None
+ finally:
+ Path(tmp_name).unlink(missing_ok=True)
+
+
+def probe_video(
+ video_path: Path, timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS
+) -> tuple[int, int, float, Optional[float]]:
+ """Returns (width, height, duration_seconds, fps_or_none) for a video file.
+
+ Raises FileNotFoundError if the file cannot be read — including when the decode
+ times out, since a file we cannot probe within the bound is treated as unreadable.
+ """
+ result = _run_worker(["probe", str(video_path)], timeout)
+ if result is None:
+ raise FileNotFoundError(f"Unable to open video at {video_path}")
+ try:
+ width = int(result["width"])
+ height = int(result["height"])
+ duration = float(result["duration"])
+ fps_raw = result.get("fps")
+ fps: Optional[float] = float(fps_raw) if fps_raw else None
+ except (KeyError, TypeError, ValueError) as e:
+ raise FileNotFoundError(f"Unable to open video at {video_path}") from e
+ return width, height, duration, fps
+
+
+def decoder_frame_count(video_path: Path, timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS) -> Optional[int]:
+ """Returns the exact decoded frame count, or None if it cannot be determined in time.
+
+ Preferred over a ``duration * fps`` estimate, which can overshoot by one on VFR
+ uploads or containers with imprecise metadata; callers fall back to that estimate
+ when this returns None.
+ """
+ result = _run_worker(["count", str(video_path)], timeout)
+ if result is None:
+ return None
+ count = result.get("count")
+ if isinstance(count, bool) or not isinstance(count, (int, float)):
+ return None
+ return int(count) if count > 0 else None
diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py
index 31f3b14619d..b0b7b0dd461 100644
--- a/invokeai/backend/model_manager/configs/factory.py
+++ b/invokeai/backend/model_manager/configs/factory.py
@@ -55,6 +55,7 @@
LoRA_LyCORIS_SD1_Config,
LoRA_LyCORIS_SD2_Config,
LoRA_LyCORIS_SDXL_Config,
+ LoRA_LyCORIS_Wan_Config,
LoRA_LyCORIS_ZImage_Config,
LoRA_OMI_FLUX_Config,
LoRA_OMI_SDXL_Config,
@@ -80,10 +81,12 @@
Main_Diffusers_SD3_Config,
Main_Diffusers_SDXL_Config,
Main_Diffusers_SDXLRefiner_Config,
+ Main_Diffusers_Wan_Config,
Main_Diffusers_ZImage_Config,
Main_GGUF_Flux2_Config,
Main_GGUF_FLUX_Config,
Main_GGUF_QwenImage_Config,
+ Main_GGUF_Wan_Config,
Main_GGUF_ZImage_Config,
MainModelDefaultSettings,
)
@@ -121,10 +124,13 @@
VAE_Checkpoint_SD1_Config,
VAE_Checkpoint_SD2_Config,
VAE_Checkpoint_SDXL_Config,
+ VAE_Checkpoint_Wan_Config,
VAE_Diffusers_Flux2_Config,
VAE_Diffusers_SD1_Config,
VAE_Diffusers_SDXL_Config,
+ VAE_Diffusers_Wan_Config,
)
+from invokeai.backend.model_manager.configs.wan_t5_encoder import WanT5Encoder_WanT5Encoder_Config
from invokeai.backend.model_manager.model_on_disk import ModelOnDisk
from invokeai.backend.model_manager.taxonomy import (
BaseModelType,
@@ -175,6 +181,7 @@
Annotated[Main_Diffusers_Flux2_Config, Main_Diffusers_Flux2_Config.get_tag()],
Annotated[Main_Diffusers_CogView4_Config, Main_Diffusers_CogView4_Config.get_tag()],
Annotated[Main_Diffusers_QwenImage_Config, Main_Diffusers_QwenImage_Config.get_tag()],
+ Annotated[Main_Diffusers_Wan_Config, Main_Diffusers_Wan_Config.get_tag()],
Annotated[Main_Diffusers_ZImage_Config, Main_Diffusers_ZImage_Config.get_tag()],
# Main (Pipeline) - checkpoint format
# IMPORTANT: FLUX.2 must be checked BEFORE FLUX.1 because FLUX.2 has specific validation
@@ -195,6 +202,7 @@
Annotated[Main_GGUF_Flux2_Config, Main_GGUF_Flux2_Config.get_tag()],
Annotated[Main_GGUF_FLUX_Config, Main_GGUF_FLUX_Config.get_tag()],
Annotated[Main_GGUF_QwenImage_Config, Main_GGUF_QwenImage_Config.get_tag()],
+ Annotated[Main_GGUF_Wan_Config, Main_GGUF_Wan_Config.get_tag()],
Annotated[Main_GGUF_ZImage_Config, Main_GGUF_ZImage_Config.get_tag()],
# VAE - checkpoint format
Annotated[VAE_Checkpoint_SD1_Config, VAE_Checkpoint_SD1_Config.get_tag()],
@@ -202,12 +210,18 @@
Annotated[VAE_Checkpoint_SDXL_Config, VAE_Checkpoint_SDXL_Config.get_tag()],
Annotated[VAE_Checkpoint_FLUX_Config, VAE_Checkpoint_FLUX_Config.get_tag()],
Annotated[VAE_Checkpoint_Flux2_Config, VAE_Checkpoint_Flux2_Config.get_tag()],
+ # IMPORTANT: VAE_Checkpoint_Wan_Config must be checked BEFORE QwenImage —
+ # both share the AutoencoderKLWan architecture and the Wan config relies
+ # on a filename heuristic to claim 16-channel files; ordering here lets
+ # Wan win when the filename suggests it.
+ Annotated[VAE_Checkpoint_Wan_Config, VAE_Checkpoint_Wan_Config.get_tag()],
Annotated[VAE_Checkpoint_QwenImage_Config, VAE_Checkpoint_QwenImage_Config.get_tag()],
Annotated[VAE_Checkpoint_Anima_Config, VAE_Checkpoint_Anima_Config.get_tag()],
# VAE - diffusers format
Annotated[VAE_Diffusers_SD1_Config, VAE_Diffusers_SD1_Config.get_tag()],
Annotated[VAE_Diffusers_SDXL_Config, VAE_Diffusers_SDXL_Config.get_tag()],
Annotated[VAE_Diffusers_Flux2_Config, VAE_Diffusers_Flux2_Config.get_tag()],
+ Annotated[VAE_Diffusers_Wan_Config, VAE_Diffusers_Wan_Config.get_tag()],
# ControlNet - checkpoint format
Annotated[ControlNet_Checkpoint_SD1_Config, ControlNet_Checkpoint_SD1_Config.get_tag()],
Annotated[ControlNet_Checkpoint_SD2_Config, ControlNet_Checkpoint_SD2_Config.get_tag()],
@@ -230,6 +244,13 @@
Annotated[LoRA_LyCORIS_FLUX_Config, LoRA_LyCORIS_FLUX_Config.get_tag()],
Annotated[LoRA_LyCORIS_ZImage_Config, LoRA_LyCORIS_ZImage_Config.get_tag()],
Annotated[LoRA_LyCORIS_QwenImage_Config, LoRA_LyCORIS_QwenImage_Config.get_tag()],
+ # Wan and Anima both target ``blocks.X`` shapes; their LoRA probes are
+ # mutually exclusive — Wan rejects Anima's ``_proj``/``mlp``/
+ # ``adaln_modulation`` markers, Anima requires at least one of those
+ # markers (see ``has_cosmos_dit_*_keys_strict``). Order between these
+ # two doesn't affect correctness; mutual exclusivity is locked in by
+ # ``test_wan_lora_probe_independence.py``.
+ Annotated[LoRA_LyCORIS_Wan_Config, LoRA_LyCORIS_Wan_Config.get_tag()],
Annotated[LoRA_LyCORIS_Anima_Config, LoRA_LyCORIS_Anima_Config.get_tag()],
# LoRA - OMI format
Annotated[LoRA_OMI_SDXL_Config, LoRA_OMI_SDXL_Config.get_tag()],
@@ -255,6 +276,8 @@
# Qwen VL Encoder (Qwen2.5-VL multimodal encoder for Qwen Image)
Annotated[QwenVLEncoder_Diffusers_Config, QwenVLEncoder_Diffusers_Config.get_tag()],
Annotated[QwenVLEncoder_Checkpoint_Config, QwenVLEncoder_Checkpoint_Config.get_tag()],
+ # Wan T5 Encoder (UMT5-XXL for Wan 2.2)
+ Annotated[WanT5Encoder_WanT5Encoder_Config, WanT5Encoder_WanT5Encoder_Config.get_tag()],
# TI - file format
Annotated[TI_File_SD1_Config, TI_File_SD1_Config.get_tag()],
Annotated[TI_File_SD2_Config, TI_File_SD2_Config.get_tag()],
diff --git a/invokeai/backend/model_manager/configs/lora.py b/invokeai/backend/model_manager/configs/lora.py
index fdaebe38565..9479dea8cdb 100644
--- a/invokeai/backend/model_manager/configs/lora.py
+++ b/invokeai/backend/model_manager/configs/lora.py
@@ -28,14 +28,23 @@
FluxLoRAFormat,
ModelFormat,
ModelType,
+ WanLoRAVariantType,
ZImageVariantType,
)
from invokeai.backend.model_manager.util.model_util import lora_token_vector_length
from invokeai.backend.patches.lora_conversions.anima_lora_constants import (
has_cosmos_dit_kohya_keys,
+ has_cosmos_dit_kohya_keys_strict,
has_cosmos_dit_peft_keys,
+ has_cosmos_dit_peft_keys_strict,
)
from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import is_state_dict_likely_flux_control
+from invokeai.backend.patches.lora_conversions.wan_lora_constants import (
+ detect_wan_lora_variant,
+ has_non_wan_architecture_keys,
+ has_wan_kohya_keys,
+ has_wan_peft_keys,
+)
# Defaults used to compute the effective slider range when one or both bounds
# are unset. These intentionally mirror the frontend's DEFAULT_LORA_WEIGHT_CONFIG
@@ -909,16 +918,20 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None:
Anima LoRAs have keys like:
- lora_unet_blocks_0_cross_attn_k_proj.lora_down.weight (Kohya format)
- diffusion_model.blocks.0.cross_attn.k_proj.lora_A.weight (diffusers PEFT format)
- - transformer.blocks.0.cross_attn.k_proj.lora_A.weight (diffusers PEFT format)
-
- Detection requires Cosmos DiT-specific subcomponent names (cross_attn,
- self_attn, mlp, adaln_modulation) to avoid false-positives on other
- architectures that also use ``blocks`` in their paths.
+ - transformer.blocks.0.mlp.layer_0.lora_A.weight (Anima-only MLP layer)
+
+ Uses the **strict** Cosmos-DiT detectors, which require an
+ Anima-exclusive subcomponent name (``mlp``, ``adaln_modulation``, or
+ ``_proj``-suffixed attention). The loose detectors would also accept
+ Wan-native LoRAs (which use ``cross_attn``/``self_attn`` too but with
+ bare ``.q``/``.k``/``.v``/``.o`` rather than ``_proj``), so they're not
+ safe for first-match-wins probing — see the regression tests in
+ ``test_wan_lora_probe_independence.py``.
"""
state_dict = mod.load_state_dict()
str_keys = [k for k in state_dict.keys() if isinstance(k, str)]
- has_cosmos_keys = has_cosmos_dit_kohya_keys(str_keys) or has_cosmos_dit_peft_keys(str_keys)
+ has_cosmos_keys = has_cosmos_dit_kohya_keys_strict(str_keys) or has_cosmos_dit_peft_keys_strict(str_keys)
# Also check for LoRA/LoKR weight suffixes
has_lora_suffix = state_dict_has_any_keys_ending_with(
@@ -941,19 +954,112 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None:
@classmethod
def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType:
- """Anima LoRAs target Cosmos DiT blocks (blocks.X.cross_attn, blocks.X.self_attn, etc.).
+ """Anima LoRAs target Cosmos DiT blocks (blocks.X.mlp, blocks.X.adaln_modulation,
+ blocks.X.cross_attn.q_proj, etc.).
- Uses Cosmos DiT-specific subcomponent names to avoid false-positives.
+ Uses the strict Cosmos-DiT detectors to be mutually exclusive with
+ Wan-LoRA detection — see ``_validate_looks_like_lora`` for rationale.
"""
state_dict = mod.load_state_dict()
str_keys = [k for k in state_dict.keys() if isinstance(k, str)]
- if has_cosmos_dit_kohya_keys(str_keys) or has_cosmos_dit_peft_keys(str_keys):
+ if has_cosmos_dit_kohya_keys_strict(str_keys) or has_cosmos_dit_peft_keys_strict(str_keys):
return BaseModelType.Anima
raise NotAMatchError("model does not look like an Anima LoRA")
+class LoRA_LyCORIS_Wan_Config(LoRA_LyCORIS_Config_Base, Config_Base):
+ """Model config for Wan 2.2 LoRA models in LyCORIS format.
+
+ Wan LoRAs target ``WanTransformer3DModel`` blocks. The Wan 2.2 A14B family
+ is dual-expert (high-noise + low-noise) — LoRAs are typically trained
+ against one expert. ``expert`` records which one so the model loader
+ invocation can wire it to the correct ``loras`` / ``loras_low_noise`` list.
+ Many LoRAs are expert-agnostic (TI2V-5B family, or community LoRAs that
+ just don't tag the expert) — these get ``expert=None`` and are applied to
+ both experts by default.
+ """
+
+ base: Literal[BaseModelType.Wan] = Field(default=BaseModelType.Wan)
+ expert: Literal["high", "low"] | None = Field(
+ default=None,
+ description="For Wan 2.2 A14B dual-expert LoRAs: 'high' targets the high-noise expert, "
+ "'low' targets the low-noise expert. None means the LoRA is expert-agnostic "
+ "(TI2V-5B, or community LoRAs without explicit tagging) and is applied to both.",
+ )
+ variant: WanLoRAVariantType | None = Field(
+ default=None,
+ description="The Wan model family this LoRA targets, detected from its inner-dim "
+ "(5120 -> A14B, 3072 -> TI2V-5B). A14B LoRAs are incompatible with TI2V-5B mains "
+ "(and vice versa) — they crash with a shape mismatch in the layer patcher. The "
+ "linear-view graph builder filters LoRAs on variant when building the LoRA "
+ "collection. None means the LoRA's inner-dim couldn't be identified.",
+ )
+
+ @classmethod
+ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None:
+ """Wan LoRAs target attn1/attn2/ffn.net (diffusers form) or self_attn/cross_attn/ffn.N (native form)."""
+ state_dict = mod.load_state_dict()
+ str_keys = [k for k in state_dict.keys() if isinstance(k, str)]
+
+ has_wan_keys = has_wan_kohya_keys(str_keys) or has_wan_peft_keys(str_keys)
+ has_lora_suffix = state_dict_has_any_keys_ending_with(
+ state_dict,
+ {
+ "lora_A.weight",
+ "lora_B.weight",
+ "lora_down.weight",
+ "lora_up.weight",
+ "dora_scale",
+ ".lokr_w1",
+ ".lokr_w2",
+ },
+ )
+
+ # Reject if any non-Wan architecture signature is present. Without this
+ # guard a Wan LoRA could be falsely identified by Anima (cross_attn /
+ # self_attn name collision) or vice versa.
+ if has_wan_keys and has_lora_suffix and not has_non_wan_architecture_keys(str_keys):
+ return
+
+ raise NotAMatchError("model does not match Wan LoRA heuristics")
+
+ @classmethod
+ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType:
+ state_dict = mod.load_state_dict()
+ str_keys = [k for k in state_dict.keys() if isinstance(k, str)]
+
+ if (has_wan_kohya_keys(str_keys) or has_wan_peft_keys(str_keys)) and not has_non_wan_architecture_keys(
+ str_keys
+ ):
+ return BaseModelType.Wan
+
+ raise NotAMatchError("model does not look like a Wan LoRA")
+
+ @classmethod
+ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:
+ # Run the base-class probe (file-check, lora-suffix, base detection).
+ instance = super().from_model_on_disk(mod, override_fields)
+
+ # Auto-detect the expert tag from the filename if the user didn't
+ # override it. ``high_noise`` / ``low_noise`` / hyphenated / concatenated
+ # variants — mirrors the GGUF transformer probe's heuristic.
+ if instance.expert is None:
+ name = mod.path.stem.lower()
+ if any(s in name for s in ("high_noise", "high-noise", "highnoise")):
+ instance.expert = "high"
+ elif any(s in name for s in ("low_noise", "low-noise", "lownoise")):
+ instance.expert = "low"
+
+ # Auto-detect the model-family variant from inner_dim in the state
+ # dict. The override field skips this if the user has set it.
+ if instance.variant is None:
+ instance.variant = detect_wan_lora_variant(mod.load_state_dict())
+
+ return instance
+
+
class ControlAdapter_Config_Base(ABC, BaseModel):
default_settings: ControlAdapterDefaultSettings | None = Field(None)
diff --git a/invokeai/backend/model_manager/configs/main.py b/invokeai/backend/model_manager/configs/main.py
index 10835b389fc..b6e37a764bb 100644
--- a/invokeai/backend/model_manager/configs/main.py
+++ b/invokeai/backend/model_manager/configs/main.py
@@ -33,6 +33,7 @@
QwenImageVariantType,
SchedulerPredictionType,
SubModelType,
+ WanVariantType,
ZImageVariantType,
)
from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor
@@ -65,7 +66,12 @@ class MainModelDefaultSettings(BaseModel):
def from_base(
cls,
base: BaseModelType,
- variant: Flux2VariantType | FluxVariantType | ModelVariantType | ZImageVariantType | None = None,
+ variant: Flux2VariantType
+ | FluxVariantType
+ | ModelVariantType
+ | WanVariantType
+ | ZImageVariantType
+ | None = None,
) -> Self | None:
match base:
case BaseModelType.StableDiffusion1:
@@ -95,6 +101,12 @@ def from_base(
return cls(steps=4, cfg_scale=1.0, width=1024, height=1024)
case BaseModelType.QwenImage:
return cls(steps=40, cfg_scale=4.0, width=1024, height=1024)
+ case BaseModelType.Wan:
+ # Wan 2.2 recommended defaults differ by variant.
+ if variant == WanVariantType.TI2V_5B:
+ return cls(steps=30, cfg_scale=5.0, width=1024, height=1024)
+ # Default to A14B settings (also used when variant is unknown).
+ return cls(steps=40, cfg_scale=4.0, width=1024, height=1024)
case _:
# TODO(psyche): Do we want defaults for other base types?
return None
@@ -1439,6 +1451,269 @@ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -
return cls(**override_fields, variant=explicit_variant)
+def _has_wan_keys(state_dict: dict[str | int, Any]) -> bool:
+ """Check if state dict contains Wan 2.2 transformer keys.
+
+ Two layouts are accepted:
+
+ * **Diffusers** (city96-style GGUF, Wan-AI/*-Diffusers safetensors): the text
+ projection is named ``condition_embedder.text_embedder.linear_1``.
+ * **Native upstream** (QuantStack-style GGUF, ComfyUI, Wan-AI's non-Diffusers
+ releases): the text projection is named ``text_embedding.0``.
+
+ Both layouts share ``patch_embedding.weight`` as the input conv. Combined with
+ the text-projection fingerprint, this won't collide with FLUX
+ (``double_blocks/single_blocks``), Qwen Image (``txt_in/img_in``), Z-Image
+ (``cap_embedder``), or Anima (``llm_adapter``).
+
+ Tolerates both bare keys and the ComfyUI ``model.diffusion_model.`` /
+ ``diffusion_model.`` prefixes.
+ """
+ text_proj_options = (
+ "condition_embedder.text_embedder.linear_1.weight",
+ "text_embedding.0.weight",
+ )
+ prefixes = ("", "model.diffusion_model.", "diffusion_model.")
+ keys = state_dict.keys()
+ if not any((p + "patch_embedding.weight") in keys for p in prefixes):
+ return False
+ return any((p + needle) in keys for p in prefixes for needle in text_proj_options)
+
+
+def _is_native_wan_layout(state_dict: dict[str | int, Any]) -> bool:
+ """True if the state dict uses the native upstream Wan key layout.
+
+ Native layout uses ``text_embedding.0/2``, ``self_attn``/``cross_attn``,
+ ``ffn.0/2``, ``head.head``, ``head.modulation``, etc. — what ComfyUI and
+ QuantStack ship. Diffusers layout uses ``condition_embedder.*``, ``attn1``/
+ ``attn2``, ``ffn.net.*``, ``proj_out``, ``scale_shift_table``.
+ """
+ prefixes = ("", "model.diffusion_model.", "diffusion_model.")
+ keys = state_dict.keys()
+ return any((p + "text_embedding.0.weight") in keys for p in prefixes)
+
+
+def _detect_wan_gguf_variant(state_dict: dict[str | int, Any]) -> WanVariantType | None:
+ """Determine A14B (T2V vs I2V) vs TI2V-5B from the GGUF state dict.
+
+ ``patch_embedding.weight`` has shape ``[inner_dim, in_channels, T, H, W]``;
+ ``in_channels`` uniquely identifies the Wan 2.2 variant:
+
+ - 16 → T2V-A14B (noise latents only).
+ - 36 → I2V-A14B (16 noise + 16 ref-image latents + 4 first-frame mask,
+ concatenated along the channel dim — see diffusers
+ ``WanImageToVideoPipeline.prepare_latents``).
+ - 48 → TI2V-5B (Wan2.2-VAE z_dim=48).
+
+ Returns None if the tensor is missing or the channel count is unrecognised.
+ """
+ candidates = (
+ "patch_embedding.weight",
+ "model.diffusion_model.patch_embedding.weight",
+ "diffusion_model.patch_embedding.weight",
+ )
+ for key in candidates:
+ if key in state_dict:
+ tensor = state_dict[key]
+ shape = getattr(tensor, "tensor_shape", None) or getattr(tensor, "shape", None)
+ if shape is None or len(shape) < 2:
+ return None
+ in_channels = int(shape[1])
+ if in_channels == 16:
+ return WanVariantType.T2V_A14B
+ if in_channels == 36:
+ return WanVariantType.I2V_A14B
+ if in_channels == 48:
+ return WanVariantType.TI2V_5B
+ return None
+ return None
+
+
+def _detect_wan_gguf_expert(filename: str) -> Literal["high", "low", "none"]:
+ """Filename heuristic for the A14B dual-expert MoE.
+
+ Community releases tag each expert in the filename — typically
+ ``high_noise`` / ``low_noise`` (or hyphenated/concatenated variants).
+ Returns 'none' when neither marker is present (single-expert model or
+ ambiguous filename).
+ """
+ name = filename.lower()
+ if any(s in name for s in ("high_noise", "high-noise", "highnoise")):
+ return "high"
+ if any(s in name for s in ("low_noise", "low-noise", "lownoise")):
+ return "low"
+ return "none"
+
+
+class Main_GGUF_Wan_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base):
+ """Model config for GGUF-quantized Wan 2.2 transformer models.
+
+ A14B's MoE ships as two GGUF files (one per expert); ``expert`` records
+ which one this is so the model loader invocation can pair them. TI2V-5B
+ is a single-transformer model and stores ``expert='none'``.
+ """
+
+ base: Literal[BaseModelType.Wan] = Field(default=BaseModelType.Wan)
+ format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized)
+ variant: WanVariantType = Field()
+ expert: Literal["high", "low", "none"] = Field(
+ default="none",
+ description="For Wan 2.2 A14B's dual-expert MoE: 'high' for the high-noise expert, "
+ "'low' for the low-noise expert. 'none' for single-transformer models (TI2V-5B).",
+ )
+
+ @classmethod
+ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:
+ raise_if_not_file(mod)
+ raise_for_override_fields(cls, override_fields)
+
+ sd = mod.load_state_dict()
+
+ if not _has_ggml_tensors(sd):
+ raise NotAMatchError("state dict does not look like GGUF quantized")
+ if not _has_wan_keys(sd):
+ raise NotAMatchError("state dict does not look like a Wan transformer")
+
+ explicit_variant = override_fields.pop("variant", None)
+ variant = explicit_variant or _detect_wan_gguf_variant(sd)
+ if variant is None:
+ raise NotAMatchError("could not determine Wan variant from state dict")
+
+ explicit_expert = override_fields.pop("expert", None)
+ expert = explicit_expert or _detect_wan_gguf_expert(mod.path.stem)
+
+ return cls(**override_fields, variant=variant, expert=expert)
+
+
+class Main_Diffusers_Wan_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base):
+ """Model config for Wan 2.2 diffusers models.
+
+ Covers both the dual-expert T2V-A14B family and the single-transformer TI2V-5B
+ family. Variant is detected from the on-disk transformer config (latent channel
+ count) plus the presence of a sibling ``transformer_2/`` directory.
+ """
+
+ base: Literal[BaseModelType.Wan] = Field(default=BaseModelType.Wan)
+ variant: WanVariantType = Field()
+ has_dual_expert: bool = Field(
+ default=False,
+ description="Whether this model ships two transformer experts (Wan 2.2 A14B MoE). False for TI2V-5B.",
+ )
+ boundary_ratio: float | None = Field(
+ default=None,
+ description="MoE expert switch point as a fraction of num_train_timesteps (typically 1000). "
+ "None for single-transformer models. Read from model_index.json by Diffusers' WanPipeline.",
+ )
+
+ @classmethod
+ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:
+ raise_if_not_dir(mod)
+
+ raise_for_override_fields(cls, override_fields)
+
+ # Wan repos ship with WanPipeline (T2V) or WanImageToVideoPipeline (I2V/TI2V).
+ # Either class name is sufficient to identify a Wan diffusers model.
+ raise_for_class_name(
+ common_config_paths(mod.path),
+ {
+ "WanPipeline",
+ "WanImageToVideoPipeline",
+ },
+ )
+
+ repo_variant = override_fields.pop("repo_variant", None) or cls._get_repo_variant_or_raise(mod)
+
+ explicit_variant = override_fields.pop("variant", None)
+ has_dual_expert = (mod.path / "transformer_2" / "config.json").exists()
+ variant = explicit_variant or cls._detect_wan_variant(mod, has_dual_expert)
+ boundary_ratio = override_fields.pop("boundary_ratio", None)
+ if boundary_ratio is None:
+ boundary_ratio = cls._read_boundary_ratio(mod)
+
+ return cls(
+ **override_fields,
+ repo_variant=repo_variant,
+ variant=variant,
+ has_dual_expert=has_dual_expert,
+ boundary_ratio=boundary_ratio,
+ )
+
+ @classmethod
+ def _read_boundary_ratio(cls, mod: ModelOnDisk) -> float | None:
+ """Pull ``boundary_ratio`` from ``model_index.json`` if present.
+
+ Diffusers' ``WanPipeline.__init__`` registers it via ``register_to_config``,
+ which persists it as a top-level key in the saved pipeline config.
+ """
+ try:
+ model_index = get_config_dict_or_raise(mod.path / "model_index.json")
+ except NotAMatchError:
+ return None
+ value = model_index.get("boundary_ratio")
+ if value is None:
+ return None
+ try:
+ return float(value)
+ except (TypeError, ValueError):
+ return None
+
+ @classmethod
+ def _detect_wan_variant(cls, mod: ModelOnDisk, has_dual_expert: bool) -> WanVariantType:
+ """Detect Wan variant from transformer + VAE config.
+
+ - T2V-A14B: dual transformer experts, standard Wan VAE (z_dim=16),
+ transformer ``in_channels=16`` (text-only conditioning).
+ - I2V-A14B: dual transformer experts, standard Wan VAE,
+ transformer ``in_channels=36`` (text + VAE-encoded reference image
+ + first-frame mask concatenated along the channel dim).
+ - TI2V-5B: single transformer, Wan2.2-VAE (z_dim=48).
+ """
+ if has_dual_expert:
+ # Disambiguate T2V vs I2V via the transformer's input channel count.
+ # Wan 2.2 I2V uses VAE-latent concatenation: 16 noise + 16 ref-image
+ # latents + 4 first-frame mask = 36. (Wan 2.1 I2V used CLIP-vision
+ # via ``image_dim``; that mechanism is absent in Wan 2.2.)
+ in_channels = cls._transformer_in_channels(mod)
+ if in_channels == 36:
+ return WanVariantType.I2V_A14B
+ return WanVariantType.T2V_A14B
+
+ # Single-transformer model: distinguish TI2V-5B from any future single-expert
+ # A14B-derived release by inspecting the VAE latent dimension.
+ try:
+ vae_config = get_config_dict_or_raise(mod.path / "vae" / "config.json")
+ z_dim = vae_config.get("z_dim")
+ if z_dim is not None and int(z_dim) >= 32:
+ return WanVariantType.TI2V_5B
+ except NotAMatchError:
+ # No VAE config to inspect — fall through to the heuristic path below.
+ pass
+
+ # Filename / repo-name heuristic as a last resort.
+ name = mod.path.name.lower()
+ if "5b" in name or "ti2v" in name:
+ return WanVariantType.TI2V_5B
+ return WanVariantType.T2V_A14B
+
+ @staticmethod
+ def _transformer_in_channels(mod: ModelOnDisk) -> int | None:
+ """Read ``in_channels`` from ``transformer/config.json``.
+
+ For Wan 2.2 A14B, this is the canonical discriminator between T2V
+ (``in_channels=16``) and I2V (``in_channels=36``). Returns None if the
+ config can't be read.
+ """
+ try:
+ transformer_config = get_config_dict_or_raise(mod.path / "transformer" / "config.json")
+ except NotAMatchError:
+ return None
+ value = transformer_config.get("in_channels")
+ try:
+ return int(value) if value is not None else None
+ except (TypeError, ValueError):
+ return None
+
+
class Main_Checkpoint_Anima_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base):
"""Model config for Anima single-file checkpoint models (safetensors).
diff --git a/invokeai/backend/model_manager/configs/vae.py b/invokeai/backend/model_manager/configs/vae.py
index 5a88cf12781..00b96c3c1ac 100644
--- a/invokeai/backend/model_manager/configs/vae.py
+++ b/invokeai/backend/model_manager/configs/vae.py
@@ -40,6 +40,11 @@ def _is_qwen_image_vae(state_dict: dict[str | int, Any]) -> bool:
1. Diffusers-format encoder/decoder keys (`encoder.conv_in`, `decoder.conv_in`)
2. 5-dimensional convolution weights (3D causal convolutions vs. standard 2D conv in SD/SDXL/FLUX VAEs)
3. 16-dimensional latent space (z_dim=16)
+
+ Note: Wan 2.2 A14B reuses the same architecture (AutoencoderKLWan with z_dim=16),
+ so this function returns True for both. Disambiguation between the two for
+ standalone files relies on the filename heuristic in :func:`_is_wan_vae` and
+ config registration order.
"""
decoder_conv_in_key = "decoder.conv_in.weight"
if decoder_conv_in_key not in state_dict:
@@ -52,6 +57,34 @@ def _is_qwen_image_vae(state_dict: dict[str | int, Any]) -> bool:
return shape[1] == 16
+def _wan_vae_z_dim(state_dict: dict[str | int, Any]) -> int | None:
+ """Return ``z_dim`` for a Wan-family VAE state dict, or ``None`` if it isn't one.
+
+ Wan-family VAEs (AutoencoderKLWan) have 5D convolution weights and a
+ decoder.conv_in input channel count of 16 (Wan 2.1 / A14B / Qwen Image) or
+ 48 (Wan 2.2 TI2V-5B's Wan2.2-VAE).
+ """
+ decoder_conv_in_key = "decoder.conv_in.weight"
+ if decoder_conv_in_key not in state_dict:
+ return None
+ weight = state_dict[decoder_conv_in_key]
+ shape = getattr(weight, "shape", None)
+ if shape is None or len(shape) != 5:
+ return None
+ z = int(shape[1])
+ return z if z in (16, 48) else None
+
+
+def _filename_suggests_wan(mod: ModelOnDisk) -> bool:
+ """Filename heuristic to distinguish standalone Wan VAE files from Qwen Image VAEs.
+
+ Both use the same ``AutoencoderKLWan`` architecture for 16-channel files, so the
+ state dict alone can't tell them apart. Filenames in the wild (community ports,
+ ComfyUI repacks) typically include ``wan`` for Wan releases.
+ """
+ return "wan" in mod.path.name.lower()
+
+
def _is_flux2_vae(state_dict: dict[str | int, Any]) -> bool:
"""Check if state dict is a FLUX.2 VAE (AutoencoderKLFlux2).
@@ -113,9 +146,10 @@ def _validate_looks_like_vae(cls, mod: ModelOnDisk) -> None:
if _is_flux2_vae(state_dict):
raise NotAMatchError("model is a FLUX.2 VAE, not a standard VAE")
- # Exclude Qwen Image VAEs - they have their own config class
- if _is_qwen_image_vae(state_dict):
- raise NotAMatchError("model is a Qwen Image VAE, not a standard VAE")
+ # Exclude Qwen Image / Wan VAEs - they share the AutoencoderKLWan
+ # architecture and each has its own config class.
+ if _is_qwen_image_vae(state_dict) or _wan_vae_z_dim(state_dict) is not None:
+ raise NotAMatchError("model is a Wan-family VAE, not a standard VAE")
@classmethod
def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType:
@@ -215,9 +249,96 @@ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -
if not _is_qwen_image_vae(state_dict):
raise NotAMatchError("state dict does not look like a Qwen Image VAE")
+ # Defer to VAE_Checkpoint_Wan_Config for files whose names indicate Wan
+ # (both architectures are 16-channel AutoencoderKLWan and otherwise
+ # indistinguishable from the state dict alone).
+ if _filename_suggests_wan(mod):
+ raise NotAMatchError("filename suggests a Wan VAE, not Qwen Image")
+
return cls(**override_fields)
+class VAE_Checkpoint_Wan_Config(Checkpoint_Config_Base, Config_Base):
+ """Model config for Wan 2.2 VAE checkpoint models (AutoencoderKLWan).
+
+ Distinguishes A14B (z_dim=16, standard Wan VAE) from TI2V-5B (z_dim=48,
+ Wan2.2-VAE) via the input channel count of ``decoder.conv_in.weight``.
+ """
+
+ type: Literal[ModelType.VAE] = Field(default=ModelType.VAE)
+ format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint)
+ base: Literal[BaseModelType.Wan] = Field(default=BaseModelType.Wan)
+ latent_channels: Literal[16, 48] = Field(
+ description="VAE latent channel count: 16 for A14B (standard Wan VAE) or 48 for TI2V-5B (Wan2.2-VAE)."
+ )
+
+ @classmethod
+ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:
+ raise_if_not_file(mod)
+
+ raise_for_override_fields(cls, override_fields)
+
+ state_dict = mod.load_state_dict()
+ z_dim = _wan_vae_z_dim(state_dict)
+ if z_dim is None:
+ raise NotAMatchError("state dict does not look like a Wan VAE")
+
+ # 48-channel files are unambiguously Wan2.2-VAE (TI2V-5B). 16-channel
+ # files are architecturally identical to Qwen Image's VAE; require the
+ # filename to suggest Wan to claim them, otherwise let the QwenImage
+ # config win.
+ latent_channels: int = z_dim
+ if latent_channels == 16 and not _filename_suggests_wan(mod):
+ raise NotAMatchError(
+ "16-channel AutoencoderKLWan VAE without 'wan' in filename — deferring to Qwen Image VAE config."
+ )
+
+ explicit = override_fields.pop("latent_channels", None)
+ if explicit is not None:
+ latent_channels = int(explicit)
+
+ return cls(**override_fields, latent_channels=latent_channels)
+
+
+class VAE_Diffusers_Wan_Config(Diffusers_Config_Base, Config_Base):
+ """Model config for Wan 2.2 VAE in diffusers folder layout (AutoencoderKLWan)."""
+
+ type: Literal[ModelType.VAE] = Field(default=ModelType.VAE)
+ format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers)
+ base: Literal[BaseModelType.Wan] = Field(default=BaseModelType.Wan)
+ latent_channels: Literal[16, 48] = Field(
+ default=16,
+ description="VAE latent channel count: 16 for A14B or 48 for TI2V-5B's Wan2.2-VAE.",
+ )
+
+ @classmethod
+ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:
+ raise_if_not_dir(mod)
+
+ raise_for_override_fields(cls, override_fields)
+
+ raise_for_class_name(
+ common_config_paths(mod.path),
+ {"AutoencoderKLWan"},
+ )
+
+ # Read z_dim from the diffusers config to set latent_channels.
+ latent_channels: int = 16
+ try:
+ config = get_config_dict_or_raise(common_config_paths(mod.path))
+ z = config.get("z_dim")
+ if z is not None and int(z) in (16, 48):
+ latent_channels = int(z)
+ except NotAMatchError:
+ pass
+
+ explicit = override_fields.pop("latent_channels", None)
+ if explicit is not None:
+ latent_channels = int(explicit)
+
+ return cls(**override_fields, latent_channels=latent_channels)
+
+
def _has_anima_vae_keys(state_dict: dict[str | int, Any]) -> bool:
"""Check if state dict looks like an Anima QwenImage VAE (AutoencoderKLQwenImage).
diff --git a/invokeai/backend/model_manager/configs/wan_t5_encoder.py b/invokeai/backend/model_manager/configs/wan_t5_encoder.py
new file mode 100644
index 00000000000..efda6a551a2
--- /dev/null
+++ b/invokeai/backend/model_manager/configs/wan_t5_encoder.py
@@ -0,0 +1,84 @@
+"""Configurations for the UMT5-XXL text encoder used by Wan 2.2.
+
+Wan ships a UMT5-XXL encoder (not the more common T5-XXL). The two are not
+weight-compatible — UMT5 has a different vocabulary and ``model_type``. We
+register a dedicated config + ModelType so users can't accidentally wire a
+FLUX/SD3-style T5-XXL into a Wan slot.
+
+For Phase 3 we accept the diffusers-folder layout only. Single-file UMT5
+checkpoints are uncommon; if they show up later, a checkpoint config can be
+added alongside this one.
+"""
+
+from __future__ import annotations
+
+import json
+from pathlib import Path
+from typing import Any, Literal, Self
+
+from pydantic import Field
+
+from invokeai.backend.model_manager.configs.base import Config_Base
+from invokeai.backend.model_manager.configs.identification_utils import (
+ NotAMatchError,
+ raise_for_override_fields,
+ raise_if_not_dir,
+)
+from invokeai.backend.model_manager.model_on_disk import ModelOnDisk
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType
+
+
+def _read_text_encoder_model_type(mod: ModelOnDisk) -> str | None:
+ """Return ``model_type`` from the encoder's ``config.json``.
+
+ Diffusers encoder folders may live at the root (``config.json``) or under a
+ ``text_encoder/`` subdirectory. UMT5-XXL sets ``model_type`` to ``"umt5"``;
+ a regular T5-XXL would be ``"t5"``.
+ """
+ candidates: list[Path] = [mod.path / "text_encoder" / "config.json", mod.path / "config.json"]
+ for path in candidates:
+ if path.exists():
+ try:
+ with path.open("r", encoding="utf-8") as f:
+ config = json.load(f)
+ except (json.JSONDecodeError, OSError):
+ continue
+ mt = config.get("model_type")
+ if isinstance(mt, str):
+ return mt.lower()
+ return None
+
+
+class WanT5Encoder_WanT5Encoder_Config(Config_Base):
+ """UMT5-XXL encoder in diffusers folder layout.
+
+ Accepts either:
+ - A directory containing ``text_encoder/`` (and typically ``tokenizer/``) ─ the
+ shape produced by ``Wan-AI/Wan2.2-T2V-A14B::text_encoder+tokenizer``.
+ - A bare ``text_encoder/`` directory whose own ``config.json`` declares
+ ``model_type: umt5``.
+ """
+
+ base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any)
+ type: Literal[ModelType.WanT5Encoder] = Field(default=ModelType.WanT5Encoder)
+ format: Literal[ModelFormat.WanT5Encoder] = Field(default=ModelFormat.WanT5Encoder)
+
+ @classmethod
+ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:
+ raise_if_not_dir(mod)
+ raise_for_override_fields(cls, override_fields)
+
+ # Refuse to claim full Wan pipelines — they should match Main_Diffusers_Wan_Config.
+ if (mod.path / "model_index.json").exists() or (mod.path / "transformer").exists():
+ raise NotAMatchError(
+ "directory looks like a full Wan pipeline (model_index.json or transformer/), "
+ "not a standalone Wan T5 encoder"
+ )
+
+ model_type = _read_text_encoder_model_type(mod)
+ if model_type is None:
+ raise NotAMatchError("no encoder config.json found at root or text_encoder/")
+ if model_type != "umt5":
+ raise NotAMatchError(f"encoder model_type is {model_type!r}, not 'umt5'")
+
+ return cls(**override_fields)
diff --git a/invokeai/backend/model_manager/load/model_loaders/lora.py b/invokeai/backend/model_manager/load/model_loaders/lora.py
index 15dfa376179..1b4f0cdebd0 100644
--- a/invokeai/backend/model_manager/load/model_loaders/lora.py
+++ b/invokeai/backend/model_manager/load/model_loaders/lora.py
@@ -63,6 +63,7 @@
)
from invokeai.backend.patches.lora_conversions.sd_lora_conversion_utils import lora_model_from_sd_state_dict
from invokeai.backend.patches.lora_conversions.sdxl_lora_conversion_utils import convert_sdxl_keys_to_diffusers_format
+from invokeai.backend.patches.lora_conversions.wan_lora_conversion_utils import lora_model_from_wan_state_dict
from invokeai.backend.patches.lora_conversions.z_image_lora_conversion_utils import lora_model_from_z_image_state_dict
@@ -175,6 +176,10 @@ def _load_model(
elif self._model_base == BaseModelType.Anima:
# Anima LoRAs use Kohya-style or diffusers PEFT format targeting Cosmos DiT blocks.
model = lora_model_from_anima_state_dict(state_dict=state_dict, alpha=None)
+ elif self._model_base == BaseModelType.Wan:
+ # Wan LoRAs use Kohya / diffusers PEFT / native PEFT formats targeting
+ # WanTransformer3DModel attention (attn1/attn2) and FFN blocks.
+ model = lora_model_from_wan_state_dict(state_dict=state_dict, alpha=None)
else:
raise ValueError(f"Unsupported LoRA base model: {self._model_base}")
diff --git a/invokeai/backend/model_manager/load/model_loaders/vae.py b/invokeai/backend/model_manager/load/model_loaders/vae.py
index 720821f3af8..b3d2eae38ee 100644
--- a/invokeai/backend/model_manager/load/model_loaders/vae.py
+++ b/invokeai/backend/model_manager/load/model_loaders/vae.py
@@ -10,6 +10,8 @@
VAE_Checkpoint_Anima_Config,
VAE_Checkpoint_Config_Base,
VAE_Checkpoint_QwenImage_Config,
+ VAE_Checkpoint_Wan_Config,
+ VAE_Diffusers_Wan_Config,
)
from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry
from invokeai.backend.model_manager.load.model_loaders.generic_diffusers import GenericDiffusersLoader
@@ -21,6 +23,133 @@
SubModelType,
)
+# Architectural defaults for the Wan 2.2-VAE (TI2V-5B). Verbatim from the
+# vae/config.json shipped with Wan-AI/Wan2.2-TI2V-5B-Diffusers — only the
+# values that differ from diffusers' AutoencoderKLWan defaults are listed.
+# latents_mean / latents_std are required because the model normalises latents
+# against them at encode/decode time; the wrong arrays produce silent garbage.
+_WAN_TI2V_5B_VAE_CONFIG: dict = {
+ "base_dim": 160,
+ "decoder_base_dim": 256,
+ "z_dim": 48,
+ "in_channels": 12,
+ "out_channels": 12,
+ "patch_size": 2,
+ "scale_factor_spatial": 16,
+ "is_residual": True,
+ "latents_mean": [
+ -0.2289,
+ -0.0052,
+ -0.1323,
+ -0.2339,
+ -0.2799,
+ 0.0174,
+ 0.1838,
+ 0.1557,
+ -0.1382,
+ 0.0542,
+ 0.2813,
+ 0.0891,
+ 0.1570,
+ -0.0098,
+ 0.0375,
+ -0.1825,
+ -0.2246,
+ -0.1207,
+ -0.0698,
+ 0.5109,
+ 0.2665,
+ -0.2108,
+ -0.2158,
+ 0.2502,
+ -0.2055,
+ -0.0322,
+ 0.1109,
+ 0.1567,
+ -0.0729,
+ 0.0899,
+ -0.2799,
+ -0.1230,
+ -0.0313,
+ -0.1649,
+ 0.0117,
+ 0.0723,
+ -0.2839,
+ -0.2083,
+ -0.0520,
+ 0.3748,
+ 0.0152,
+ 0.1957,
+ 0.1433,
+ -0.2944,
+ 0.3573,
+ -0.0548,
+ -0.1681,
+ -0.0667,
+ ],
+ "latents_std": [
+ 0.4765,
+ 1.0364,
+ 0.4514,
+ 1.1677,
+ 0.5313,
+ 0.4990,
+ 0.4818,
+ 0.5013,
+ 0.8158,
+ 1.0344,
+ 0.5894,
+ 1.0901,
+ 0.6885,
+ 0.6165,
+ 0.8454,
+ 0.4978,
+ 0.5759,
+ 0.3523,
+ 0.7135,
+ 0.6804,
+ 0.5833,
+ 1.4146,
+ 0.8986,
+ 0.5659,
+ 0.7069,
+ 0.5338,
+ 0.4889,
+ 0.4917,
+ 0.4069,
+ 0.4999,
+ 0.6866,
+ 0.4093,
+ 0.5709,
+ 0.6065,
+ 0.6415,
+ 0.4944,
+ 0.5726,
+ 1.2042,
+ 0.5458,
+ 1.6887,
+ 0.3971,
+ 1.0600,
+ 0.3943,
+ 0.5537,
+ 0.5444,
+ 0.4089,
+ 0.7468,
+ 0.7744,
+ ],
+}
+
+
+def _wan_vae_init_kwargs_for(latent_channels: int) -> dict:
+ """Return the AutoencoderKLWan constructor kwargs for a given z_dim.
+
+ z_dim=48 means TI2V-5B's Wan 2.2-VAE (different base dim, patchified IO,
+ 16x spatial). Anything else falls back to the A14B / Wan 2.1 defaults.
+ """
+ if latent_channels == 48:
+ return dict(_WAN_TI2V_5B_VAE_CONFIG)
+ return {"z_dim": latent_channels}
+
@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.VAE, format=ModelFormat.Diffusers)
@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.VAE, format=ModelFormat.Checkpoint)
@@ -39,6 +168,10 @@ def _load_model(
config.path,
torch_dtype=self._torch_dtype,
)
+ elif isinstance(config, VAE_Checkpoint_Wan_Config):
+ return self._load_wan_vae(config)
+ elif isinstance(config, VAE_Diffusers_Wan_Config):
+ return self._load_wan_vae_diffusers(config)
elif isinstance(config, VAE_Checkpoint_QwenImage_Config):
return self._load_qwen_image_vae(config)
elif isinstance(config, VAE_Checkpoint_Config_Base):
@@ -49,6 +182,67 @@ def _load_model(
else:
return super()._load_model(config, submodel_type)
+ def _load_wan_vae(self, config: VAE_Checkpoint_Wan_Config) -> AnyModel:
+ """Load a Wan 2.2 VAE from a single safetensors file.
+
+ Picks the correct ``AutoencoderKLWan`` config based on ``z_dim``. The Wan
+ ecosystem ships two distinct VAE architectures:
+
+ * ``z_dim=16`` — the Wan 2.1 / Wan 2.2 A14B VAE. Diffusers' defaults match
+ this one (base_dim=96, 8x spatial, no patchify, 3 in/out channels).
+ * ``z_dim=48`` — the Wan 2.2-VAE used by TI2V-5B. Larger (base_dim=160,
+ decoder_base_dim=256), 16x spatial, patchify with patch_size=2 (so
+ in/out channels are 12 = 3 RGB x 2x2 patch), residual blocks, and
+ its own latents_mean / latents_std.
+
+ Without overriding those params at construction time, the state dict
+ from the TI2V-5B VAE checkpoint won't load (channel and shape mismatches
+ throughout the encoder + decoder).
+ """
+ import accelerate
+ from diffusers.models.autoencoders.autoencoder_kl_wan import AutoencoderKLWan
+ from safetensors.torch import load_file
+
+ sd = load_file(config.path)
+
+ if self._torch_dtype is not None:
+ for k in list(sd.keys()):
+ if sd[k].is_floating_point():
+ sd[k] = sd[k].to(self._torch_dtype)
+
+ new_sd_size = sum(t.nelement() * t.element_size() for t in sd.values())
+ self._ram_cache.make_room(new_sd_size)
+
+ init_kwargs = _wan_vae_init_kwargs_for(config.latent_channels)
+ with accelerate.init_empty_weights():
+ model = AutoencoderKLWan(**init_kwargs)
+
+ model.load_state_dict(sd, strict=True, assign=True)
+ model.eval()
+ return model
+
+ def _load_wan_vae_diffusers(self, config: VAE_Diffusers_Wan_Config) -> AnyModel:
+ """Load a Wan 2.2 VAE from a flat diffusers folder (AutoencoderKLWan).
+
+ The standalone install ``Wan-AI/Wan2.2-T2V-A14B-Diffusers::vae`` lands as a
+ single-class folder (``config.json`` + ``diffusion_pytorch_model.safetensors``,
+ no ``model_index.json``). The generic loader rejects this when a
+ ``submodel_type`` is requested — we always pass ``SubModelType.VAE`` from
+ the model loader invocation since that's how cached entries are keyed.
+ Loading ``AutoencoderKLWan`` directly here sidesteps the submodel check.
+
+ Forces bfloat16 (same as ``WanDiffusersModel``) — fp16 is unstable on the
+ Wan VAE.
+ """
+ import torch
+ from diffusers.models.autoencoders.autoencoder_kl_wan import AutoencoderKLWan
+
+ return AutoencoderKLWan.from_pretrained(
+ config.path,
+ torch_dtype=torch.bfloat16,
+ local_files_only=True,
+ )
+
def _load_qwen_image_vae(self, config: VAE_Checkpoint_QwenImage_Config) -> AnyModel:
"""Load a Qwen Image VAE from a single safetensors file.
diff --git a/invokeai/backend/model_manager/load/model_loaders/wan.py b/invokeai/backend/model_manager/load/model_loaders/wan.py
new file mode 100644
index 00000000000..f3bb7de7b61
--- /dev/null
+++ b/invokeai/backend/model_manager/load/model_loaders/wan.py
@@ -0,0 +1,354 @@
+"""Loader registrations for Wan 2.2 image-generation models.
+
+Currently covers:
+- Main: Diffusers format (T2V-A14B with dual experts via Transformer +
+ Transformer2 submodels, plus TI2V-5B). Phase 4 will add a GGUFQuantized loader.
+- WanT5Encoder: standalone UMT5-XXL encoder folder (``text_encoder/`` +
+ ``tokenizer/`` subdirs, or a flat ``text_encoder/`` folder).
+- VAE: handled in ``vae.py`` (registered for type=VAE generically).
+"""
+
+from pathlib import Path
+from typing import Optional
+
+import torch
+
+from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base, Diffusers_Config_Base
+from invokeai.backend.model_manager.configs.factory import AnyModelConfig
+from invokeai.backend.model_manager.configs.main import Main_GGUF_Wan_Config, _is_native_wan_layout
+from invokeai.backend.model_manager.load.load_default import ModelLoader
+from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry
+from invokeai.backend.model_manager.load.model_loaders.generic_diffusers import GenericDiffusersLoader
+from invokeai.backend.model_manager.taxonomy import (
+ AnyModel,
+ BaseModelType,
+ ModelFormat,
+ ModelType,
+ SubModelType,
+ WanVariantType,
+)
+from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor
+from invokeai.backend.quantization.gguf.loaders import gguf_sd_loader
+from invokeai.backend.quantization.gguf.utils import TORCH_COMPATIBLE_QTYPES
+from invokeai.backend.util.devices import TorchDevice
+
+
+@ModelLoaderRegistry.register(base=BaseModelType.Wan, type=ModelType.Main, format=ModelFormat.Diffusers)
+class WanDiffusersModel(GenericDiffusersLoader):
+ """Loader for Wan 2.2 diffusers-format models (T2V-A14B and TI2V-5B).
+
+ Forces bfloat16 for the transformer and VAE — fp16 is unstable on Wan VAE
+ (same issue affects the Flux VAE). Resolves the appropriate Hugging Face
+ class for each submodel via the parent loader's ``get_hf_load_class``.
+ """
+
+ def _load_model(
+ self,
+ config: AnyModelConfig,
+ submodel_type: Optional[SubModelType] = None,
+ ) -> AnyModel:
+ if isinstance(config, Checkpoint_Config_Base):
+ raise NotImplementedError("Single-file checkpoint format is not yet supported for Wan models.")
+
+ if submodel_type is None:
+ raise Exception("A submodel type must be provided when loading Wan main pipelines.")
+
+ model_path = Path(config.path)
+ load_class = self.get_hf_load_class(model_path, submodel_type)
+ repo_variant = config.repo_variant if isinstance(config, Diffusers_Config_Base) else None
+ variant = repo_variant.value if repo_variant else None
+ model_path = model_path / submodel_type.value
+
+ # bfloat16 across the board: matches Diffusers WanPipeline reference and
+ # avoids the fp16 instability seen in the Wan VAE.
+ dtype_kwarg = {"dtype": torch.bfloat16}
+ try:
+ result: AnyModel = load_class.from_pretrained(
+ model_path,
+ **dtype_kwarg,
+ variant=variant,
+ local_files_only=True,
+ )
+ except TypeError:
+ # Older diffusers releases use torch_dtype instead of dtype.
+ dtype_kwarg = {"torch_dtype": torch.bfloat16}
+ result = load_class.from_pretrained(
+ model_path,
+ **dtype_kwarg,
+ variant=variant,
+ local_files_only=True,
+ )
+ except OSError as e:
+ # Some Wan repos ship without a fp16 variant suffix on every submodel.
+ # If the requested variant isn't on disk, fall back to the default weights.
+ if variant and "no file named" in str(e):
+ result = load_class.from_pretrained(model_path, **dtype_kwarg, local_files_only=True)
+ else:
+ raise
+
+ return result
+
+
+# Native (upstream) -> Diffusers key rename rules.
+#
+# Mirrors diffusers.loaders.single_file_utils.convert_wan_transformer_to_diffusers
+# (T2V subset; we don't ship VACE / motion / face-adapter conversion). Order
+# matters — `cross_attn`/`self_attn` must come before `.q. .k. .v. .o.` so the
+# attention blocks are renamed before the projection suffix swap. The norm2/3
+# swap uses a placeholder to avoid collisions during the substring rewrite.
+_WAN_NATIVE_TO_DIFFUSERS_RENAMES: tuple[tuple[str, str], ...] = (
+ ("time_embedding.0", "condition_embedder.time_embedder.linear_1"),
+ ("time_embedding.2", "condition_embedder.time_embedder.linear_2"),
+ ("text_embedding.0", "condition_embedder.text_embedder.linear_1"),
+ ("text_embedding.2", "condition_embedder.text_embedder.linear_2"),
+ ("time_projection.1", "condition_embedder.time_proj"),
+ ("cross_attn", "attn2"),
+ ("self_attn", "attn1"),
+ (".o.", ".to_out.0."),
+ (".q.", ".to_q."),
+ (".k.", ".to_k."),
+ (".v.", ".to_v."),
+ (".k_img.", ".add_k_proj."),
+ (".v_img.", ".add_v_proj."),
+ (".norm_k_img.", ".norm_added_k."),
+ ("head.modulation", "scale_shift_table"),
+ ("head.head", "proj_out"),
+ ("modulation", "scale_shift_table"),
+ ("ffn.0", "ffn.net.0.proj"),
+ ("ffn.2", "ffn.net.2"),
+ # norm2 <-> norm3 swap via placeholder
+ ("norm2", "norm__placeholder"),
+ ("norm3", "norm2"),
+ ("norm__placeholder", "norm3"),
+ # I2V-only keys (harmless on T2V)
+ ("img_emb.proj.0", "condition_embedder.image_embedder.norm1"),
+ ("img_emb.proj.1", "condition_embedder.image_embedder.ff.net.0.proj"),
+ ("img_emb.proj.3", "condition_embedder.image_embedder.ff.net.2"),
+ ("img_emb.proj.4", "condition_embedder.image_embedder.norm2"),
+)
+
+
+def _convert_wan_native_to_diffusers(state_dict: dict) -> dict:
+ """Rename native upstream Wan keys (ComfyUI / QuantStack) to diffusers names.
+
+ Pure substring replacement — no tensor manipulation — so it's safe to apply
+ to a dict of GGMLTensors. Returns a new dict; the input is not mutated.
+ """
+ converted: dict = {}
+ for key, value in state_dict.items():
+ if not isinstance(key, str):
+ converted[key] = value
+ continue
+ new_key = key
+ for needle, replacement in _WAN_NATIVE_TO_DIFFUSERS_RENAMES:
+ new_key = new_key.replace(needle, replacement)
+ converted[new_key] = value
+ return converted
+
+
+def _unwrap_unquantized_to_compute_dtype(state_dict: dict) -> dict:
+ """Replace non-quantized GGMLTensor entries with plain tensors at compute_dtype.
+
+ Why: QuantStack-style GGUFs store biases (and other small tensors) as F16,
+ while Wan's ``patch_embedding`` is an ``nn.Conv3d``. ``conv3d`` isn't in
+ GGMLTensor's dispatch table, so PyTorch reads the wrapper's underlying F16
+ storage directly and crashes against bf16 latents
+ (``Input type (c10::BFloat16) and bias type (c10::Half) should be the same``).
+
+ For compatible qtypes (F16/F32/BF16) we just pre-cast to compute_dtype here —
+ they're not quantized, there's no benefit to keeping them wrapped, and
+ unwrapping them sidesteps the missing-op problem entirely. Genuinely
+ quantized tensors (Q4_K, Q6_K, etc.) stay wrapped — their on-demand
+ dequantization through the linear/addmm dispatch path still works.
+ """
+ unwrapped: dict = {}
+ for key, value in state_dict.items():
+ if isinstance(value, GGMLTensor) and value._ggml_quantization_type in TORCH_COMPATIBLE_QTYPES:
+ # GGMLTensor.get_dequantized_tensor() already casts to compute_dtype.
+ unwrapped[key] = value.get_dequantized_tensor()
+ else:
+ unwrapped[key] = value
+ return unwrapped
+
+
+@ModelLoaderRegistry.register(base=BaseModelType.Wan, type=ModelType.Main, format=ModelFormat.GGUFQuantized)
+class WanGGUFCheckpointModel(ModelLoader):
+ """Loader for GGUF-quantized Wan 2.2 transformer models.
+
+ The community typically distributes Wan A14B as two files (one per expert
+ — high-noise + low-noise). Each file is loaded independently here; the
+ pairing happens at the WanModelLoaderInvocation layer. TI2V-5B ships as a
+ single file.
+
+ Mirrors the QwenImage GGUF loader pattern: ``gguf_sd_loader`` -> strip the
+ ComfyUI ``model.diffusion_model.`` / ``diffusion_model.`` prefix if present
+ -> auto-detect arch from state-dict shapes -> ``init_empty_weights`` +
+ ``load_state_dict(strict=False, assign=True)``.
+ """
+
+ def _load_model(
+ self,
+ config: AnyModelConfig,
+ submodel_type: Optional[SubModelType] = None,
+ ) -> AnyModel:
+ if not isinstance(config, Main_GGUF_Wan_Config):
+ raise TypeError(f"Expected Main_GGUF_Wan_Config, got {type(config).__name__}.")
+
+ if submodel_type != SubModelType.Transformer:
+ raise ValueError(
+ "Only the Transformer submodel is available from a GGUF Wan checkpoint. "
+ "Pair with a standalone Wan VAE and Wan T5 encoder for the other components."
+ )
+
+ return self._load_from_singlefile(config)
+
+ def _load_from_singlefile(self, config: Main_GGUF_Wan_Config) -> AnyModel:
+ import accelerate
+ from diffusers import WanTransformer3DModel
+
+ model_path = Path(config.path)
+ target_device = TorchDevice.choose_torch_device()
+ compute_dtype = TorchDevice.choose_bfloat16_safe_dtype(target_device)
+
+ sd = gguf_sd_loader(model_path, compute_dtype=compute_dtype)
+
+ # Strip ComfyUI-style prefixes if present.
+ for prefix in ("model.diffusion_model.", "diffusion_model."):
+ if any(isinstance(k, str) and k.startswith(prefix) for k in sd.keys()):
+ sd = {
+ (k[len(prefix) :] if isinstance(k, str) and k.startswith(prefix) else k): v for k, v in sd.items()
+ }
+ break
+
+ # QuantStack and other community releases ship the native upstream Wan key
+ # layout (text_embedding.0, self_attn/cross_attn, ffn.0/2, head.head, ...);
+ # diffusers' WanTransformer3DModel expects condition_embedder.*, attn1/attn2,
+ # ffn.net.*, proj_out. Convert in place if needed.
+ if _is_native_wan_layout(sd):
+ sd = _convert_wan_native_to_diffusers(sd)
+
+ # Pre-cast non-quantized tensors (F16/F32/BF16 biases, scale_shift_table,
+ # patch_embedding.weight, etc.) to compute_dtype. This avoids dtype
+ # mismatches in conv3d at the input (patch_embedding is the only Conv3d
+ # in WanTransformer3DModel; conv3d isn't in GGMLTensor's dispatch table
+ # so the wrapper's underlying storage dtype reaches PyTorch directly).
+ sd = _unwrap_unquantized_to_compute_dtype(sd)
+
+ # Auto-detect architecture from the state dict.
+ num_layers = 0
+ for key in sd.keys():
+ if isinstance(key, str) and key.startswith("blocks."):
+ parts = key.split(".")
+ if len(parts) >= 2:
+ try:
+ num_layers = max(num_layers, int(parts[1]) + 1)
+ except ValueError:
+ pass
+
+ # Patch embedding gives us in_channels (16=A14B, 48=TI2V-5B) and inner dim.
+ patch_w = sd.get("patch_embedding.weight")
+ if patch_w is None:
+ raise RuntimeError("GGUF state dict missing patch_embedding.weight after prefix strip")
+ patch_shape = patch_w.tensor_shape if isinstance(patch_w, GGMLTensor) else patch_w.shape
+ inner_dim = int(patch_shape[0])
+ in_channels = int(patch_shape[1])
+
+ # Wan uses head_dim=128 throughout the family; num_heads = inner_dim / 128.
+ attention_head_dim = 128
+ num_attention_heads = inner_dim // attention_head_dim
+
+ ffn_w = sd.get("blocks.0.ffn.net.0.proj.weight")
+ if ffn_w is None:
+ raise RuntimeError("GGUF state dict missing blocks.0.ffn.net.0.proj.weight after prefix strip")
+ ffn_shape = ffn_w.tensor_shape if isinstance(ffn_w, GGMLTensor) else ffn_w.shape
+ ffn_dim = int(ffn_shape[0])
+
+ text_w = sd.get("condition_embedder.text_embedder.linear_1.weight")
+ text_dim = 4096
+ if text_w is not None:
+ text_shape = text_w.tensor_shape if isinstance(text_w, GGMLTensor) else text_w.shape
+ text_dim = int(text_shape[1])
+
+ # out_channels is read from proj_out.weight directly rather than assumed
+ # equal to in_channels: I2V-A14B has in_channels=36 (16 noise + 16
+ # ref-image latents + 4 mask, concatenated by the denoise loop) but
+ # out_channels=16 (only the noise prediction comes back). proj_out is
+ # ``nn.Linear(inner_dim, out_channels * prod(patch_size))`` and
+ # patch_size is (1, 2, 2) → prod = 4 for the Wan 2.2 family.
+ proj_out_w = sd.get("proj_out.weight")
+ if proj_out_w is None:
+ raise RuntimeError("GGUF state dict missing proj_out.weight after prefix strip")
+ proj_out_shape = proj_out_w.tensor_shape if isinstance(proj_out_w, GGMLTensor) else proj_out_w.shape
+ out_channels = int(proj_out_shape[0]) // 4
+
+ # Layer count fallback (only triggers if the auto-count loop above
+ # found zero blocks, which shouldn't happen for a valid GGUF). T2V/I2V
+ # A14B have 40 layers; TI2V-5B has 30.
+ layer_count_fallback = 30 if config.variant == WanVariantType.TI2V_5B else 40
+
+ model_config: dict = {
+ "patch_size": (1, 2, 2),
+ "in_channels": in_channels,
+ "out_channels": out_channels,
+ "num_layers": num_layers if num_layers > 0 else layer_count_fallback,
+ "attention_head_dim": attention_head_dim,
+ "num_attention_heads": num_attention_heads,
+ "ffn_dim": ffn_dim,
+ "text_dim": text_dim,
+ }
+
+ with accelerate.init_empty_weights():
+ model = WanTransformer3DModel(**model_config)
+
+ model.load_state_dict(sd, strict=False, assign=True)
+ return model
+
+
+@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.WanT5Encoder, format=ModelFormat.WanT5Encoder)
+class WanT5EncoderLoader(ModelLoader):
+ """Loader for the standalone Wan UMT5-XXL encoder.
+
+ Accepts two on-disk layouts:
+ 1. Parent dir with ``text_encoder/`` (and typically ``tokenizer/``) subdirs —
+ what ``Wan-AI/Wan2.2-T2V-A14B::text_encoder+tokenizer`` produces.
+ 2. A flat ``text_encoder/`` folder with ``config.json`` declaring
+ ``model_type: umt5`` directly at the root. In this case the tokenizer
+ is loaded from the same folder via ``AutoTokenizer.from_pretrained``.
+ """
+
+ def _load_model(
+ self,
+ config: AnyModelConfig,
+ submodel_type: Optional[SubModelType] = None,
+ ) -> AnyModel:
+ if submodel_type is None:
+ raise ValueError("A submodel type (Tokenizer or TextEncoder) must be provided.")
+
+ root = Path(config.path)
+ nested_text_encoder = root / "text_encoder"
+ nested_tokenizer = root / "tokenizer"
+
+ if submodel_type == SubModelType.TextEncoder:
+ from transformers import UMT5EncoderModel
+
+ target = nested_text_encoder if nested_text_encoder.exists() else root
+ return UMT5EncoderModel.from_pretrained(
+ str(target),
+ torch_dtype=torch.bfloat16,
+ local_files_only=True,
+ )
+ if submodel_type == SubModelType.Tokenizer:
+ from transformers import AutoTokenizer
+
+ # Prefer a sibling tokenizer/ directory; fall back to the encoder dir
+ # itself, which is normal for "flat" downloads.
+ target = (
+ nested_tokenizer
+ if nested_tokenizer.exists()
+ else (nested_text_encoder if nested_text_encoder.exists() else root)
+ )
+ return AutoTokenizer.from_pretrained(str(target), local_files_only=True)
+
+ raise ValueError(
+ f"Unsupported submodel type for WanT5Encoder: {submodel_type.value if submodel_type else 'None'}"
+ )
diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py
index 64f099554a7..9036d9de2db 100644
--- a/invokeai/backend/model_manager/starter_models.py
+++ b/invokeai/backend/model_manager/starter_models.py
@@ -15,6 +15,7 @@
ModelFormat,
ModelType,
QwenImageVariantType,
+ WanVariantType,
)
@@ -1299,6 +1300,229 @@ def _gemini_3_resolution_presets(
default_settings=ExternalApiModelDefaultSettings(width=1328, height=1328, num_images=1),
panel_schema=ExternalModelPanelSchema(image=[{"name": "dimensions"}]),
)
+# region Wan 2.2 (local)
+# Shared components — all Wan 2.2 variants use the UMT5-XXL text encoder. A14B
+# (both T2V and I2V) uses a 16-channel VAE; TI2V-5B uses a 48-channel VAE. The
+# two VAEs are not interchangeable.
+wan_22_t5_encoder = StarterModel(
+ name="Wan T5 Encoder (UMT5-XXL)",
+ base=BaseModelType.Any,
+ source="Wan-AI/Wan2.2-T2V-A14B-Diffusers::text_encoder+tokenizer",
+ description="UMT5-XXL text encoder used by all Wan 2.2 variants (T2V/I2V A14B and TI2V-5B). "
+ "Required when running a GGUF Wan main without a Diffusers Component Source. (~11GB)",
+ type=ModelType.WanT5Encoder,
+ format=ModelFormat.WanT5Encoder,
+)
+
+wan_22_a14b_vae = StarterModel(
+ name="Wan 2.2 A14B VAE",
+ base=BaseModelType.Wan,
+ source="Wan-AI/Wan2.2-T2V-A14B-Diffusers::vae/diffusion_pytorch_model.safetensors",
+ description="Wan 2.2 A14B VAE (16-channel). Shared between T2V and I2V A14B variants. "
+ "Not interchangeable with the TI2V-5B VAE. (~250MB)",
+ type=ModelType.VAE,
+ format=ModelFormat.Checkpoint,
+)
+
+wan_22_5b_vae = StarterModel(
+ name="Wan 2.2 TI2V-5B VAE",
+ base=BaseModelType.Wan,
+ source="Wan-AI/Wan2.2-TI2V-5B-Diffusers::vae/diffusion_pytorch_model.safetensors",
+ description="Wan 2.2 TI2V-5B VAE (48-channel). Required for the TI2V-5B model family. "
+ "Not interchangeable with the A14B VAE. (~400MB)",
+ type=ModelType.VAE,
+ format=ModelFormat.Checkpoint,
+)
+
+# T2V A14B — full Diffusers + GGUF expert pairs (Q4_K_M and Q8_0).
+# The high-noise GGUF is the "main" entry the user picks; the low-noise GGUF
+# is wired as the partner expert via the Advanced panel. Each high-noise entry
+# lists its low-noise partner plus the shared VAE/encoder as dependencies so
+# the bundle/dependency installer pulls everything together.
+wan_22_t2v_a14b_diffusers = StarterModel(
+ name="Wan 2.2 T2V A14B (Diffusers)",
+ base=BaseModelType.Wan,
+ source="Wan-AI/Wan2.2-T2V-A14B-Diffusers",
+ description="Full Diffusers Wan 2.2 T2V A14B model — both expert transformers, VAE, and UMT5-XXL "
+ "encoder in a single folder. No additional components needed. (~80GB)",
+ type=ModelType.Main,
+ format=ModelFormat.Diffusers,
+ variant=WanVariantType.T2V_A14B,
+)
+
+wan_22_t2v_a14b_low_gguf_q4_k_m = StarterModel(
+ name="Wan 2.2 T2V A14B Low Noise (Q4_K_M)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF/resolve/main/LowNoise/Wan2.2-T2V-A14B-LowNoise-Q4_K_M.gguf",
+ description="Wan 2.2 T2V A14B low-noise expert transformer (Q4_K_M). Paired with the high-noise "
+ "expert; selected via the Advanced 'Transformer (Low Noise)' field. (~9.7GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.T2V_A14B,
+)
+
+wan_22_t2v_a14b_gguf_q4_k_m = StarterModel(
+ name="Wan 2.2 T2V A14B High Noise (Q4_K_M)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF/resolve/main/HighNoise/Wan2.2-T2V-A14B-HighNoise-Q4_K_M.gguf",
+ description="Wan 2.2 T2V A14B high-noise expert transformer (Q4_K_M). Pick this as the main model; "
+ "the low-noise partner is wired in Advanced. Good quality/size balance. (~9.7GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.T2V_A14B,
+ dependencies=[wan_22_a14b_vae, wan_22_t5_encoder, wan_22_t2v_a14b_low_gguf_q4_k_m],
+)
+
+wan_22_t2v_a14b_low_gguf_q8_0 = StarterModel(
+ name="Wan 2.2 T2V A14B Low Noise (Q8_0)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF/resolve/main/LowNoise/Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf",
+ description="Wan 2.2 T2V A14B low-noise expert transformer (Q8_0). Highest quality quantization. (~15.4GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.T2V_A14B,
+)
+
+wan_22_t2v_a14b_gguf_q8_0 = StarterModel(
+ name="Wan 2.2 T2V A14B High Noise (Q8_0)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF/resolve/main/HighNoise/Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf",
+ description="Wan 2.2 T2V A14B high-noise expert transformer (Q8_0). Pick as the main; pair with the "
+ "low-noise Q8_0 partner in Advanced. Highest quality quantization. (~15.4GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.T2V_A14B,
+ dependencies=[wan_22_a14b_vae, wan_22_t5_encoder, wan_22_t2v_a14b_low_gguf_q8_0],
+)
+
+# T2V Lightning LoRAs — V1.1 Seko rank-64 pair (4-step inference).
+wan_22_t2v_lightning_high = StarterModel(
+ name="Wan 2.2 T2V Lightning High Noise (4-step, V1.1)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/lightx2v/Wan2.2-Lightning/resolve/main/Wan2.2-T2V-A14B-4steps-lora-rank64-Seko-V1.1/high_noise_model.safetensors",
+ description="Lightning distillation LoRA for the Wan 2.2 T2V A14B high-noise expert — enables "
+ "4-step generation. Use together with the low-noise variant. Settings: Steps=4, CFG=1.",
+ type=ModelType.LoRA,
+)
+
+wan_22_t2v_lightning_low = StarterModel(
+ name="Wan 2.2 T2V Lightning Low Noise (4-step, V1.1)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/lightx2v/Wan2.2-Lightning/resolve/main/Wan2.2-T2V-A14B-4steps-lora-rank64-Seko-V1.1/low_noise_model.safetensors",
+ description="Lightning distillation LoRA for the Wan 2.2 T2V A14B low-noise expert — enables "
+ "4-step generation. Use together with the high-noise variant. Settings: Steps=4, CFG=1.",
+ type=ModelType.LoRA,
+)
+
+# I2V A14B — full Diffusers + GGUF expert pairs (Q4_K_M and Q8_0).
+wan_22_i2v_a14b_diffusers = StarterModel(
+ name="Wan 2.2 I2V A14B (Diffusers)",
+ base=BaseModelType.Wan,
+ source="Wan-AI/Wan2.2-I2V-A14B-Diffusers",
+ description="Full Diffusers Wan 2.2 I2V A14B model — both expert transformers, VAE, and UMT5-XXL "
+ "encoder. Use the Reference Images panel to provide the conditioning image. (~80GB)",
+ type=ModelType.Main,
+ format=ModelFormat.Diffusers,
+ variant=WanVariantType.I2V_A14B,
+)
+
+wan_22_i2v_a14b_low_gguf_q4_k_m = StarterModel(
+ name="Wan 2.2 I2V A14B Low Noise (Q4_K_M)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-I2V-A14B-GGUF/resolve/main/LowNoise/Wan2.2-I2V-A14B-LowNoise-Q4_K_M.gguf",
+ description="Wan 2.2 I2V A14B low-noise expert transformer (Q4_K_M). (~9.7GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.I2V_A14B,
+)
+
+wan_22_i2v_a14b_gguf_q4_k_m = StarterModel(
+ name="Wan 2.2 I2V A14B High Noise (Q4_K_M)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-I2V-A14B-GGUF/resolve/main/HighNoise/Wan2.2-I2V-A14B-HighNoise-Q4_K_M.gguf",
+ description="Wan 2.2 I2V A14B high-noise expert transformer (Q4_K_M). Pick as the main; pair with "
+ "the low-noise partner in Advanced. Use the Reference Images panel for the conditioning image. (~9.7GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.I2V_A14B,
+ dependencies=[wan_22_a14b_vae, wan_22_t5_encoder, wan_22_i2v_a14b_low_gguf_q4_k_m],
+)
+
+wan_22_i2v_a14b_low_gguf_q8_0 = StarterModel(
+ name="Wan 2.2 I2V A14B Low Noise (Q8_0)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-I2V-A14B-GGUF/resolve/main/LowNoise/Wan2.2-I2V-A14B-LowNoise-Q8_0.gguf",
+ description="Wan 2.2 I2V A14B low-noise expert transformer (Q8_0). Highest quality quantization. (~15.4GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.I2V_A14B,
+)
+
+wan_22_i2v_a14b_gguf_q8_0 = StarterModel(
+ name="Wan 2.2 I2V A14B High Noise (Q8_0)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-I2V-A14B-GGUF/resolve/main/HighNoise/Wan2.2-I2V-A14B-HighNoise-Q8_0.gguf",
+ description="Wan 2.2 I2V A14B high-noise expert transformer (Q8_0). Highest quality quantization. (~15.4GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.I2V_A14B,
+ dependencies=[wan_22_a14b_vae, wan_22_t5_encoder, wan_22_i2v_a14b_low_gguf_q8_0],
+)
+
+# I2V Lightning LoRAs — Seko rank-64 pair (4-step inference). Currently only V1.
+wan_22_i2v_lightning_high = StarterModel(
+ name="Wan 2.2 I2V Lightning High Noise (4-step, V1)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/lightx2v/Wan2.2-Lightning/resolve/main/Wan2.2-I2V-A14B-4steps-lora-rank64-Seko-V1/high_noise_model.safetensors",
+ description="Lightning distillation LoRA for the Wan 2.2 I2V A14B high-noise expert — enables "
+ "4-step image-to-image generation. Use together with the low-noise variant. Settings: Steps=4, CFG=1.",
+ type=ModelType.LoRA,
+)
+
+wan_22_i2v_lightning_low = StarterModel(
+ name="Wan 2.2 I2V Lightning Low Noise (4-step, V1)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/lightx2v/Wan2.2-Lightning/resolve/main/Wan2.2-I2V-A14B-4steps-lora-rank64-Seko-V1/low_noise_model.safetensors",
+ description="Lightning distillation LoRA for the Wan 2.2 I2V A14B low-noise expert — enables "
+ "4-step image-to-image generation. Use together with the high-noise variant. Settings: Steps=4, CFG=1.",
+ type=ModelType.LoRA,
+)
+
+# TI2V-5B — single-transformer model (no expert pair). Uses its own 48-channel VAE.
+wan_22_ti2v_5b_diffusers = StarterModel(
+ name="Wan 2.2 TI2V-5B (Diffusers)",
+ base=BaseModelType.Wan,
+ source="Wan-AI/Wan2.2-TI2V-5B-Diffusers",
+ description="Full Diffusers Wan 2.2 TI2V-5B model — single 5B transformer, 48-channel VAE, and "
+ "UMT5-XXL encoder. Smaller and faster than A14B; runs on consumer GPUs. (~20GB)",
+ type=ModelType.Main,
+ format=ModelFormat.Diffusers,
+ variant=WanVariantType.TI2V_5B,
+)
+
+wan_22_ti2v_5b_gguf_q4_k_m = StarterModel(
+ name="Wan 2.2 TI2V-5B (Q4_K_M)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-TI2V-5B-GGUF/resolve/main/Wan2.2-TI2V-5B-Q4_K_M.gguf",
+ description="Wan 2.2 TI2V-5B transformer (Q4_K_M). Single-expert model — no low-noise partner needed. (~3.4GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.TI2V_5B,
+ dependencies=[wan_22_5b_vae, wan_22_t5_encoder],
+)
+
+wan_22_ti2v_5b_gguf_q8_0 = StarterModel(
+ name="Wan 2.2 TI2V-5B (Q8_0)",
+ base=BaseModelType.Wan,
+ source="https://huggingface.co/QuantStack/Wan2.2-TI2V-5B-GGUF/resolve/main/Wan2.2-TI2V-5B-Q8_0.gguf",
+ description="Wan 2.2 TI2V-5B transformer (Q8_0). Highest quality quantization. (~5.4GB)",
+ type=ModelType.Main,
+ format=ModelFormat.GGUFQuantized,
+ variant=WanVariantType.TI2V_5B,
+ dependencies=[wan_22_5b_vae, wan_22_t5_encoder],
+)
+# endregion
+
alibabacloud_wan26_t2i = StarterModel(
name="Wan 2.6 Text-to-Image",
base=BaseModelType.External,
@@ -1744,6 +1968,26 @@ def _gemini_3_resolution_presets(
z_image_qwen3_encoder_quantized,
z_image_controlnet_union,
z_image_controlnet_tile,
+ wan_22_t5_encoder,
+ wan_22_a14b_vae,
+ wan_22_5b_vae,
+ wan_22_t2v_a14b_diffusers,
+ wan_22_t2v_a14b_low_gguf_q4_k_m,
+ wan_22_t2v_a14b_gguf_q4_k_m,
+ wan_22_t2v_a14b_low_gguf_q8_0,
+ wan_22_t2v_a14b_gguf_q8_0,
+ wan_22_t2v_lightning_high,
+ wan_22_t2v_lightning_low,
+ wan_22_i2v_a14b_diffusers,
+ wan_22_i2v_a14b_low_gguf_q4_k_m,
+ wan_22_i2v_a14b_gguf_q4_k_m,
+ wan_22_i2v_a14b_low_gguf_q8_0,
+ wan_22_i2v_a14b_gguf_q8_0,
+ wan_22_i2v_lightning_high,
+ wan_22_i2v_lightning_low,
+ wan_22_ti2v_5b_diffusers,
+ wan_22_ti2v_5b_gguf_q4_k_m,
+ wan_22_ti2v_5b_gguf_q8_0,
gemini_flash_image,
gemini_pro_image_preview,
gemini_3_1_flash_image_preview,
@@ -1861,6 +2105,31 @@ def _gemini_3_resolution_presets(
anima_lllite_sketch,
]
+# Wan 2.2 starter bundles. Split into T2V and I2V so users only pay for the
+# capability they need: a 12 GB card can install just the T2V bundle and have
+# both text-to-video (T2V-A14B) and a low-VRAM image-to-video option (via
+# TI2V-5B, which handles both modes in one ~3.4 GB model). The I2V bundle adds
+# the heavier I2V-A14B path for users with more headroom. Q8 variants and full
+# Diffusers builds stay available as a-la-carte starters.
+wan_t2v_bundle: list[StarterModel] = [
+ wan_22_t5_encoder,
+ wan_22_a14b_vae,
+ wan_22_5b_vae,
+ wan_22_ti2v_5b_gguf_q4_k_m,
+ wan_22_t2v_a14b_gguf_q4_k_m,
+ wan_22_t2v_a14b_low_gguf_q4_k_m,
+ wan_22_t2v_lightning_high,
+ wan_22_t2v_lightning_low,
+]
+wan_i2v_bundle: list[StarterModel] = [
+ wan_22_t5_encoder,
+ wan_22_a14b_vae,
+ wan_22_i2v_a14b_gguf_q4_k_m,
+ wan_22_i2v_a14b_low_gguf_q4_k_m,
+ wan_22_i2v_lightning_high,
+ wan_22_i2v_lightning_low,
+]
+
STARTER_BUNDLES: dict[str, StarterModelBundle] = {
BaseModelType.StableDiffusion1: StarterModelBundle(name="Stable Diffusion 1.5", models=sd1_bundle),
BaseModelType.StableDiffusionXL: StarterModelBundle(name="SDXL", models=sdxl_bundle),
@@ -1869,6 +2138,8 @@ def _gemini_3_resolution_presets(
BaseModelType.ZImage: StarterModelBundle(name="Z-Image Turbo", models=zimage_bundle),
BaseModelType.QwenImage: StarterModelBundle(name="Qwen Image", models=qwen_image_bundle),
BaseModelType.Anima: StarterModelBundle(name="Anima", models=anima_bundle),
+ "wan_t2v": StarterModelBundle(name="Wan 2.2 Text-to-Video", models=wan_t2v_bundle),
+ "wan_i2v": StarterModelBundle(name="Wan 2.2 Image-to-Video", models=wan_i2v_bundle),
}
assert len(STARTER_MODELS) == len({m.source for m in STARTER_MODELS}), "Duplicate starter models"
diff --git a/invokeai/backend/model_manager/taxonomy.py b/invokeai/backend/model_manager/taxonomy.py
index a2e4e58bdc4..7f7b9f21c1e 100644
--- a/invokeai/backend/model_manager/taxonomy.py
+++ b/invokeai/backend/model_manager/taxonomy.py
@@ -58,6 +58,8 @@ class BaseModelType(str, Enum):
"""Indicates the model is associated with Qwen Image Edit 2511 model architecture."""
Anima = "anima"
"""Indicates the model is associated with Anima model architecture (Cosmos Predict2 DiT + LLM Adapter)."""
+ Wan = "wan"
+ """Indicates the model is associated with the Wan 2.2 model architecture (T2V-A14B / TI2V-5B), used for image generation at num_frames=1."""
Unknown = "unknown"
"""Indicates the model's base architecture is unknown."""
@@ -79,6 +81,7 @@ class ModelType(str, Enum):
T5Encoder = "t5_encoder"
Qwen3Encoder = "qwen3_encoder"
QwenVLEncoder = "qwen_vl_encoder"
+ WanT5Encoder = "wan_t5_encoder"
SpandrelImageToImage = "spandrel_image_to_image"
SigLIP = "siglip"
FluxRedux = "flux_redux"
@@ -93,6 +96,7 @@ class SubModelType(str, Enum):
UNet = "unet"
Transformer = "transformer"
+ Transformer2 = "transformer_2"
TextEncoder = "text_encoder"
TextEncoder2 = "text_encoder_2"
TextEncoder3 = "text_encoder_3"
@@ -165,6 +169,44 @@ class QwenImageVariantType(str, Enum):
"""Qwen Image Edit - image editing model with reference image support."""
+class WanVariantType(str, Enum):
+ """Wan 2.2 model variants.
+
+ All variants are used for image generation at num_frames=1. The A14B family
+ is a Mixture-of-Experts (high-noise + low-noise) totalling ~28B params; the
+ T2V sub-variant takes text only, while the I2V sub-variant additionally
+ conditions on a reference image (encoded by the VAE and concatenated to the
+ noise latents along the channel dim — its transformer has ``in_channels=36``
+ instead of ``16``). TI2V-5B is a single ~5B transformer with a
+ higher-compression VAE (z_dim=48).
+ """
+
+ T2V_A14B = "t2v_a14b"
+ """Wan 2.2 T2V-A14B - dual-expert MoE (text only, 16-channel Wan VAE, transformer in_channels=16)."""
+
+ I2V_A14B = "i2v_a14b"
+ """Wan 2.2 I2V-A14B - dual-expert MoE with VAE-latent reference-image conditioning (transformer in_channels=36)."""
+
+ TI2V_5B = "ti2v_5b"
+ """Wan 2.2 TI2V-5B - smaller single-transformer model with Wan2.2-VAE (48 latent channels)."""
+
+
+class WanLoRAVariantType(str, Enum):
+ """Wan 2.2 LoRA variants, identifying which model family a LoRA targets.
+
+ Detected from the LoRA's inner attention dim: A14B has ``inner_dim=5120``,
+ TI2V-5B has ``inner_dim=3072``. A14B and 5B LoRAs are NOT interchangeable —
+ applying one against the wrong main model crashes in the layer patcher
+ with a tensor-shape error.
+ """
+
+ A14B = "a14b"
+ """Targets a Wan 2.2 A14B main (T2V or I2V, inner_dim=5120)."""
+
+ Wan5B = "5b"
+ """Targets the Wan 2.2 TI2V-5B main (inner_dim=3072)."""
+
+
class Qwen3VariantType(str, Enum):
"""Qwen3 text encoder variants based on model size."""
@@ -193,6 +235,7 @@ class ModelFormat(str, Enum):
T5Encoder = "t5_encoder"
Qwen3Encoder = "qwen3_encoder"
QwenVLEncoder = "qwen_vl_encoder"
+ WanT5Encoder = "wan_t5_encoder"
BnbQuantizedLlmInt8b = "bnb_quantized_int8b"
BnbQuantizednf4b = "bnb_quantized_nf4b"
GGUFQuantized = "gguf_quantized"
@@ -248,6 +291,8 @@ class FluxLoRAFormat(str, Enum):
Flux2VariantType,
ZImageVariantType,
QwenImageVariantType,
+ WanVariantType,
+ WanLoRAVariantType,
Qwen3VariantType,
]
variant_type_adapter = TypeAdapter[
@@ -257,6 +302,8 @@ class FluxLoRAFormat(str, Enum):
| Flux2VariantType
| ZImageVariantType
| QwenImageVariantType
+ | WanVariantType
+ | WanLoRAVariantType
| Qwen3VariantType
](
ModelVariantType
@@ -265,5 +312,7 @@ class FluxLoRAFormat(str, Enum):
| Flux2VariantType
| ZImageVariantType
| QwenImageVariantType
+ | WanVariantType
+ | WanLoRAVariantType
| Qwen3VariantType
)
diff --git a/invokeai/backend/patches/lora_conversions/anima_lora_constants.py b/invokeai/backend/patches/lora_conversions/anima_lora_constants.py
index 380e31998a7..5a54de82e86 100644
--- a/invokeai/backend/patches/lora_conversions/anima_lora_constants.py
+++ b/invokeai/backend/patches/lora_conversions/anima_lora_constants.py
@@ -17,7 +17,10 @@
# in ``anima_lora_conversion_utils``) to avoid circular imports.
# ---------------------------------------------------------------------------
-# Cosmos DiT subcomponent names unique to the Anima / Cosmos Predict2 architecture.
+# Cosmos DiT subcomponent names that ALSO appear in Wan (cross_attn, self_attn)
+# plus those unique to Cosmos. Used by ``anima_lora_conversion_utils`` to find
+# block layers during state-dict conversion, where the architecture is already
+# known to be Anima.
_COSMOS_DIT_SUBCOMPONENTS_RE = r"(cross_attn|self_attn|mlp|adaln_modulation)"
# Kohya format: lora_unet_[llm_adapter_]blocks_N_
@@ -29,17 +32,53 @@
)
+# Subcomponents *uniquely* identifying Anima/Cosmos DiT: ``mlp`` and
+# ``adaln_modulation`` (Wan calls those ``ffn`` and ``modulation`` respectively),
+# plus the Cosmos attention naming with a ``_proj`` suffix on the projection
+# letter (Wan native uses bare ``.q``/``.k``/``.v``/``.o`` — no ``_proj``).
+#
+# Used by the probe in ``configs/lora.py`` to make Anima-LoRA detection
+# *mutually exclusive* with Wan-LoRA detection: a state dict carrying only
+# ``cross_attn.q`` / ``ffn.0`` (Wan native) will NOT match here, regardless of
+# the order configs are tried.
+_COSMOS_DIT_EXCLUSIVE_SUBCOMPONENTS_RE = (
+ r"(mlp|adaln_modulation|"
+ r"(?:cross|self)_attn[._](?:[qkv]_proj|output_proj))"
+)
+
+_KOHYA_ANIMA_STRICT_RE = re.compile(r"lora_unet_(llm_adapter_)?blocks_\d+_" + _COSMOS_DIT_EXCLUSIVE_SUBCOMPONENTS_RE)
+_PEFT_ANIMA_STRICT_RE = re.compile(
+ r"(diffusion_model|transformer|base_model\.model\.transformer)\.blocks\.\d+\."
+ + _COSMOS_DIT_EXCLUSIVE_SUBCOMPONENTS_RE
+)
+
+
def has_cosmos_dit_kohya_keys(str_keys: list[str]) -> bool:
- """Check for Kohya-style keys targeting Cosmos DiT blocks with specific subcomponents.
+ """Loose detector — matches any Cosmos-shaped block submodule including
+ those whose names collide with Wan (``cross_attn``, ``self_attn``).
- Requires both the ``lora_unet_[llm_adapter_]blocks_N_`` prefix **and** a
- Cosmos DiT subcomponent name (cross_attn, self_attn, mlp, adaln_modulation)
- to avoid false-positives on other architectures that might also use bare
- ``blocks`` in their key paths.
+ For probe disambiguation between Anima and Wan, prefer
+ ``has_cosmos_dit_kohya_keys_strict``. This loose form is still useful
+ inside the Anima conversion utility, where the architecture is already
+ confirmed to be Anima and we just need to enumerate matching layers.
"""
return any(_KOHYA_ANIMA_RE.search(k) is not None for k in str_keys)
def has_cosmos_dit_peft_keys(str_keys: list[str]) -> bool:
- """Check for diffusers PEFT keys targeting Cosmos DiT blocks with specific subcomponents."""
+ """Loose PEFT-format detector — see ``has_cosmos_dit_kohya_keys`` docstring."""
return any(_PEFT_ANIMA_RE.search(k) is not None for k in str_keys)
+
+
+def has_cosmos_dit_kohya_keys_strict(str_keys: list[str]) -> bool:
+ """Strict Kohya detector requiring an Anima-exclusive submodule (``mlp``,
+ ``adaln_modulation``, or Cosmos's ``_proj``-suffixed attention names).
+
+ Mutually exclusive with the Wan LoRA probe — no Wan LoRA can satisfy this.
+ """
+ return any(_KOHYA_ANIMA_STRICT_RE.search(k) is not None for k in str_keys)
+
+
+def has_cosmos_dit_peft_keys_strict(str_keys: list[str]) -> bool:
+ """Strict PEFT detector. See ``has_cosmos_dit_kohya_keys_strict`` docstring."""
+ return any(_PEFT_ANIMA_STRICT_RE.search(k) is not None for k in str_keys)
diff --git a/invokeai/backend/patches/lora_conversions/wan_lora_constants.py b/invokeai/backend/patches/lora_conversions/wan_lora_constants.py
new file mode 100644
index 00000000000..c7a6859d6f0
--- /dev/null
+++ b/invokeai/backend/patches/lora_conversions/wan_lora_constants.py
@@ -0,0 +1,174 @@
+# Wan 2.2 LoRA prefix constants and key-shape detection helpers.
+#
+# Wan LoRAs come in three shapes in the wild:
+#
+# 1. **Diffusers PEFT** (HF naming), with or without a "transformer." prefix:
+# blocks.0.attn1.to_q.lora_A.weight
+# transformer.blocks.0.attn1.to_q.lora_A.weight
+#
+# 2. **Native upstream PEFT** (ComfyUI / Wan-AI checkpoint naming) with
+# "diffusion_model." or "transformer." prefix:
+# diffusion_model.blocks.0.self_attn.q.lora_A.weight
+# transformer.blocks.0.cross_attn.k.lora_A.weight
+#
+# 3. **Kohya**, with the standard ``lora_unet_blocks__`` shape,
+# in either diffusers naming (``attn1_to_q``) or native naming (``self_attn_q``):
+# lora_unet_blocks_0_attn1_to_q.lora_down.weight
+# lora_unet_blocks_0_self_attn_q.lora_down.weight
+#
+# The detection helpers below are shared with ``configs/lora.py`` so the probe
+# and the conversion code agree on what counts as a Wan LoRA. They keep this
+# file circular-import-free.
+
+import re
+
+from invokeai.backend.model_manager.taxonomy import WanLoRAVariantType
+
+# Prefix for Wan transformer LoRA layers in the ModelPatchRaw layer dict.
+# Same convention as Anima / QwenImage — the LayerPatcher uses this prefix to
+# resolve patches against the loaded transformer's parameter paths.
+WAN_LORA_TRANSFORMER_PREFIX = "lora_transformer-"
+
+
+# Diffusers Wan-specific submodules: attn1/attn2 (self/cross attention with
+# to_q/to_k/to_v/to_out.0 children) and ffn.net (gated FFN). These are unique
+# to WanTransformer3DModel — none of FLUX (double_blocks/single_blocks),
+# QwenImage (transformer_blocks.X.attn), Z-Image (diffusion_model.layers),
+# or Anima/Cosmos (mlp + adaln_modulation) produce this combination.
+_WAN_DIFFUSERS_SUBMODULES = r"(attn1\.|attn2\.|ffn\.net\.)"
+
+# Native upstream Wan submodules. self_attn / cross_attn collide with Anima's
+# Cosmos DiT naming, so we look for the bare ``.q``/``.k``/``.v``/``.o``
+# projection suffix (no ``_proj`` tail) AND/OR the ``ffn.`` MLP layout —
+# Anima uses ``mlp`` instead, so this is mutually exclusive.
+_WAN_NATIVE_SUBMODULES = r"(self_attn\.[qkvo](\.|$)|cross_attn\.[qkvo](\.|$)|ffn\.\d+\.)"
+
+# Anti-patterns: keys that would indicate Anima/Cosmos (mlp / adaln_modulation /
+# the ``q_proj`` projection naming Cosmos uses on its attention blocks),
+# QwenImage (transformer_blocks), Flux (double_blocks / single_blocks), or
+# Z-Image (diffusion_model.layers). If any of these are present, the LoRA is
+# NOT Wan.
+_ANIMA_ANTI_RE = re.compile(r"blocks[\._]\d+[\._](mlp|adaln_modulation)")
+# Anima Cosmos attention uses ``q_proj`` / ``k_proj`` / ``v_proj`` / ``output_proj``
+# under self_attn/cross_attn. Wan native uses just ``q``/``k``/``v``/``o`` — so
+# the ``_proj`` suffix on a self_attn/cross_attn child is a definitive Anima tell,
+# in both Kohya (``self_attn_q_proj``) and PEFT (``self_attn.q_proj``) forms.
+_ANIMA_ATTN_ANTI_RE = re.compile(r"(self_attn|cross_attn)[\._]([qkv]_proj|output_proj)")
+_QWEN_ANTI_RE = re.compile(r"(^|\.)transformer_blocks\.\d+\.")
+_FLUX_ANTI_RE = re.compile(r"(^|\.|_)(double_blocks|single_blocks|single_transformer_blocks)[\._]\d+")
+_Z_IMAGE_ANTI_RE = re.compile(r"diffusion_model\.layers\.\d+\.")
+
+
+# Kohya format: lora_unet_blocks__(attn1_to_X | ffn_N | (self|cross)_attn_X
+# where X is a single q/k/v/o letter). The strict alphabet on the attention
+# child keeps us from matching Anima's ``cross_attn_q_proj`` (which has an
+# additional ``_proj`` segment).
+_KOHYA_WAN_RE = re.compile(
+ r"lora_unet_blocks_\d+_"
+ r"(attn[12]_(to_[qkv]|to_out_0|norm_[qk])"
+ r"|(self_attn|cross_attn)_[qkvo](_|\.|$)"
+ r"|ffn_(\d+|net_\d+_proj|net_\d+))"
+)
+
+# PEFT format: .blocks..
+# Prefix may be empty, "transformer.", "diffusion_model.", or "base_model.model.transformer."
+_PEFT_WAN_DIFFUSERS_RE = re.compile(
+ r"(?:^|(?:diffusion_model|transformer|base_model\.model\.transformer)\.)blocks\.\d+\." + _WAN_DIFFUSERS_SUBMODULES
+)
+_PEFT_WAN_NATIVE_RE = re.compile(
+ r"(?:^|(?:diffusion_model|transformer|base_model\.model\.transformer)\.)blocks\.\d+\." + _WAN_NATIVE_SUBMODULES
+)
+
+
+def has_wan_kohya_keys(str_keys: list[str]) -> bool:
+ """Kohya-style keys naming Wan submodules (attn1/attn2/self_attn/cross_attn/ffn)."""
+ return any(_KOHYA_WAN_RE.search(k) is not None for k in str_keys)
+
+
+def has_wan_peft_keys(str_keys: list[str]) -> bool:
+ """Diffusers PEFT keys naming Wan submodules in either diffusers or native layout."""
+ for k in str_keys:
+ if _PEFT_WAN_DIFFUSERS_RE.search(k) is not None:
+ return True
+ if _PEFT_WAN_NATIVE_RE.search(k) is not None:
+ return True
+ return False
+
+
+def detect_wan_lora_variant(state_dict: dict) -> WanLoRAVariantType | None:
+ """Inspect a Wan LoRA state dict and guess which model family it targets.
+
+ A14B has inner_dim=5120; TI2V-5B has inner_dim=3072. Every transformer
+ block's ``attn1.to_q`` (or native ``self_attn.q``) LoRA pair has weights
+ shaped against the inner dim — ``lora_up.weight`` is ``[inner_dim, rank]``
+ and ``lora_down.weight`` is ``[rank, inner_dim]``. The larger dim of
+ either is the inner dim.
+
+ Returns:
+ ``WanLoRAVariantType.A14B`` if inner_dim == 5120,
+ ``WanLoRAVariantType.Wan5B`` if inner_dim == 3072,
+ ``None`` if no recognisable attn weight is found or inner_dim is
+ ambiguous (e.g. LoRA that only patches FFN at non-standard rank).
+ """
+ # Probe several common key shapes — diffusers PEFT (lora_A/lora_B),
+ # native Kohya naming (lora_up/lora_down), with or without a
+ # diffusion_model/transformer prefix, in diffusers or native attn
+ # naming. The first matching tensor is enough.
+ candidate_suffixes = (
+ # diffusers PEFT
+ ".attn1.to_q.lora_A.weight",
+ ".attn1.to_q.lora_B.weight",
+ ".self_attn.q.lora_A.weight",
+ ".self_attn.q.lora_B.weight",
+ # native (Kohya) PEFT
+ ".attn1.to_q.lora_up.weight",
+ ".attn1.to_q.lora_down.weight",
+ ".self_attn.q.lora_up.weight",
+ ".self_attn.q.lora_down.weight",
+ )
+ kohya_substrings = (
+ "_attn1_to_q.lora_up.weight",
+ "_attn1_to_q.lora_down.weight",
+ "_self_attn_q.lora_up.weight",
+ "_self_attn_q.lora_down.weight",
+ )
+
+ for key, tensor in state_dict.items():
+ if not isinstance(key, str):
+ continue
+ match_suffix = any(key.endswith(suffix) for suffix in candidate_suffixes)
+ match_kohya = any(needle in key for needle in kohya_substrings)
+ if not (match_suffix or match_kohya):
+ continue
+ shape = getattr(tensor, "shape", None)
+ if shape is None or len(shape) < 2:
+ continue
+ inner_dim = max(int(shape[0]), int(shape[1]))
+ if inner_dim == 5120:
+ return WanLoRAVariantType.A14B
+ if inner_dim == 3072:
+ return WanLoRAVariantType.Wan5B
+ # Any other inner_dim is uncharted — bail rather than guess.
+ return None
+
+ return None
+
+
+def has_non_wan_architecture_keys(str_keys: list[str]) -> bool:
+ """True if any key indicates a non-Wan architecture (Anima, Qwen, Flux, Z-Image).
+
+ Used as an exclusion guard — a Wan LoRA should never carry these patterns,
+ so finding them is grounds to reject the Wan probe.
+ """
+ for k in str_keys:
+ if _ANIMA_ANTI_RE.search(k) is not None:
+ return True
+ if _ANIMA_ATTN_ANTI_RE.search(k) is not None:
+ return True
+ if _QWEN_ANTI_RE.search(k) is not None:
+ return True
+ if _FLUX_ANTI_RE.search(k) is not None:
+ return True
+ if _Z_IMAGE_ANTI_RE.search(k) is not None:
+ return True
+ return False
diff --git a/invokeai/backend/patches/lora_conversions/wan_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/wan_lora_conversion_utils.py
new file mode 100644
index 00000000000..5592572b246
--- /dev/null
+++ b/invokeai/backend/patches/lora_conversions/wan_lora_conversion_utils.py
@@ -0,0 +1,250 @@
+"""Wan 2.2 LoRA conversion utilities.
+
+Wan LoRAs target the ``WanTransformer3DModel`` attention and FFN layers. We
+normalise every supported source layout to the diffusers parameter-path naming
+the loaded model uses at runtime (``blocks..attn1.to_q``,
+``blocks..attn2.to_k``, ``blocks..ffn.net.0.proj``, etc.).
+
+Supported source layouts:
+
+- **Diffusers PEFT**: ``[transformer.|base_model.model.transformer.]blocks.X.attn1.to_q.lora_A.weight``
+- **Native PEFT** (ComfyUI / Wan-AI native naming, with diffusion_model or transformer prefix):
+ ``diffusion_model.blocks.X.self_attn.q.lora_A.weight``
+- **Kohya** in either naming: ``lora_unet_blocks_X_attn1_to_q.lora_down.weight``
+ or ``lora_unet_blocks_X_self_attn_q.lora_down.weight``
+"""
+
+import re
+from typing import Dict
+
+import torch
+
+from invokeai.backend.patches.layers.base_layer_patch import BaseLayerPatch
+from invokeai.backend.patches.layers.utils import any_lora_layer_from_state_dict
+from invokeai.backend.patches.lora_conversions.wan_lora_constants import (
+ WAN_LORA_TRANSFORMER_PREFIX,
+ has_wan_kohya_keys,
+)
+from invokeai.backend.patches.model_patch_raw import ModelPatchRaw
+
+# Kohya layer-name regex: lora_unet_blocks__
+_KOHYA_KEY_REGEX = re.compile(r"lora_unet_blocks_(\d+)_(.*)")
+
+
+# Kohya submodule name -> diffusers parameter-path tail.
+#
+# Longest-match-first ordering matters because some keys are prefixes of others
+# (e.g. ``attn1_to_q`` vs ``attn1_to_out_0``). The lookup is exact (not prefix),
+# so this is purely cosmetic, but kept consistent with QwenImage's convention.
+_KOHYA_SUBMODULE_MAP: list[tuple[str, str]] = [
+ # --- Diffusers naming ---
+ # Self-attention (attn1)
+ ("attn1_to_q", "attn1.to_q"),
+ ("attn1_to_k", "attn1.to_k"),
+ ("attn1_to_v", "attn1.to_v"),
+ ("attn1_to_out_0", "attn1.to_out.0"),
+ ("attn1_norm_q", "attn1.norm_q"),
+ ("attn1_norm_k", "attn1.norm_k"),
+ # Cross-attention (attn2)
+ ("attn2_to_q", "attn2.to_q"),
+ ("attn2_to_k", "attn2.to_k"),
+ ("attn2_to_v", "attn2.to_v"),
+ ("attn2_to_out_0", "attn2.to_out.0"),
+ ("attn2_norm_q", "attn2.norm_q"),
+ ("attn2_norm_k", "attn2.norm_k"),
+ # FFN diffusers
+ ("ffn_net_0_proj", "ffn.net.0.proj"),
+ ("ffn_net_2", "ffn.net.2"),
+ # --- Native naming (mapped onto diffusers paths) ---
+ # self_attn -> attn1
+ ("self_attn_q", "attn1.to_q"),
+ ("self_attn_k", "attn1.to_k"),
+ ("self_attn_v", "attn1.to_v"),
+ ("self_attn_o", "attn1.to_out.0"),
+ ("self_attn_norm_q", "attn1.norm_q"),
+ ("self_attn_norm_k", "attn1.norm_k"),
+ # cross_attn -> attn2
+ ("cross_attn_q", "attn2.to_q"),
+ ("cross_attn_k", "attn2.to_k"),
+ ("cross_attn_v", "attn2.to_v"),
+ ("cross_attn_o", "attn2.to_out.0"),
+ ("cross_attn_norm_q", "attn2.norm_q"),
+ ("cross_attn_norm_k", "attn2.norm_k"),
+ # FFN native
+ ("ffn_0", "ffn.net.0.proj"),
+ ("ffn_2", "ffn.net.2"),
+]
+
+
+# Layer-path rules used for PEFT-style keys: applied as substring replacements
+# to the *layer path* (everything between an optional prefix and the LoRA suffix).
+# Order matters — see ``convert_wan_transformer_to_diffusers`` in diffusers for
+# the equivalent state-dict-key rules. We use trailing-dot semantics so e.g.
+# ``.q.`` matches ``self_attn.q.something`` but not ``norm_q``.
+#
+# Paths are augmented with a sentinel trailing ``.`` before applying these
+# rules so that bare endings like ``blocks.0.self_attn.q`` get rewritten as
+# ``blocks.0.attn1.to_q``.
+_NATIVE_TO_DIFFUSERS_PATH_RULES: tuple[tuple[str, str], ...] = (
+ ("cross_attn.", "attn2."),
+ ("self_attn.", "attn1."),
+ (".o.", ".to_out.0."),
+ (".q.", ".to_q."),
+ (".k.", ".to_k."),
+ (".v.", ".to_v."),
+ ("ffn.0.", "ffn.net.0.proj."),
+ ("ffn.2.", "ffn.net.2."),
+)
+
+# Prefixes seen on PEFT-style Wan LoRA keys.
+_PEFT_PREFIXES_TO_STRIP: tuple[str, ...] = (
+ "base_model.model.transformer.",
+ "transformer.",
+ "diffusion_model.",
+)
+
+
+def lora_model_from_wan_state_dict(state_dict: Dict[str, torch.Tensor], alpha: float | None = None) -> ModelPatchRaw:
+ """Convert any supported Wan LoRA state dict into a ``ModelPatchRaw``.
+
+ Detects Kohya vs PEFT layouts and dispatches accordingly. Layer paths in
+ the returned patch use diffusers naming (``blocks.X.attn1.to_q``) prefixed
+ with ``WAN_LORA_TRANSFORMER_PREFIX`` so the runtime ``LayerPatcher`` can
+ match them against ``WanTransformer3DModel`` parameters.
+ """
+ str_keys = [k for k in state_dict.keys() if isinstance(k, str)]
+ if has_wan_kohya_keys(str_keys):
+ return _convert_kohya_format(state_dict, alpha)
+ return _convert_peft_format(state_dict, alpha)
+
+
+def _convert_kohya_format(state_dict: Dict[str, torch.Tensor], alpha: float | None) -> ModelPatchRaw:
+ """Convert a Kohya-format Wan LoRA state dict.
+
+ Keys look like ``lora_unet_blocks__.{lora_down,lora_up,alpha}.weight``.
+ Unrecognised submodules are silently skipped (logged at conversion debug level
+ by the layer factory if needed).
+ """
+ layers: dict[str, BaseLayerPatch] = {}
+ grouped = _group_by_layer(state_dict)
+
+ for kohya_layer, layer_dict in grouped.items():
+ path = _kohya_layer_to_diffusers_path(kohya_layer)
+ if path is None:
+ continue
+ values = _normalize_lora_param_names(layer_dict, alpha)
+ layers[f"{WAN_LORA_TRANSFORMER_PREFIX}{path}"] = any_lora_layer_from_state_dict(values)
+
+ return ModelPatchRaw(layers=layers)
+
+
+def _convert_peft_format(state_dict: Dict[str, torch.Tensor], alpha: float | None) -> ModelPatchRaw:
+ """Convert a Diffusers-PEFT or native-PEFT Wan LoRA state dict."""
+ layers: dict[str, BaseLayerPatch] = {}
+ grouped = _group_by_layer(state_dict)
+
+ for raw_layer_key, layer_dict in grouped.items():
+ stripped = _strip_peft_prefix(raw_layer_key)
+ path = _native_layer_path_to_diffusers(stripped)
+ if path is None:
+ continue
+ values = _normalize_lora_param_names(layer_dict, alpha)
+ layers[f"{WAN_LORA_TRANSFORMER_PREFIX}{path}"] = any_lora_layer_from_state_dict(values)
+
+ return ModelPatchRaw(layers=layers)
+
+
+def _kohya_layer_to_diffusers_path(kohya_layer: str) -> str | None:
+ """``lora_unet_blocks_0_self_attn_q`` -> ``blocks.0.attn1.to_q``."""
+ m = _KOHYA_KEY_REGEX.match(kohya_layer)
+ if not m:
+ return None
+ block_idx = m.group(1)
+ sub = m.group(2)
+ for kohya_sub, diffusers_sub in _KOHYA_SUBMODULE_MAP:
+ if sub == kohya_sub:
+ return f"blocks.{block_idx}.{diffusers_sub}"
+ return None
+
+
+def _strip_peft_prefix(layer_key: str) -> str:
+ """Strip ``transformer.``, ``diffusion_model.``, ``base_model.model.transformer.`` if present."""
+ for prefix in _PEFT_PREFIXES_TO_STRIP:
+ if layer_key.startswith(prefix):
+ return layer_key[len(prefix) :]
+ return layer_key
+
+
+def _native_layer_path_to_diffusers(path: str) -> str | None:
+ """Rewrite a stripped PEFT layer path to diffusers naming.
+
+ No-op if the path is already in diffusers form (contains attn1./attn2./ffn.net.).
+ Returns None only if the path can't be plausibly identified as Wan.
+ """
+ if not path.startswith("blocks."):
+ return None
+
+ if "attn1." in path or "attn2." in path or "ffn.net." in path:
+ return path
+
+ # Apply the native-to-diffusers replacements with a sentinel trailing dot
+ # so rules like ``.q.`` fire on a bare-ending ``...self_attn.q``.
+ augmented = path + "."
+ for needle, replacement in _NATIVE_TO_DIFFUSERS_PATH_RULES:
+ augmented = augmented.replace(needle, replacement)
+ return augmented.rstrip(".")
+
+
+def _normalize_lora_param_names(layer_dict: dict[str, torch.Tensor], alpha: float | None) -> dict[str, torch.Tensor]:
+ """Map PEFT-style ``lora_A``/``lora_B`` to ``lora_down``/``lora_up``.
+
+ Kohya-style ``lora_down``/``lora_up`` pass through unchanged.
+ """
+ if "lora_A.weight" in layer_dict:
+ values: dict[str, torch.Tensor] = {
+ "lora_down.weight": layer_dict["lora_A.weight"],
+ "lora_up.weight": layer_dict["lora_B.weight"],
+ }
+ if alpha is not None:
+ values["alpha"] = torch.tensor(alpha)
+ if "alpha" in layer_dict:
+ values["alpha"] = layer_dict["alpha"]
+ if "dora_scale" in layer_dict:
+ values["dora_scale"] = layer_dict["dora_scale"]
+ return values
+ return layer_dict
+
+
+def _group_by_layer(state_dict: Dict[str, torch.Tensor]) -> dict[str, dict[str, torch.Tensor]]:
+ """Group state-dict keys by their layer path (everything before the LoRA-suffix tail)."""
+ grouped: dict[str, dict[str, torch.Tensor]] = {}
+
+ known_suffixes = [
+ ".lora_A.weight",
+ ".lora_B.weight",
+ ".lora_down.weight",
+ ".lora_up.weight",
+ ".dora_scale",
+ ".alpha",
+ ]
+
+ for key in state_dict:
+ if not isinstance(key, str):
+ continue
+
+ layer_name = None
+ key_name = None
+ for suffix in known_suffixes:
+ if key.endswith(suffix):
+ layer_name = key[: -len(suffix)]
+ key_name = suffix[1:] # drop leading dot
+ break
+
+ if layer_name is None:
+ parts = key.rsplit(".", maxsplit=2)
+ layer_name = parts[0]
+ key_name = ".".join(parts[1:])
+
+ grouped.setdefault(layer_name, {})[key_name] = state_dict[key]
+
+ return grouped
diff --git a/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py b/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py
index 6a9959f1e87..2274b34890b 100644
--- a/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py
+++ b/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py
@@ -130,6 +130,27 @@ def to(self, device: torch.device | None = None, dtype: torch.dtype | None = Non
return self
+@dataclass
+class WanConditioningInfo:
+ """Wan 2.2 text conditioning information from the UMT5-XXL encoder.
+
+ The Wan transformer takes the encoder's last hidden state directly as
+ cross-attention context (``encoder_hidden_states``).
+ """
+
+ prompt_embeds: torch.Tensor
+ """UMT5-XXL hidden states. Shape: (seq_len, hidden_size) where hidden_size=4096."""
+
+ prompt_attention_mask: torch.Tensor | None = None
+ """Attention mask marking valid (non-padding) tokens. Shape: (seq_len,). 1 for valid, 0 for padding."""
+
+ def to(self, device: torch.device | None = None, dtype: torch.dtype | None = None):
+ self.prompt_embeds = self.prompt_embeds.to(device=device, dtype=dtype)
+ if self.prompt_attention_mask is not None:
+ self.prompt_attention_mask = self.prompt_attention_mask.to(device=device)
+ return self
+
+
@dataclass
class ConditioningFieldData:
# If you change this class, adding more types, you _must_ update the instantiation of ObjectSerializerDisk in
@@ -144,6 +165,7 @@ class ConditioningFieldData:
| List[ZImageConditioningInfo]
| List[QwenImageConditioningInfo]
| List[AnimaConditioningInfo]
+ | List[WanConditioningInfo]
)
diff --git a/invokeai/backend/wan/__init__.py b/invokeai/backend/wan/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/invokeai/backend/wan/extensions/__init__.py b/invokeai/backend/wan/extensions/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/invokeai/backend/wan/extensions/wan_ref_image_extension.py b/invokeai/backend/wan/extensions/wan_ref_image_extension.py
new file mode 100644
index 00000000000..867325b6f10
--- /dev/null
+++ b/invokeai/backend/wan/extensions/wan_ref_image_extension.py
@@ -0,0 +1,219 @@
+"""Wan 2.2 I2V reference-image conditioning.
+
+Wan 2.2 I2V-A14B conditions on a reference image by **VAE-encoding** it and
+concatenating the resulting latents to the noise latents along the channel
+dim — its transformer has ``in_channels=36`` (16 noise + 16 ref-image latents
++ 4 first-frame mask) rather than 16.
+
+This module produces the 20-channel condition tensor ``[B, 20, T_lat, H_lat, W_lat]``
+that the denoise loop will concatenate to the 16-channel noise latents each
+step, yielding the 36-channel input the I2V transformer expects.
+
+Mirrors diffusers ``WanImageToVideoPipeline.prepare_latents`` lines 423–481
+with ``num_frames=1`` and ``expand_timesteps=False`` (the defaults for
+single-frame image generation).
+"""
+
+import torch
+import torchvision.transforms.functional as TF
+from diffusers.models.autoencoders import AutoencoderKLWan
+from PIL import Image
+
+# Wan 2.2 VAE temporal scale factor — single frame still consumes a 4-position
+# slice of the mask tensor, which is why the mask contributes 4 channels.
+_WAN_VAE_TEMPORAL_SCALE = 4
+
+
+def preprocess_reference_image(image: Image.Image, width: int, height: int) -> torch.Tensor:
+ """Resize a PIL image to (width, height) and return a normalised [-1, 1]
+ tensor of shape ``[1, 3, 1, height, width]`` ready for ``AutoencoderKLWan.encode``."""
+ if width % 8 != 0 or height % 8 != 0:
+ raise ValueError(f"Reference-image dimensions must be multiples of 8 (got {width}x{height}).")
+ resized = image.convert("RGB").resize((width, height), Image.LANCZOS)
+ # [0, 1] CHW float tensor.
+ pixel = TF.to_tensor(resized)
+ # Scale to [-1, 1] to match the Wan VAE's expected input range.
+ pixel = pixel * 2.0 - 1.0
+ # [3, H, W] -> [1, 3, 1, H, W]: add batch + temporal dims.
+ return pixel.unsqueeze(0).unsqueeze(2)
+
+
+def encode_reference_image_to_ti2v_condition(
+ image: Image.Image,
+ vae: AutoencoderKLWan,
+ width: int,
+ height: int,
+ device: torch.device,
+ dtype: torch.dtype,
+) -> torch.Tensor:
+ """Build the TI2V-5B-style reference condition tensor.
+
+ Returns shape ``[1, 48, 1, height // 16, width // 16]`` — single VAE-encoded
+ latent frame of the reference image, normalised against the VAE's
+ per-channel mean/std. TI2V-5B does **not** use the A14B 4-channel mask;
+ the mask is built inline in the denoise loop (``expand_timesteps`` path)
+ and used to blend this condition with the noisy latents at each step:
+ ``(1 - mask) * condition + mask * latents``.
+
+ Mirrors :class:`diffusers.WanImageToVideoPipeline.prepare_latents` lines
+ 423-466 with ``expand_timesteps=True``.
+
+ Wan 2.2-VAE has 16x spatial compression (vs A14B's 8x), so the latent
+ dims are ``height // 16`` and ``width // 16``.
+ """
+ vae_dtype = next(iter(vae.parameters())).dtype
+ pixel = preprocess_reference_image(image, width=width, height=height).to(device=device, dtype=vae_dtype)
+
+ with torch.inference_mode():
+ encoded = vae.encode(pixel, return_dict=False)[0]
+ latents = encoded.sample() # [1, 48, 1, H_lat, W_lat]
+
+ latents_mean = torch.tensor(vae.config.latents_mean).view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
+ latents_std = torch.tensor(vae.config.latents_std).view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
+ latent_condition = (latents - latents_mean) / latents_std
+
+ return latent_condition.to(dtype=dtype)
+
+
+def encode_reference_image_to_condition(
+ image: Image.Image,
+ vae: AutoencoderKLWan,
+ width: int,
+ height: int,
+ device: torch.device,
+ dtype: torch.dtype,
+) -> torch.Tensor:
+ """Build the 20-channel I2V condition tensor for a reference image.
+
+ Returns shape ``[1, 20, 1, height // 8, width // 8]`` (4-channel first-frame
+ mask concatenated with 16-channel VAE-encoded image latents along the
+ channel dim).
+
+ The output should later be concatenated with the 16-channel noise latents
+ inside the denoise loop to produce the 36-channel input the I2V transformer
+ expects.
+ """
+ vae_dtype = next(iter(vae.parameters())).dtype
+ pixel = preprocess_reference_image(image, width=width, height=height).to(device=device, dtype=vae_dtype)
+
+ with torch.inference_mode():
+ encoded = vae.encode(pixel, return_dict=False)[0]
+ latents = encoded.sample() # [1, 16, 1, H_lat, W_lat]
+
+ # Normalise against the VAE's per-channel mean/std, matching diffusers'
+ # ``WanImageToVideoPipeline.prepare_latents`` (lines 440-459). Note the
+ # multiplication by 1/std == division by std.
+ latents_mean = torch.tensor(vae.config.latents_mean).view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
+ latents_std = torch.tensor(vae.config.latents_std).view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
+ latent_condition = (latents - latents_mean) / latents_std
+
+ latent_condition = latent_condition.to(dtype=dtype)
+
+ # First-frame mask: at num_frames=1 every position is "the first frame"
+ # (i.e., conditioned). After the temporal-scale expansion the mask is
+ # 4 channels of ones at [1, T_lat=1, H_lat, W_lat].
+ _, _, t_lat, h_lat, w_lat = latent_condition.shape
+ mask = torch.ones(1, _WAN_VAE_TEMPORAL_SCALE, t_lat, h_lat, w_lat, device=device, dtype=dtype)
+
+ return torch.cat([mask, latent_condition], dim=1)
+
+
+def encode_reference_image_to_video_condition(
+ image: Image.Image,
+ vae: AutoencoderKLWan,
+ width: int,
+ height: int,
+ num_frames: int,
+ device: torch.device,
+ dtype: torch.dtype,
+ last_image: Image.Image | None = None,
+) -> torch.Tensor:
+ """Build the multi-frame I2V condition tensor for Wan 2.2 video generation.
+
+ Returns shape ``[1, 20, T_lat, height // 8, width // 8]`` where
+ ``T_lat = (num_frames - 1) // 4 + 1`` (the standard Wan VAE temporal
+ compression — e.g. 21 latent frames for 81 pixel frames). First 4 channels
+ are the rearranged first-frame mask, last 16 channels are the VAE-encoded
+ latents of the image+zero pseudo-video.
+
+ When ``last_image`` is given the function builds a **first-last-frame
+ (FLF2V)** condition instead: the end image is placed in the final temporal
+ slot and the mask anchors **both** the first and last latent frames, so the
+ model interpolates the motion between the two stills. With ``last_image=None``
+ it builds the standard single-reference I2V condition (frame 0 anchored,
+ the rest free).
+
+ Mirrors :class:`diffusers.WanImageToVideoPipeline.prepare_latents`
+ (``expand_timesteps=False``):
+
+ 1. The reference image is concatenated with zero pixel-frames to form a
+ ``[1, 3, num_frames, H, W]`` pseudo-video — frame 0 carries the start
+ image, frame ``num_frames - 1`` carries ``last_image`` (when given), and
+ the frames in between are zero. The model was trained against latents
+ produced this way; padding in latent space after a 1-frame VAE encode
+ would land different values.
+ 2. The VAE encodes that to ``[1, 16, T_lat, H_lat, W_lat]`` and we
+ normalise by the per-channel ``(mean, std)`` from ``vae.config``.
+ 3. The mask starts in pixel-frame space as ``[1, 1, num_frames, ...]``
+ with 1 at the anchored frame(s) and 0 elsewhere — frame 0 for plain I2V,
+ or frames 0 and ``num_frames - 1`` for FLF2V. The first frame is repeated
+ 4× then the whole thing is reshaped/transposed into ``[1, 4, T_lat, ...]``.
+
+ The denoise loop concatenates the result along the channel dim to the
+ 16-channel noise latents each step, yielding the 36-channel input the
+ Wan 2.2 I2V-A14B transformer expects.
+ """
+ if last_image is not None and num_frames <= 1:
+ raise ValueError("last_image (FLF2V) interpolation requires num_frames > 1.")
+
+ vae_dtype = next(iter(vae.parameters())).dtype
+ pixel = preprocess_reference_image(image, width=width, height=height).to(
+ device=device, dtype=vae_dtype
+ ) # [1, 3, 1, H, W]
+
+ # Pad the temporal dim with zero pixel-frames; the VAE handles temporal
+ # compression to T_lat. For FLF2V the end image takes the final slot and
+ # only the in-between frames are zero.
+ if num_frames > 1:
+ if last_image is not None:
+ last_pixel = preprocess_reference_image(last_image, width=width, height=height).to(
+ device=device, dtype=vae_dtype
+ )
+ middle_zeros = torch.zeros(1, 3, num_frames - 2, height, width, device=device, dtype=vae_dtype)
+ video_condition = torch.cat([pixel, middle_zeros, last_pixel], dim=2)
+ else:
+ zero_frames = torch.zeros(1, 3, num_frames - 1, height, width, device=device, dtype=vae_dtype)
+ video_condition = torch.cat([pixel, zero_frames], dim=2)
+ else:
+ video_condition = pixel
+
+ with torch.inference_mode():
+ encoded = vae.encode(video_condition, return_dict=False)[0]
+ latents = encoded.sample() # [1, 16, T_lat, H_lat, W_lat]
+
+ latents_mean = torch.tensor(vae.config.latents_mean).view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
+ latents_std = torch.tensor(vae.config.latents_std).view(1, -1, 1, 1, 1).to(latents.device, latents.dtype)
+ latent_condition = (latents - latents_mean) / latents_std
+
+ latent_condition = latent_condition.to(dtype=dtype)
+ _, _, t_lat, h_lat, w_lat = latent_condition.shape
+
+ # Build the mask in pixel-frame space then rearrange to the 4-channel
+ # latent-temporal form the transformer expects.
+ mask_pixel = torch.ones(1, 1, num_frames, h_lat, w_lat, device=device, dtype=dtype)
+ if num_frames > 1:
+ if last_image is not None:
+ # FLF2V: keep both the first and last frames anchored; zero the middle.
+ mask_pixel[:, :, 1:-1] = 0
+ else:
+ mask_pixel[:, :, 1:] = 0
+
+ first_frame_mask = mask_pixel[:, :, 0:1].repeat_interleave(repeats=_WAN_VAE_TEMPORAL_SCALE, dim=2)
+ mask = torch.cat([first_frame_mask, mask_pixel[:, :, 1:]], dim=2)
+ # mask is now [1, 1, _WAN_VAE_TEMPORAL_SCALE + (num_frames - 1), H_lat, W_lat]
+ # = [1, 1, num_frames + 3, H_lat, W_lat]. Total temporal positions
+ # (num_frames + 3) equals (t_lat * _WAN_VAE_TEMPORAL_SCALE) when
+ # (num_frames - 1) is divisible by 4 (the contract of num_latent_frames_for).
+ mask = mask.view(1, t_lat, _WAN_VAE_TEMPORAL_SCALE, h_lat, w_lat).transpose(1, 2)
+
+ return torch.cat([mask, latent_condition], dim=1)
diff --git a/invokeai/backend/wan/sampling_utils.py b/invokeai/backend/wan/sampling_utils.py
new file mode 100644
index 00000000000..f8e4b6f632a
--- /dev/null
+++ b/invokeai/backend/wan/sampling_utils.py
@@ -0,0 +1,82 @@
+"""Sampling utilities for Wan 2.2 image generation.
+
+Single-frame inference uses 5D ``[B, C, T=1, H, W]`` latent tensors. The
+scale factors are dictated by the model variant:
+
+* A14B — standard Wan VAE: spatial 8x, latent channels 16
+* TI2V-5B — Wan2.2-VAE: spatial 16x, latent channels 48
+"""
+
+from __future__ import annotations
+
+import torch
+
+from invokeai.backend.model_manager.taxonomy import WanVariantType
+
+
+def get_spatial_scale_factor(variant: WanVariantType) -> int:
+ """Return the VAE spatial downsampling factor for a Wan variant."""
+ if variant == WanVariantType.TI2V_5B:
+ return 16
+ return 8 # A14B and any future single-expert variant default to standard Wan VAE.
+
+
+def get_default_latent_channels(variant: WanVariantType) -> int:
+ """Return the default latent-channel count for a Wan variant.
+
+ Use the actual transformer ``in_channels`` from the loaded model when
+ possible; this helper is for cases where we need the count before the
+ transformer is on device (e.g. building the noise tensor before entering
+ the model-on-device context).
+ """
+ if variant == WanVariantType.TI2V_5B:
+ return 48
+ return 16
+
+
+def make_noise(
+ *,
+ batch_size: int,
+ latent_channels: int,
+ height: int,
+ width: int,
+ spatial_scale_factor: int,
+ device: torch.device,
+ dtype: torch.dtype,
+ seed: int,
+ num_latent_frames: int = 1,
+) -> torch.Tensor:
+ """Generate Wan-shaped noise: ``[B, C, T_lat, H/s, W/s]``.
+
+ For single-frame image generation the default ``num_latent_frames=1`` yields
+ a temporal dim of 1 (matching the original behaviour). Video generation
+ passes the latent-space frame count computed from the pixel-frame count via
+ :func:`num_latent_frames_for` (or directly).
+
+ Mirrors Anima's ``_get_noise``: noise is generated on CPU (deterministic
+ across CUDA / ROCm / MPS) and moved to ``device`` afterwards.
+ """
+ return torch.randn(
+ batch_size,
+ latent_channels,
+ num_latent_frames,
+ height // spatial_scale_factor,
+ width // spatial_scale_factor,
+ device="cpu",
+ dtype=torch.float32,
+ generator=torch.Generator(device="cpu").manual_seed(seed),
+ ).to(device=device, dtype=dtype)
+
+
+# Wan 2.2 VAE temporal compression ratio. 4 pixel-frames collapse to 1 latent-
+# temporal slice (this is also why the I2V conditioning mask has 4 channels).
+WAN_VAE_TEMPORAL_SCALE_FACTOR = 4
+
+
+def num_latent_frames_for(num_frames: int, vae_scale_factor_temporal: int = WAN_VAE_TEMPORAL_SCALE_FACTOR) -> int:
+ """Convert a pixel-frame count to latent-frame count for the Wan VAE.
+
+ Matches Diffusers ``WanPipeline.prepare_latents``: ``(num_frames - 1) // s + 1``
+ (e.g. ``81 -> 21`` for the standard Wan VAE).
+ """
+ return (num_frames - 1) // vae_scale_factor_temporal + 1
diff --git a/invokeai/frontend/web/openapi.json b/invokeai/frontend/web/openapi.json
index e2801e9e39a..07bebf6d9c4 100644
--- a/invokeai/frontend/web/openapi.json
+++ b/invokeai/frontend/web/openapi.json
@@ -801,6 +801,9 @@
{
"$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
@@ -843,6 +846,9 @@
{
"$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
@@ -861,6 +867,9 @@
{
"$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
{
"$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
@@ -876,6 +885,9 @@
{
"$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
@@ -927,6 +939,9 @@
{
"$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
{
"$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
@@ -978,6 +993,9 @@
{
"$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
{
"$ref": "#/components/schemas/TI_File_SD1_Config"
},
@@ -1125,6 +1143,9 @@
{
"$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
@@ -1167,6 +1188,9 @@
{
"$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
@@ -1185,6 +1209,9 @@
{
"$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
{
"$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
@@ -1200,6 +1227,9 @@
{
"$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
@@ -1251,6 +1281,9 @@
{
"$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
{
"$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
@@ -1302,6 +1335,9 @@
{
"$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
{
"$ref": "#/components/schemas/TI_File_SD1_Config"
},
@@ -1449,6 +1485,9 @@
{
"$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
@@ -1491,6 +1530,9 @@
{
"$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
@@ -1509,6 +1551,9 @@
{
"$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
{
"$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
@@ -1524,6 +1569,9 @@
{
"$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
@@ -1575,6 +1623,9 @@
{
"$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
{
"$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
@@ -1626,6 +1677,9 @@
{
"$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
{
"$ref": "#/components/schemas/TI_File_SD1_Config"
},
@@ -1823,6 +1877,9 @@
{
"$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
@@ -1865,6 +1922,9 @@
{
"$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
@@ -1883,6 +1943,9 @@
{
"$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
{
"$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
@@ -1898,6 +1961,9 @@
{
"$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
@@ -1949,6 +2015,9 @@
{
"$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
{
"$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
@@ -2000,6 +2069,9 @@
{
"$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
{
"$ref": "#/components/schemas/TI_File_SD1_Config"
},
@@ -2221,6 +2293,9 @@
{
"$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
@@ -2263,6 +2338,9 @@
{
"$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
@@ -2281,6 +2359,9 @@
{
"$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
{
"$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
@@ -2296,6 +2377,9 @@
{
"$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
@@ -2347,6 +2431,9 @@
{
"$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
{
"$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
@@ -2398,6 +2485,9 @@
{
"$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
{
"$ref": "#/components/schemas/TI_File_SD1_Config"
},
@@ -3439,6 +3529,9 @@
{
"$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
@@ -3481,6 +3574,9 @@
{
"$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
@@ -3499,6 +3595,9 @@
{
"$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
{
"$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
@@ -3514,6 +3613,9 @@
{
"$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
@@ -3565,6 +3667,9 @@
{
"$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
{
"$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
@@ -3616,6 +3721,9 @@
{
"$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
{
"$ref": "#/components/schemas/TI_File_SD1_Config"
},
@@ -5528,12 +5636,310 @@
]
}
},
- "/api/v1/boards/": {
- "post": {
- "tags": ["boards"],
- "summary": "Create Board",
- "description": "Creates a board for the current user",
- "operationId": "create_board",
+ "/api/v1/videos/upload": {
+ "post": {
+ "tags": ["videos"],
+ "summary": "Upload Video",
+ "description": "Uploads a video for the current user.",
+ "operationId": "upload_video",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "video_category",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "$ref": "#/components/schemas/ImageCategory",
+ "description": "The category of the video"
+ },
+ "description": "The category of the video"
+ },
+ {
+ "name": "is_intermediate",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "boolean",
+ "description": "Whether this is an intermediate video",
+ "title": "Is Intermediate"
+ },
+ "description": "Whether this is an intermediate video"
+ },
+ {
+ "name": "board_id",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The board to add this video to, if any",
+ "title": "Board Id"
+ },
+ "description": "The board to add this video to, if any"
+ },
+ {
+ "name": "session_id",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The session ID associated with this upload, if any",
+ "title": "Session Id"
+ },
+ "description": "The session ID associated with this upload, if any"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_upload_video"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "The video was uploaded successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VideoDTO"
+ }
+ }
+ }
+ },
+ "415": {
+ "description": "Video upload failed"
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/videos/i/{video_name}": {
+ "delete": {
+ "tags": ["videos"],
+ "summary": "Delete Video",
+ "operationId": "delete_video",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "video_name",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The name of the video to delete",
+ "title": "Video Name"
+ },
+ "description": "The name of the video to delete"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DeleteVideosResult"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "tags": ["videos"],
+ "summary": "Update Video",
+ "operationId": "update_video",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "video_name",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The name of the video to update",
+ "title": "Video Name"
+ },
+ "description": "The name of the video to update"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VideoRecordChanges",
+ "description": "The changes to apply to the video"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VideoDTO"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "tags": ["videos"],
+ "summary": "Get Video Dto",
+ "operationId": "get_video_dto",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "video_name",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The name of video to get",
+ "title": "Video Name"
+ },
+ "description": "The name of video to get"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VideoDTO"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/videos/delete": {
+ "post": {
+ "tags": ["videos"],
+ "summary": "Delete Videos From List",
+ "operationId": "delete_videos_from_list",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_delete_videos_from_list"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DeleteVideosResult"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
+ }
+ },
+ "/api/v1/videos/i/{video_name}/metadata": {
+ "get": {
+ "tags": ["videos"],
+ "summary": "Get Video Metadata",
+ "operationId": "get_video_metadata",
"security": [
{
"HTTPBearer": []
@@ -5541,25 +5947,32 @@
],
"parameters": [
{
- "name": "board_name",
- "in": "query",
+ "name": "video_name",
+ "in": "path",
"required": true,
"schema": {
"type": "string",
- "maxLength": 300,
- "description": "The name of the board to create",
- "title": "Board Name"
+ "description": "The name of video to get",
+ "title": "Video Name"
},
- "description": "The name of the board to create"
+ "description": "The name of video to get"
}
],
"responses": {
- "201": {
- "description": "The board was created successfully",
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/BoardDTO"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Response Get Video Metadata"
}
}
}
@@ -5575,129 +5988,83 @@
}
}
}
- },
- "get": {
- "tags": ["boards"],
- "summary": "List Boards",
- "description": "Gets a list of boards for the current user, including shared boards. Admin users see all boards.",
- "operationId": "list_boards",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
+ }
+ },
+ "/api/v1/videos/i/{video_name}/full": {
+ "head": {
+ "tags": ["videos"],
+ "summary": "Get Video Full",
+ "description": "Serves the video file with HTTP Range support so HTML5 seek/scrub works.\n\nLike the image equivalent, this endpoint is intentionally unauthenticated because browsers\nload videos via tags which cannot send Bearer tokens. Video names are UUIDs,\nproviding security through unguessability.",
+ "operationId": "get_video_full_head",
"parameters": [
{
- "name": "order_by",
- "in": "query",
- "required": false,
- "schema": {
- "$ref": "#/components/schemas/BoardRecordOrderBy",
- "description": "The attribute to order by",
- "default": "created_at"
- },
- "description": "The attribute to order by"
- },
- {
- "name": "direction",
- "in": "query",
- "required": false,
- "schema": {
- "$ref": "#/components/schemas/SQLiteDirection",
- "description": "The direction to order by",
- "default": "DESC"
- },
- "description": "The direction to order by"
- },
- {
- "name": "all",
- "in": "query",
- "required": false,
+ "name": "video_name",
+ "in": "path",
+ "required": true,
"schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Whether to list all boards",
- "title": "All"
+ "type": "string",
+ "description": "The name of video file to get",
+ "title": "Video Name"
},
- "description": "Whether to list all boards"
+ "description": "The name of video file to get"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Return the full video file",
+ "content": {
+ "video/mp4": {}
+ }
},
- {
- "name": "offset",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "description": "The page offset",
- "title": "Offset"
- },
- "description": "The page offset"
+ "404": {
+ "description": "Video not found"
},
- {
- "name": "limit",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
}
- ],
- "description": "The number of boards per page",
- "title": "Limit"
- },
- "description": "The number of boards per page"
- },
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "tags": ["videos"],
+ "summary": "Get Video Full",
+ "description": "Serves the video file with HTTP Range support so HTML5 seek/scrub works.\n\nLike the image equivalent, this endpoint is intentionally unauthenticated because browsers\nload videos via tags which cannot send Bearer tokens. Video names are UUIDs,\nproviding security through unguessability.",
+ "operationId": "get_video_full",
+ "parameters": [
{
- "name": "include_archived",
- "in": "query",
- "required": false,
+ "name": "video_name",
+ "in": "path",
+ "required": true,
"schema": {
- "type": "boolean",
- "description": "Whether or not to include archived boards in list",
- "default": false,
- "title": "Include Archived"
+ "type": "string",
+ "description": "The name of video file to get",
+ "title": "Video Name"
},
- "description": "Whether or not to include archived boards in list"
+ "description": "The name of video file to get"
}
],
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "Return the full video file",
"content": {
- "application/json": {
- "schema": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/OffsetPaginatedResults_BoardDTO_"
- },
- {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/BoardDTO"
- }
- }
- ],
- "title": "Response List Boards"
- }
- }
+ "video/mp4": {}
+ }
+ },
+ "206": {
+ "description": "Return a byte-range of the video file",
+ "content": {
+ "video/mp4": {}
}
},
+ "404": {
+ "description": "Video not found"
+ },
"422": {
"description": "Validation Error",
"content": {
@@ -5711,41 +6078,35 @@
}
}
},
- "/api/v1/boards/{board_id}": {
+ "/api/v1/videos/i/{video_name}/thumbnail": {
"get": {
- "tags": ["boards"],
- "summary": "Get Board",
- "description": "Gets a board (user must have access to it)",
- "operationId": "get_board",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
+ "tags": ["videos"],
+ "summary": "Get Video Thumbnail",
+ "description": "Returns the first-frame WebP thumbnail of a video. Unauthenticated; UUIDs provide unguessability.",
+ "operationId": "get_video_thumbnail",
"parameters": [
{
- "name": "board_id",
+ "name": "video_name",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The id of board to get",
- "title": "Board Id"
+ "description": "The name of thumbnail file to get",
+ "title": "Video Name"
},
- "description": "The id of board to get"
+ "description": "The name of thumbnail file to get"
}
],
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "Return the video thumbnail",
"content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BoardDTO"
- }
- }
+ "image/webp": {}
}
},
+ "404": {
+ "description": "Video not found"
+ },
"422": {
"description": "Validation Error",
"content": {
@@ -5757,12 +6118,13 @@
}
}
}
- },
- "patch": {
- "tags": ["boards"],
- "summary": "Update Board",
- "description": "Updates a board (user must have access to it)",
- "operationId": "update_board",
+ }
+ },
+ "/api/v1/videos/i/{video_name}/urls": {
+ "get": {
+ "tags": ["videos"],
+ "summary": "Get Video Urls",
+ "operationId": "get_video_urls",
"security": [
{
"HTTPBearer": []
@@ -5770,35 +6132,24 @@
],
"parameters": [
{
- "name": "board_id",
+ "name": "video_name",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The id of board to update",
- "title": "Board Id"
+ "description": "The name of the video whose URL to get",
+ "title": "Video Name"
},
- "description": "The id of board to update"
+ "description": "The name of the video whose URL to get"
}
],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/BoardChanges",
- "description": "The changes to apply to the board"
- }
- }
- }
- },
"responses": {
- "201": {
- "description": "The board was updated successfully",
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/BoardDTO"
+ "$ref": "#/components/schemas/VideoUrlsDTO"
}
}
}
@@ -5814,47 +6165,159 @@
}
}
}
- },
- "delete": {
- "tags": ["boards"],
- "summary": "Delete Board",
- "description": "Deletes a board (user must have access to it)",
- "operationId": "delete_board",
+ }
+ },
+ "/api/v1/videos/": {
+ "get": {
+ "tags": ["videos"],
+ "summary": "List Video Dtos",
+ "description": "Gets a list of video DTOs for the current user.",
+ "operationId": "list_video_dtos",
"security": [
{
"HTTPBearer": []
}
],
"parameters": [
+ {
+ "name": "video_origin",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ResourceOrigin"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The origin of videos to list.",
+ "title": "Video Origin"
+ },
+ "description": "The origin of videos to list."
+ },
+ {
+ "name": "categories",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ImageCategory"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories of video to include.",
+ "title": "Categories"
+ },
+ "description": "The categories of video to include."
+ },
+ {
+ "name": "is_intermediate",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to list intermediate videos.",
+ "title": "Is Intermediate"
+ },
+ "description": "Whether to list intermediate videos."
+ },
{
"name": "board_id",
- "in": "path",
- "required": true,
+ "in": "query",
+ "required": false,
"schema": {
- "type": "string",
- "description": "The id of board to delete",
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The board id to filter by. Use 'none' to find videos without a board.",
"title": "Board Id"
},
- "description": "The id of board to delete"
+ "description": "The board id to filter by. Use 'none' to find videos without a board."
},
{
- "name": "include_images",
+ "name": "offset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "description": "The page offset",
+ "default": 0,
+ "title": "Offset"
+ },
+ "description": "The page offset"
+ },
+ {
+ "name": "limit",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "description": "The number of videos per page",
+ "default": 10,
+ "title": "Limit"
+ },
+ "description": "The number of videos per page"
+ },
+ {
+ "name": "order_dir",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The order of sort",
+ "default": "DESC"
+ },
+ "description": "The order of sort"
+ },
+ {
+ "name": "starred_first",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "Whether to sort by starred videos first",
+ "default": true,
+ "title": "Starred First"
+ },
+ "description": "Whether to sort by starred videos first"
+ },
+ {
+ "name": "search_term",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
- "type": "boolean"
+ "type": "string"
},
{
"type": "null"
}
],
- "description": "Permanently delete all images on the board",
- "default": false,
- "title": "Include Images"
+ "description": "The term to search for",
+ "title": "Search Term"
},
- "description": "Permanently delete all images on the board"
+ "description": "The term to search for"
}
],
"responses": {
@@ -5863,7 +6326,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/DeleteBoardResult"
+ "$ref": "#/components/schemas/OffsetPaginatedResults_VideoDTO_"
}
}
}
@@ -5881,12 +6344,12 @@
}
}
},
- "/api/v1/boards/{board_id}/image_names": {
+ "/api/v1/videos/names": {
"get": {
- "tags": ["boards"],
- "summary": "List All Board Image Names",
- "description": "Gets a list of images for a board",
- "operationId": "list_all_board_image_names",
+ "tags": ["videos"],
+ "summary": "Get Video Names",
+ "description": "Gets ordered list of video names with metadata for optimistic updates.",
+ "operationId": "get_video_names",
"security": [
{
"HTTPBearer": []
@@ -5894,15 +6357,22 @@
],
"parameters": [
{
- "name": "board_id",
- "in": "path",
- "required": true,
+ "name": "video_origin",
+ "in": "query",
+ "required": false,
"schema": {
- "type": "string",
- "description": "The id of the board or 'none' for uncategorized images",
- "title": "Board Id"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ResourceOrigin"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The origin of videos to list.",
+ "title": "Video Origin"
},
- "description": "The id of the board or 'none' for uncategorized images"
+ "description": "The origin of videos to list."
},
{
"name": "categories",
@@ -5920,10 +6390,10 @@
"type": "null"
}
],
- "description": "The categories of image to include.",
+ "description": "The categories of video to include.",
"title": "Categories"
},
- "description": "The categories of image to include."
+ "description": "The categories of video to include."
},
{
"name": "is_intermediate",
@@ -5938,10 +6408,69 @@
"type": "null"
}
],
- "description": "Whether to list intermediate images.",
+ "description": "Whether to list intermediate videos.",
"title": "Is Intermediate"
},
- "description": "Whether to list intermediate images."
+ "description": "Whether to list intermediate videos."
+ },
+ {
+ "name": "board_id",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The board id to filter by. Use 'none' to find videos without a board.",
+ "title": "Board Id"
+ },
+ "description": "The board id to filter by. Use 'none' to find videos without a board."
+ },
+ {
+ "name": "order_dir",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The order of sort",
+ "default": "DESC"
+ },
+ "description": "The order of sort"
+ },
+ {
+ "name": "starred_first",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "Whether to sort by starred videos first",
+ "default": true,
+ "title": "Starred First"
+ },
+ "description": "Whether to sort by starred videos first"
+ },
+ {
+ "name": "search_term",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The term to search for",
+ "title": "Search Term"
+ },
+ "description": "The term to search for"
}
],
"responses": {
@@ -5950,11 +6479,7 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "title": "Response List All Board Image Names"
+ "$ref": "#/components/schemas/VideoNamesResult"
}
}
}
@@ -5972,29 +6497,28 @@
}
}
},
- "/api/v1/board_images/": {
+ "/api/v1/videos/star": {
"post": {
- "tags": ["boards"],
- "summary": "Add Image To Board",
- "description": "Creates a board_image",
- "operationId": "add_image_to_board",
+ "tags": ["videos"],
+ "summary": "Star Videos In List",
+ "operationId": "star_videos_in_list",
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_add_image_to_board"
+ "$ref": "#/components/schemas/Body_star_videos_in_list"
}
}
},
"required": true
},
"responses": {
- "201": {
- "description": "The image was added to a board successfully",
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/AddImagesToBoardResult"
+ "$ref": "#/components/schemas/StarredVideosResult"
}
}
}
@@ -6015,29 +6539,30 @@
"HTTPBearer": []
}
]
- },
- "delete": {
- "tags": ["boards"],
- "summary": "Remove Image From Board",
- "description": "Removes an image from its board, if it had one",
- "operationId": "remove_image_from_board",
+ }
+ },
+ "/api/v1/videos/unstar": {
+ "post": {
+ "tags": ["videos"],
+ "summary": "Unstar Videos In List",
+ "operationId": "unstar_videos_in_list",
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_remove_image_from_board"
+ "$ref": "#/components/schemas/Body_unstar_videos_in_list"
}
}
},
"required": true
},
"responses": {
- "201": {
- "description": "The image was removed from the board successfully",
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RemoveImagesFromBoardResult"
+ "$ref": "#/components/schemas/UnstarredVideosResult"
}
}
}
@@ -6060,29 +6585,28 @@
]
}
},
- "/api/v1/board_images/batch": {
+ "/api/v1/videos/board": {
"post": {
- "tags": ["boards"],
- "summary": "Add Images To Board",
- "description": "Adds a list of images to a board",
- "operationId": "add_images_to_board",
+ "tags": ["videos"],
+ "summary": "Add Video To Board",
+ "operationId": "add_video_to_board",
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_add_images_to_board"
+ "$ref": "#/components/schemas/VideoBoardArg"
}
}
},
"required": true
},
"responses": {
- "201": {
- "description": "Images were added to board successfully",
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/AddImagesToBoardResult"
+ "$ref": "#/components/schemas/AddVideosToBoardResult"
}
}
}
@@ -6103,31 +6627,28 @@
"HTTPBearer": []
}
]
- }
- },
- "/api/v1/board_images/batch/delete": {
- "post": {
- "tags": ["boards"],
- "summary": "Remove Images From Board",
- "description": "Removes a list of images from their board, if they had one",
- "operationId": "remove_images_from_board",
+ },
+ "delete": {
+ "tags": ["videos"],
+ "summary": "Remove Video From Board",
+ "operationId": "remove_video_from_board",
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_remove_images_from_board"
+ "$ref": "#/components/schemas/Body_remove_video_from_board"
}
}
},
"required": true
},
"responses": {
- "201": {
- "description": "Images were removed from board successfully",
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/RemoveImagesFromBoardResult"
+ "$ref": "#/components/schemas/RemoveVideosFromBoardResult"
}
}
}
@@ -6150,41 +6671,12 @@
]
}
},
- "/api/v1/virtual_boards/by_date": {
+ "/api/v1/gallery/items/": {
"get": {
- "tags": ["virtual_boards"],
- "summary": "List Virtual Boards By Date",
- "description": "Gets a list of virtual sub-boards grouped by date.",
- "operationId": "list_virtual_boards_by_date",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "items": {
- "$ref": "#/components/schemas/VirtualSubBoardDTO"
- },
- "type": "array",
- "title": "Response List Virtual Boards By Date"
- }
- }
- }
- }
- },
- "security": [
- {
- "HTTPBearer": []
- }
- ]
- }
- },
- "/api/v1/virtual_boards/by_date/{date}/image_names": {
- "get": {
- "tags": ["virtual_boards"],
- "summary": "List Virtual Board Image Names By Date",
- "description": "Gets ordered image names for a specific date.",
- "operationId": "list_virtual_board_image_names_by_date",
+ "tags": ["gallery"],
+ "summary": "List Gallery Items",
+ "description": "Returns a paginated, time-sorted stream of polymorphic gallery items (images + videos).",
+ "operationId": "list_gallery_items",
"security": [
{
"HTTPBearer": []
@@ -6192,59 +6684,126 @@
],
"parameters": [
{
- "name": "date",
- "in": "path",
- "required": true,
+ "name": "origin",
+ "in": "query",
+ "required": false,
"schema": {
- "type": "string",
- "description": "The ISO date string, e.g. '2026-03-18'",
- "title": "Date"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ResourceOrigin"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The origin of items to list.",
+ "title": "Origin"
},
- "description": "The ISO date string, e.g. '2026-03-18'"
+ "description": "The origin of items to list."
},
{
- "name": "starred_first",
+ "name": "categories",
"in": "query",
"required": false,
"schema": {
- "type": "boolean",
- "description": "Whether to sort starred images first",
- "default": true,
- "title": "Starred First"
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ImageCategory"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories to include. Shared between images and videos.",
+ "title": "Categories"
},
- "description": "Whether to sort starred images first"
+ "description": "The categories to include. Shared between images and videos."
},
{
- "name": "order_dir",
+ "name": "is_intermediate",
"in": "query",
"required": false,
"schema": {
- "$ref": "#/components/schemas/SQLiteDirection",
- "description": "The sort direction",
- "default": "DESC"
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to list intermediate items.",
+ "title": "Is Intermediate"
},
- "description": "The sort direction"
+ "description": "Whether to list intermediate items."
},
{
- "name": "categories",
+ "name": "board_id",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/ImageCategory"
- }
+ "type": "string"
},
{
"type": "null"
}
],
- "description": "The categories of images to include",
- "title": "Categories"
+ "description": "The board id to filter by. Use 'none' to find items without a board.",
+ "title": "Board Id"
},
- "description": "The categories of images to include"
+ "description": "The board id to filter by. Use 'none' to find items without a board."
+ },
+ {
+ "name": "offset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "description": "The page offset",
+ "default": 0,
+ "title": "Offset"
+ },
+ "description": "The page offset"
+ },
+ {
+ "name": "limit",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "description": "The number of items per page",
+ "default": 10,
+ "title": "Limit"
+ },
+ "description": "The number of items per page"
+ },
+ {
+ "name": "order_dir",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The order of sort",
+ "default": "DESC"
+ },
+ "description": "The order of sort"
+ },
+ {
+ "name": "starred_first",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "Whether to sort by starred items first",
+ "default": true,
+ "title": "Starred First"
+ },
+ "description": "Whether to sort by starred items first"
},
{
"name": "search_term",
@@ -6259,10 +6818,10 @@
"type": "null"
}
],
- "description": "Search term to filter images",
+ "description": "The term to search for",
"title": "Search Term"
},
- "description": "Search term to filter images"
+ "description": "The term to search for"
}
],
"responses": {
@@ -6271,7 +6830,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/ImageNamesResult"
+ "$ref": "#/components/schemas/OffsetPaginatedResults_GalleryItem_"
}
}
}
@@ -6289,12 +6848,12 @@
}
}
},
- "/api/v1/model_relationships/i/{model_key}": {
+ "/api/v1/gallery/items/names": {
"get": {
- "tags": ["model_relationships"],
- "summary": "Get Related Models",
- "description": "Get a list of model keys related to a given model.",
- "operationId": "get_related_models",
+ "tags": ["gallery"],
+ "summary": "Get Gallery Item Names",
+ "description": "Returns an ordered (kind, name) list \u2014 used to drive virtualized gallery selection.",
+ "operationId": "get_gallery_item_names",
"security": [
{
"HTTPBearer": []
@@ -6302,236 +6861,139 @@
],
"parameters": [
{
- "name": "model_key",
- "in": "path",
- "required": true,
+ "name": "origin",
+ "in": "query",
+ "required": false,
"schema": {
- "type": "string",
- "description": "The key of the model to get relationships for",
- "title": "Model Key"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ResourceOrigin"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The origin of items to list.",
+ "title": "Origin"
},
- "description": "The key of the model to get relationships for"
- }
- ],
- "responses": {
- "200": {
- "description": "A list of related model keys was retrieved successfully",
- "content": {
- "application/json": {
- "schema": {
+ "description": "The origin of items to list."
+ },
+ {
+ "name": "categories",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
"type": "array",
"items": {
- "type": "string"
- },
- "title": "Response Get Related Models"
+ "$ref": "#/components/schemas/ImageCategory"
+ }
},
- "example": [
- "15e9eb28-8cfe-47c9-b610-37907a79fc3c",
- "71272e82-0e5f-46d5-bca9-9a61f4bd8a82",
- "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2"
- ]
- }
- }
- },
- "404": {
- "description": "The specified model could not be found"
- },
- "422": {
- "description": "Validation error"
- }
- }
- }
- },
- "/api/v1/model_relationships/": {
- "post": {
- "tags": ["model_relationships"],
- "summary": "Add Model Relationship",
- "description": "Creates a **bidirectional** relationship between two models, allowing each to reference the other as related.",
- "operationId": "add_model_relationship_api_v1_model_relationships__post",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ModelRelationshipCreateRequest",
- "description": "The model keys to relate"
- }
- }
- },
- "required": true
- },
- "responses": {
- "204": {
- "description": "The relationship was successfully created"
- },
- "400": {
- "description": "Invalid model keys or self-referential relationship"
- },
- "409": {
- "description": "The relationship already exists"
- },
- "422": {
- "description": "Validation error"
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories to include. Shared between images and videos.",
+ "title": "Categories"
+ },
+ "description": "The categories to include. Shared between images and videos."
},
- "500": {
- "description": "Internal server error"
- }
- },
- "security": [
{
- "HTTPBearer": []
- }
- ]
- },
- "delete": {
- "tags": ["model_relationships"],
- "summary": "Remove Model Relationship",
- "description": "Removes a **bidirectional** relationship between two models. The relationship must already exist.",
- "operationId": "remove_model_relationship_api_v1_model_relationships__delete",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ModelRelationshipCreateRequest",
- "description": "The model keys to disconnect"
- }
- }
- },
- "required": true
- },
- "responses": {
- "204": {
- "description": "The relationship was successfully removed"
- },
- "400": {
- "description": "Invalid model keys or self-referential relationship"
- },
- "404": {
- "description": "The relationship does not exist"
- },
- "422": {
- "description": "Validation error"
+ "name": "is_intermediate",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to list intermediate items.",
+ "title": "Is Intermediate"
+ },
+ "description": "Whether to list intermediate items."
},
- "500": {
- "description": "Internal server error"
- }
- },
- "security": [
{
- "HTTPBearer": []
- }
- ]
- }
- },
- "/api/v1/model_relationships/batch": {
- "post": {
- "tags": ["model_relationships"],
- "summary": "Get Related Model Keys (Batch)",
- "description": "Retrieves all **unique related model keys** for a list of given models. This is useful for contextual suggestions or filtering.",
- "operationId": "get_related_models_batch",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ModelRelationshipBatchRequest",
- "description": "Model keys to check for related connections"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "Related model keys retrieved successfully",
- "content": {
- "application/json": {
- "schema": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Response Get Related Models Batch"
+ "name": "board_id",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
},
- "example": [
- "ca562b14-995e-4a42-90c1-9528f1a5921d",
- "cc0c2b8a-c62e-41d6-878e-cc74dde5ca8f",
- "18ca7649-6a9e-47d5-bc17-41ab1e8cec81",
- "7c12d1b2-0ef9-4bec-ba55-797b2d8f2ee1",
- "c382eaa3-0e28-4ab0-9446-408667699aeb",
- "71272e82-0e5f-46d5-bca9-9a61f4bd8a82",
- "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2"
- ]
- }
- }
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The board id to filter by. Use 'none' to find items without a board.",
+ "title": "Board Id"
+ },
+ "description": "The board id to filter by. Use 'none' to find items without a board."
},
- "422": {
- "description": "Validation error"
+ {
+ "name": "order_dir",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The order of sort",
+ "default": "DESC"
+ },
+ "description": "The order of sort"
},
- "500": {
- "description": "Internal server error"
- }
- },
- "security": [
{
- "HTTPBearer": []
- }
- ]
- }
- },
- "/api/v1/app/version": {
- "get": {
- "tags": ["app"],
- "summary": "Get Version",
- "operationId": "app_version",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/AppVersion"
+ "name": "starred_first",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "Whether to sort by starred items first",
+ "default": true,
+ "title": "Starred First"
+ },
+ "description": "Whether to sort by starred items first"
+ },
+ {
+ "name": "search_term",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
}
- }
- }
+ ],
+ "description": "The term to search for",
+ "title": "Search Term"
+ },
+ "description": "The term to search for"
}
- }
- }
- },
- "/api/v1/app/app_deps": {
- "get": {
- "tags": ["app"],
- "summary": "Get App Deps",
- "operationId": "get_app_deps",
+ ],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "additionalProperties": {
- "type": "string"
- },
- "type": "object",
- "title": "Response Get App Deps"
+ "$ref": "#/components/schemas/GalleryItemNamesResult"
}
}
}
- }
- }
- }
- },
- "/api/v1/app/patchmatch_status": {
- "get": {
- "tags": ["app"],
- "summary": "Get Patchmatch Status",
- "operationId": "get_patchmatch_status",
- "responses": {
- "200": {
- "description": "Successful Response",
+ },
+ "422": {
+ "description": "Validation Error",
"content": {
"application/json": {
"schema": {
- "type": "boolean",
- "title": "Response Get Patchmatch Status"
+ "$ref": "#/components/schemas/HTTPValidationError"
}
}
}
@@ -6539,46 +7001,38 @@
}
}
},
- "/api/v1/app/runtime_config": {
- "get": {
- "tags": ["app"],
- "summary": "Get Runtime Config",
- "operationId": "get_runtime_config",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/InvokeAIAppConfigWithSetFields"
- }
- }
- }
+ "/api/v1/boards/": {
+ "post": {
+ "tags": ["boards"],
+ "summary": "Create Board",
+ "description": "Creates a board for the current user",
+ "operationId": "create_board",
+ "security": [
+ {
+ "HTTPBearer": []
}
- }
- },
- "patch": {
- "tags": ["app"],
- "summary": "Update Runtime Config",
- "operationId": "update_runtime_config",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/UpdateAppGenerationSettingsRequest",
- "description": "Writable runtime configuration changes"
- }
- }
- },
- "required": true
- },
+ ],
+ "parameters": [
+ {
+ "name": "board_name",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "maxLength": 300,
+ "description": "The name of the board to create",
+ "title": "Board Name"
+ },
+ "description": "The name of the board to create"
+ }
+ ],
"responses": {
- "200": {
- "description": "Successful Response",
+ "201": {
+ "description": "The board was created successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/InvokeAIAppConfigWithSetFields"
+ "$ref": "#/components/schemas/BoardDTO"
}
}
}
@@ -6593,65 +7047,13 @@
}
}
}
- },
- "security": [
- {
- "HTTPBearer": []
- }
- ]
- }
- },
- "/api/v1/app/external_providers/status": {
- "get": {
- "tags": ["app"],
- "summary": "Get External Provider Statuses",
- "operationId": "get_external_provider_statuses",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "items": {
- "$ref": "#/components/schemas/ExternalProviderStatusModel"
- },
- "type": "array",
- "title": "Response Get External Provider Statuses"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/app/external_providers/config": {
- "get": {
- "tags": ["app"],
- "summary": "Get External Provider Configs",
- "operationId": "get_external_provider_configs",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "items": {
- "$ref": "#/components/schemas/ExternalProviderConfigModel"
- },
- "type": "array",
- "title": "Response Get External Provider Configs"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/app/external_providers/config/{provider_id}": {
- "post": {
- "tags": ["app"],
- "summary": "Set External Provider Config",
- "operationId": "set_external_provider_config",
+ }
+ },
+ "get": {
+ "tags": ["boards"],
+ "summary": "List Boards",
+ "description": "Gets a list of boards for the current user, including shared boards. Admin users see all boards.",
+ "operationId": "list_boards",
"security": [
{
"HTTPBearer": []
@@ -6659,35 +7061,112 @@
],
"parameters": [
{
- "name": "provider_id",
- "in": "path",
- "required": true,
+ "name": "order_by",
+ "in": "query",
+ "required": false,
"schema": {
- "type": "string",
- "description": "The external provider identifier",
- "title": "Provider Id"
+ "$ref": "#/components/schemas/BoardRecordOrderBy",
+ "description": "The attribute to order by",
+ "default": "created_at"
},
- "description": "The external provider identifier"
+ "description": "The attribute to order by"
+ },
+ {
+ "name": "direction",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The direction to order by",
+ "default": "DESC"
+ },
+ "description": "The direction to order by"
+ },
+ {
+ "name": "all",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to list all boards",
+ "title": "All"
+ },
+ "description": "Whether to list all boards"
+ },
+ {
+ "name": "offset",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The page offset",
+ "title": "Offset"
+ },
+ "description": "The page offset"
+ },
+ {
+ "name": "limit",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The number of boards per page",
+ "title": "Limit"
+ },
+ "description": "The number of boards per page"
+ },
+ {
+ "name": "include_archived",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "Whether or not to include archived boards in list",
+ "default": false,
+ "title": "Include Archived"
+ },
+ "description": "Whether or not to include archived boards in list"
}
],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ExternalProviderConfigUpdate",
- "description": "External provider configuration settings"
- }
- }
- }
- },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/ExternalProviderConfigModel"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/OffsetPaginatedResults_BoardDTO_"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/BoardDTO"
+ }
+ }
+ ],
+ "title": "Response List Boards"
}
}
}
@@ -6703,11 +7182,14 @@
}
}
}
- },
- "delete": {
- "tags": ["app"],
- "summary": "Reset External Provider Config",
- "operationId": "reset_external_provider_config",
+ }
+ },
+ "/api/v1/boards/{board_id}": {
+ "get": {
+ "tags": ["boards"],
+ "summary": "Get Board",
+ "description": "Gets a board (user must have access to it)",
+ "operationId": "get_board",
"security": [
{
"HTTPBearer": []
@@ -6715,15 +7197,15 @@
],
"parameters": [
{
- "name": "provider_id",
+ "name": "board_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The external provider identifier",
- "title": "Provider Id"
+ "description": "The id of board to get",
+ "title": "Board Id"
},
- "description": "The external provider identifier"
+ "description": "The id of board to get"
}
],
"responses": {
@@ -6732,7 +7214,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/ExternalProviderConfigModel"
+ "$ref": "#/components/schemas/BoardDTO"
}
}
}
@@ -6748,147 +7230,12 @@
}
}
}
- }
- },
- "/api/v1/app/logging": {
- "get": {
- "tags": ["app"],
- "summary": "Get Log Level",
- "description": "Returns the log level",
- "operationId": "get_log_level",
- "responses": {
- "200": {
- "description": "The operation was successful",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/LogLevel"
- }
- }
- }
- }
- }
},
- "post": {
- "tags": ["app"],
- "summary": "Set Log Level",
- "description": "Sets the log verbosity level",
- "operationId": "set_log_level",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/LogLevel",
- "description": "New log verbosity level"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "The operation was successful",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/LogLevel"
- }
- }
- }
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/app/invocation_cache": {
- "delete": {
- "tags": ["app"],
- "summary": "Clear Invocation Cache",
- "description": "Clears the invocation cache",
- "operationId": "clear_invocation_cache",
- "responses": {
- "200": {
- "description": "The operation was successful",
- "content": {
- "application/json": {
- "schema": {}
- }
- }
- }
- }
- }
- },
- "/api/v1/app/invocation_cache/enable": {
- "put": {
- "tags": ["app"],
- "summary": "Enable Invocation Cache",
- "description": "Clears the invocation cache",
- "operationId": "enable_invocation_cache",
- "responses": {
- "200": {
- "description": "The operation was successful",
- "content": {
- "application/json": {
- "schema": {}
- }
- }
- }
- }
- }
- },
- "/api/v1/app/invocation_cache/disable": {
- "put": {
- "tags": ["app"],
- "summary": "Disable Invocation Cache",
- "description": "Clears the invocation cache",
- "operationId": "disable_invocation_cache",
- "responses": {
- "200": {
- "description": "The operation was successful",
- "content": {
- "application/json": {
- "schema": {}
- }
- }
- }
- }
- }
- },
- "/api/v1/app/invocation_cache/status": {
- "get": {
- "tags": ["app"],
- "summary": "Get Invocation Cache Status",
- "description": "Clears the invocation cache",
- "operationId": "get_invocation_cache_status",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/InvocationCacheStatus"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/queue/{queue_id}/enqueue_batch": {
- "post": {
- "tags": ["queue"],
- "summary": "Enqueue Batch",
- "description": "Processes a batch and enqueues the output graphs for execution for the current user.",
- "operationId": "enqueue_batch",
+ "patch": {
+ "tags": ["boards"],
+ "summary": "Update Board",
+ "description": "Updates a board (user must have access to it)",
+ "operationId": "update_board",
"security": [
{
"HTTPBearer": []
@@ -6896,15 +7243,15 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "board_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
+ "description": "The id of board to update",
+ "title": "Board Id"
},
- "description": "The queue id to perform this operation on"
+ "description": "The id of board to update"
}
],
"requestBody": {
@@ -6912,31 +7259,22 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_enqueue_batch"
+ "$ref": "#/components/schemas/BoardChanges",
+ "description": "The changes to apply to the board"
}
}
}
},
"responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/EnqueueBatchResult"
- }
- }
- }
- },
"201": {
+ "description": "The board was updated successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/EnqueueBatchResult"
+ "$ref": "#/components/schemas/BoardDTO"
}
}
- },
- "description": "Created"
+ }
},
"422": {
"description": "Validation Error",
@@ -6949,14 +7287,12 @@
}
}
}
- }
- },
- "/api/v1/queue/{queue_id}/list_all": {
- "get": {
- "tags": ["queue"],
- "summary": "List All Queue Items",
- "description": "Gets all queue items",
- "operationId": "list_all_queue_items",
+ },
+ "delete": {
+ "tags": ["boards"],
+ "summary": "Delete Board",
+ "description": "Deletes a board (user must have access to it)",
+ "operationId": "delete_board",
"security": [
{
"HTTPBearer": []
@@ -6964,33 +7300,34 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "board_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
+ "description": "The id of board to delete",
+ "title": "Board Id"
},
- "description": "The queue id to perform this operation on"
+ "description": "The id of board to delete"
},
{
- "name": "destination",
+ "name": "include_images",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
- "type": "string"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "description": "The destination of queue items to fetch",
- "title": "Destination"
+ "description": "Permanently delete all images and videos on the board",
+ "default": false,
+ "title": "Include Images"
},
- "description": "The destination of queue items to fetch"
+ "description": "Permanently delete all images and videos on the board"
}
],
"responses": {
@@ -6999,11 +7336,7 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/SessionQueueItem"
- },
- "title": "Response 200 List All Queue Items"
+ "$ref": "#/components/schemas/DeleteBoardResult"
}
}
}
@@ -7021,12 +7354,12 @@
}
}
},
- "/api/v1/queue/{queue_id}/item_ids": {
+ "/api/v1/boards/{board_id}/image_names": {
"get": {
- "tags": ["queue"],
- "summary": "Get Queue Item Ids",
- "description": "Gets all queue item ids that match the given parameters.\n\nIDs for every user's items are returned (item ids carry no sensitive data on their own).\nWhen the corresponding items are hydrated via get_queue_items_by_item_ids, those belonging\nto other users are redacted by sanitize_queue_item_for_user. This lets a non-admin see\npartially-redacted entries for other users' jobs in the queue list, while still revealing\nonly timestamps and status for items they do not own.\n\ncurrent_user is required so the endpoint stays behind authentication in multiuser mode.",
- "operationId": "get_queue_item_ids",
+ "tags": ["boards"],
+ "summary": "List All Board Image Names",
+ "description": "Gets a list of images for a board",
+ "operationId": "list_all_board_image_names",
"security": [
{
"HTTPBearer": []
@@ -7034,26 +7367,54 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "board_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
+ "description": "The id of the board or 'none' for uncategorized images",
+ "title": "Board Id"
},
- "description": "The queue id to perform this operation on"
+ "description": "The id of the board or 'none' for uncategorized images"
},
{
- "name": "order_dir",
+ "name": "categories",
"in": "query",
"required": false,
"schema": {
- "$ref": "#/components/schemas/SQLiteDirection",
- "description": "The order of sort",
- "default": "DESC"
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ImageCategory"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories of image to include.",
+ "title": "Categories"
},
- "description": "The order of sort"
+ "description": "The categories of image to include."
+ },
+ {
+ "name": "is_intermediate",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to list intermediate images.",
+ "title": "Is Intermediate"
+ },
+ "description": "Whether to list intermediate images."
}
],
"responses": {
@@ -7062,7 +7423,11 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/ItemIdsResult"
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "Response List All Board Image Names"
}
}
}
@@ -7080,51 +7445,29 @@
}
}
},
- "/api/v1/queue/{queue_id}/items_by_ids": {
+ "/api/v1/board_images/": {
"post": {
- "tags": ["queue"],
- "summary": "Get Queue Items By Item Ids",
- "description": "Gets queue items for the specified queue item ids. Maintains order of item ids.",
- "operationId": "get_queue_items_by_item_ids",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
+ "tags": ["boards"],
+ "summary": "Add Image To Board",
+ "description": "Creates a board_image",
+ "operationId": "add_image_to_board",
"requestBody": {
- "required": true,
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_get_queue_items_by_item_ids"
+ "$ref": "#/components/schemas/Body_add_image_to_board"
}
}
- }
+ },
+ "required": true
},
"responses": {
- "200": {
- "description": "Successful Response",
+ "201": {
+ "description": "The image was added to a board successfully",
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/SessionQueueItem"
- },
- "title": "Response 200 Get Queue Items By Item Ids"
+ "$ref": "#/components/schemas/AddImagesToBoardResult"
}
}
}
@@ -7139,88 +7482,35 @@
}
}
}
- }
- }
- },
- "/api/v1/queue/{queue_id}/processor/resume": {
- "put": {
- "tags": ["queue"],
- "summary": "Resume",
- "description": "Resumes session processor. Admin only.",
- "operationId": "resume",
+ },
"security": [
{
"HTTPBearer": []
}
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SessionProcessorStatus"
- }
+ ]
+ },
+ "delete": {
+ "tags": ["boards"],
+ "summary": "Remove Image From Board",
+ "description": "Removes an image from its board, if it had one",
+ "operationId": "remove_image_from_board",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_remove_image_from_board"
}
}
},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/queue/{queue_id}/processor/pause": {
- "put": {
- "tags": ["queue"],
- "summary": "Pause",
- "description": "Pauses session processor. Admin only.",
- "operationId": "pause",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
+ "required": true
+ },
"responses": {
- "200": {
- "description": "Successful Response",
+ "201": {
+ "description": "The image was removed from the board successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SessionProcessorStatus"
+ "$ref": "#/components/schemas/RemoveImagesFromBoardResult"
}
}
}
@@ -7235,40 +7525,37 @@
}
}
}
- }
- }
- },
- "/api/v1/queue/{queue_id}/cancel_all_except_current": {
- "put": {
- "tags": ["queue"],
- "summary": "Cancel All Except Current",
- "description": "Immediately cancels all queue items except in-processing items. Non-admin users can only cancel their own items.",
- "operationId": "cancel_all_except_current",
+ },
"security": [
{
"HTTPBearer": []
}
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
+ ]
+ }
+ },
+ "/api/v1/board_images/batch": {
+ "post": {
+ "tags": ["boards"],
+ "summary": "Add Images To Board",
+ "description": "Adds a list of images to a board",
+ "operationId": "add_images_to_board",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_add_images_to_board"
+ }
+ }
+ },
+ "required": true
+ },
"responses": {
- "200": {
- "description": "Successful Response",
+ "201": {
+ "description": "Images were added to board successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/CancelAllExceptCurrentResult"
+ "$ref": "#/components/schemas/AddImagesToBoardResult"
}
}
}
@@ -7283,40 +7570,37 @@
}
}
}
- }
- }
- },
- "/api/v1/queue/{queue_id}/delete_all_except_current": {
- "put": {
- "tags": ["queue"],
- "summary": "Delete All Except Current",
- "description": "Immediately deletes all queue items except in-processing items. Non-admin users can only delete their own items.",
- "operationId": "delete_all_except_current",
+ },
"security": [
{
"HTTPBearer": []
}
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
+ ]
+ }
+ },
+ "/api/v1/board_images/batch/delete": {
+ "post": {
+ "tags": ["boards"],
+ "summary": "Remove Images From Board",
+ "description": "Removes a list of images from their board, if they had one",
+ "operationId": "remove_images_from_board",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_remove_images_from_board"
+ }
+ }
+ },
+ "required": true
+ },
"responses": {
- "200": {
- "description": "Successful Response",
+ "201": {
+ "description": "Images were removed from board successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/DeleteAllExceptCurrentResult"
+ "$ref": "#/components/schemas/RemoveImagesFromBoardResult"
}
}
}
@@ -7331,73 +7615,49 @@
}
}
}
- }
- }
- },
- "/api/v1/queue/{queue_id}/cancel_by_batch_ids": {
- "put": {
- "tags": ["queue"],
- "summary": "Cancel By Batch Ids",
- "description": "Immediately cancels all queue items from the given batch ids. Non-admin users can only cancel their own items.",
- "operationId": "cancel_by_batch_ids",
+ },
"security": [
{
"HTTPBearer": []
}
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_cancel_by_batch_ids"
- }
- }
- }
- },
+ ]
+ }
+ },
+ "/api/v1/virtual_boards/by_date": {
+ "get": {
+ "tags": ["virtual_boards"],
+ "summary": "List Virtual Boards By Date",
+ "description": "Gets a list of virtual sub-boards grouped by date. Covers both images and videos.",
+ "operationId": "list_virtual_boards_by_date",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/CancelByBatchIDsResult"
- }
- }
- }
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
+ "items": {
+ "$ref": "#/components/schemas/VirtualSubBoardDTO"
+ },
+ "type": "array",
+ "title": "Response List Virtual Boards By Date"
}
}
}
}
- }
+ },
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
}
},
- "/api/v1/queue/{queue_id}/cancel_by_destination": {
- "put": {
- "tags": ["queue"],
- "summary": "Cancel By Destination",
- "description": "Immediately cancels all queue items with the given destination. Non-admin users can only cancel their own items.",
- "operationId": "cancel_by_destination",
+ "/api/v1/virtual_boards/by_date/{date}/image_names": {
+ "get": {
+ "tags": ["virtual_boards"],
+ "summary": "List Virtual Board Image Names By Date",
+ "description": "Gets ordered image names for a specific date. Image-only; kept for API compatibility \u2014\nthe UI uses the polymorphic `/by_date/{date}/item_names` endpoint.",
+ "operationId": "list_virtual_board_image_names_by_date",
"security": [
{
"HTTPBearer": []
@@ -7405,26 +7665,77 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "date",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
+ "description": "The ISO date string, e.g. '2026-03-18'",
+ "title": "Date"
},
- "description": "The queue id to perform this operation on"
+ "description": "The ISO date string, e.g. '2026-03-18'"
},
{
- "name": "destination",
+ "name": "starred_first",
"in": "query",
- "required": true,
+ "required": false,
"schema": {
- "type": "string",
- "description": "The destination to cancel all queue items for",
- "title": "Destination"
+ "type": "boolean",
+ "description": "Whether to sort starred images first",
+ "default": true,
+ "title": "Starred First"
},
- "description": "The destination to cancel all queue items for"
+ "description": "Whether to sort starred images first"
+ },
+ {
+ "name": "order_dir",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The sort direction",
+ "default": "DESC"
+ },
+ "description": "The sort direction"
+ },
+ {
+ "name": "categories",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ImageCategory"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories of images to include",
+ "title": "Categories"
+ },
+ "description": "The categories of images to include"
+ },
+ {
+ "name": "search_term",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Search term to filter images",
+ "title": "Search Term"
+ },
+ "description": "Search term to filter images"
}
],
"responses": {
@@ -7433,7 +7744,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/CancelByDestinationResult"
+ "$ref": "#/components/schemas/ImageNamesResult"
}
}
}
@@ -7451,12 +7762,12 @@
}
}
},
- "/api/v1/queue/{queue_id}/retry_items_by_id": {
- "put": {
- "tags": ["queue"],
- "summary": "Retry Items By Id",
- "description": "Retries the given queue items. Users can only retry their own items unless they are an admin.",
- "operationId": "retry_items_by_id",
+ "/api/v1/virtual_boards/by_date/{date}/item_names": {
+ "get": {
+ "tags": ["virtual_boards"],
+ "summary": "List Virtual Board Item Names By Date",
+ "description": "Gets ordered polymorphic (image + video) item refs for a specific date.",
+ "operationId": "list_virtual_board_item_names_by_date",
"security": [
{
"HTTPBearer": []
@@ -7464,78 +7775,77 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "date",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
+ "description": "The ISO date string, e.g. '2026-03-18'",
+ "title": "Date"
},
- "description": "The queue id to perform this operation on"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "array",
- "items": {
- "type": "integer"
+ "description": "The ISO date string, e.g. '2026-03-18'"
+ },
+ {
+ "name": "starred_first",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "Whether to sort starred items first",
+ "default": true,
+ "title": "Starred First"
+ },
+ "description": "Whether to sort starred items first"
+ },
+ {
+ "name": "order_dir",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The sort direction",
+ "default": "DESC"
+ },
+ "description": "The sort direction"
+ },
+ {
+ "name": "categories",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ImageCategory"
+ }
},
- "description": "The queue item ids to retry",
- "title": "Item Ids"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/RetryItemsResult"
+ {
+ "type": "null"
}
- }
- }
+ ],
+ "description": "The categories of items to include",
+ "title": "Categories"
+ },
+ "description": "The categories of items to include"
},
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/queue/{queue_id}/clear": {
- "put": {
- "tags": ["queue"],
- "summary": "Clear",
- "description": "Clears the queue entirely. Admin users clear all items; non-admin users only clear their own items. If there's a currently-executing item, users can only cancel it if they own it or are an admin.",
- "operationId": "clear",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "parameters": [
{
- "name": "queue_id",
- "in": "path",
- "required": true,
+ "name": "search_term",
+ "in": "query",
+ "required": false,
"schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Search term to filter items",
+ "title": "Search Term"
},
- "description": "The queue id to perform this operation on"
+ "description": "Search term to filter items"
}
],
"responses": {
@@ -7544,7 +7854,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/ClearResult"
+ "$ref": "#/components/schemas/GalleryItemNamesResult"
}
}
}
@@ -7562,12 +7872,12 @@
}
}
},
- "/api/v1/queue/{queue_id}/prune": {
- "put": {
- "tags": ["queue"],
- "summary": "Prune",
- "description": "Prunes all completed or errored queue items. Non-admin users can only prune their own items.",
- "operationId": "prune",
+ "/api/v1/model_relationships/i/{model_key}": {
+ "get": {
+ "tags": ["model_relationships"],
+ "summary": "Get Related Models",
+ "description": "Get a list of model keys related to a given model.",
+ "operationId": "get_related_models",
"security": [
{
"HTTPBearer": []
@@ -7575,206 +7885,216 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "model_key",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
+ "description": "The key of the model to get relationships for",
+ "title": "Model Key"
},
- "description": "The queue id to perform this operation on"
+ "description": "The key of the model to get relationships for"
}
],
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "A list of related model keys was retrieved successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/PruneResult"
- }
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "Response Get Related Models"
+ },
+ "example": [
+ "15e9eb28-8cfe-47c9-b610-37907a79fc3c",
+ "71272e82-0e5f-46d5-bca9-9a61f4bd8a82",
+ "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2"
+ ]
}
}
},
+ "404": {
+ "description": "The specified model could not be found"
+ },
"422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
+ "description": "Validation error"
}
}
}
},
- "/api/v1/queue/{queue_id}/current": {
- "get": {
- "tags": ["queue"],
- "summary": "Get Current Queue Item",
- "description": "Gets the currently execution queue item",
- "operationId": "get_current_queue_item",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/SessionQueueItem"
- },
- {
- "type": "null"
- },
- {
- "$ref": "#/components/schemas/SessionQueueItem"
- },
- {
- "type": "null"
- }
- ],
- "title": "Response 200 Get Current Queue Item"
- }
+ "/api/v1/model_relationships/": {
+ "post": {
+ "tags": ["model_relationships"],
+ "summary": "Add Model Relationship",
+ "description": "Creates a **bidirectional** relationship between two models, allowing each to reference the other as related.",
+ "operationId": "add_model_relationship_api_v1_model_relationships__post",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ModelRelationshipCreateRequest",
+ "description": "The model keys to relate"
}
}
},
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The relationship was successfully created"
+ },
+ "400": {
+ "description": "Invalid model keys or self-referential relationship"
+ },
+ "409": {
+ "description": "The relationship already exists"
+ },
"422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
+ "description": "Validation error"
+ },
+ "500": {
+ "description": "Internal server error"
}
- }
- }
- },
- "/api/v1/queue/{queue_id}/next": {
- "get": {
- "tags": ["queue"],
- "summary": "Get Next Queue Item",
- "description": "Gets the next queue item, without executing it",
- "operationId": "get_next_queue_item",
+ },
"security": [
{
"HTTPBearer": []
}
- ],
- "parameters": [
+ ]
+ },
+ "delete": {
+ "tags": ["model_relationships"],
+ "summary": "Remove Model Relationship",
+ "description": "Removes a **bidirectional** relationship between two models. The relationship must already exist.",
+ "operationId": "remove_model_relationship_api_v1_model_relationships__delete",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ModelRelationshipCreateRequest",
+ "description": "The model keys to disconnect"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The relationship was successfully removed"
+ },
+ "400": {
+ "description": "Invalid model keys or self-referential relationship"
+ },
+ "404": {
+ "description": "The relationship does not exist"
+ },
+ "422": {
+ "description": "Validation error"
+ },
+ "500": {
+ "description": "Internal server error"
+ }
+ },
+ "security": [
{
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
+ "HTTPBearer": []
}
- ],
+ ]
+ }
+ },
+ "/api/v1/model_relationships/batch": {
+ "post": {
+ "tags": ["model_relationships"],
+ "summary": "Get Related Model Keys (Batch)",
+ "description": "Retrieves all **unique related model keys** for a list of given models. This is useful for contextual suggestions or filtering.",
+ "operationId": "get_related_models_batch",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ModelRelationshipBatchRequest",
+ "description": "Model keys to check for related connections"
+ }
+ }
+ },
+ "required": true
+ },
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "Related model keys retrieved successfully",
"content": {
"application/json": {
"schema": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/SessionQueueItem"
- },
- {
- "type": "null"
- },
- {
- "$ref": "#/components/schemas/SessionQueueItem"
- },
- {
- "type": "null"
- }
- ],
- "title": "Response 200 Get Next Queue Item"
- }
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Response Get Related Models Batch"
+ },
+ "example": [
+ "ca562b14-995e-4a42-90c1-9528f1a5921d",
+ "cc0c2b8a-c62e-41d6-878e-cc74dde5ca8f",
+ "18ca7649-6a9e-47d5-bc17-41ab1e8cec81",
+ "7c12d1b2-0ef9-4bec-ba55-797b2d8f2ee1",
+ "c382eaa3-0e28-4ab0-9446-408667699aeb",
+ "71272e82-0e5f-46d5-bca9-9a61f4bd8a82",
+ "a5d7cd49-1b98-4534-a475-aeee4ccf5fa2"
+ ]
}
}
},
"422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
+ "description": "Validation error"
+ },
+ "500": {
+ "description": "Internal server error"
}
- }
- }
- },
- "/api/v1/queue/{queue_id}/status": {
- "get": {
- "tags": ["queue"],
- "summary": "Get Queue Status",
- "description": "Gets the status of the session queue. Returns global counts; non-admin users additionally\nget their own pending/in_progress counts (so the UI can show an X/Y badge) and cannot see the\ncurrent item's identifiers unless they own it.",
- "operationId": "get_queue_status",
+ },
"security": [
{
"HTTPBearer": []
}
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- }
- ],
+ ]
+ }
+ },
+ "/api/v1/app/version": {
+ "get": {
+ "tags": ["app"],
+ "summary": "Get Version",
+ "operationId": "app_version",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SessionQueueAndProcessorStatus"
+ "$ref": "#/components/schemas/AppVersion"
}
}
}
- },
- "422": {
- "description": "Validation Error",
+ }
+ }
+ }
+ },
+ "/api/v1/app/app_deps": {
+ "get": {
+ "tags": ["app"],
+ "summary": "Get App Deps",
+ "operationId": "get_app_deps",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": "object",
+ "title": "Response Get App Deps"
}
}
}
@@ -7782,107 +8102,66 @@
}
}
},
- "/api/v1/queue/{queue_id}/b/{batch_id}/status": {
+ "/api/v1/app/patchmatch_status": {
"get": {
- "tags": ["queue"],
- "summary": "Get Batch Status",
- "description": "Gets the status of a batch. Non-admin users only see their own batches.",
- "operationId": "get_batch_status",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- },
- {
- "name": "batch_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The batch to get the status of",
- "title": "Batch Id"
- },
- "description": "The batch to get the status of"
- }
- ],
+ "tags": ["app"],
+ "summary": "Get Patchmatch Status",
+ "operationId": "get_patchmatch_status",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/BatchStatus"
+ "type": "boolean",
+ "title": "Response Get Patchmatch Status"
}
}
}
- },
- "422": {
- "description": "Validation Error",
+ }
+ }
+ }
+ },
+ "/api/v1/app/runtime_config": {
+ "get": {
+ "tags": ["app"],
+ "summary": "Get Runtime Config",
+ "operationId": "get_runtime_config",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
+ "$ref": "#/components/schemas/InvokeAIAppConfigWithSetFields"
}
}
}
}
}
- }
- },
- "/api/v1/queue/{queue_id}/i/{item_id}": {
- "get": {
- "tags": ["queue"],
- "summary": "Get Queue Item",
- "description": "Gets a queue item",
- "operationId": "get_queue_item",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
+ },
+ "patch": {
+ "tags": ["app"],
+ "summary": "Update Runtime Config",
+ "operationId": "update_runtime_config",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateAppGenerationSettingsRequest",
+ "description": "Writable runtime configuration changes"
+ }
+ }
},
- {
- "name": "item_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "description": "The queue item to get",
- "title": "Item Id"
- },
- "description": "The queue item to get"
- }
- ],
+ "required": true
+ },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SessionQueueItem"
+ "$ref": "#/components/schemas/InvokeAIAppConfigWithSetFields"
}
}
}
@@ -7897,57 +8176,53 @@
}
}
}
- }
- },
- "delete": {
- "tags": ["queue"],
- "summary": "Delete Queue Item",
- "description": "Deletes a queue item. Users can only delete their own items unless they are an admin.",
- "operationId": "delete_queue_item",
+ },
"security": [
{
"HTTPBearer": []
}
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- },
- {
- "name": "item_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "description": "The queue item to delete",
- "title": "Item Id"
- },
- "description": "The queue item to delete"
- }
- ],
+ ]
+ }
+ },
+ "/api/v1/app/external_providers/status": {
+ "get": {
+ "tags": ["app"],
+ "summary": "Get External Provider Statuses",
+ "operationId": "get_external_provider_statuses",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {}
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/ExternalProviderStatusModel"
+ },
+ "type": "array",
+ "title": "Response Get External Provider Statuses"
+ }
}
}
- },
- "422": {
- "description": "Validation Error",
+ }
+ }
+ }
+ },
+ "/api/v1/app/external_providers/config": {
+ "get": {
+ "tags": ["app"],
+ "summary": "Get External Provider Configs",
+ "operationId": "get_external_provider_configs",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
+ "items": {
+ "$ref": "#/components/schemas/ExternalProviderConfigModel"
+ },
+ "type": "array",
+ "title": "Response Get External Provider Configs"
}
}
}
@@ -7955,12 +8230,11 @@
}
}
},
- "/api/v1/queue/{queue_id}/i/{item_id}/cancel": {
- "put": {
- "tags": ["queue"],
- "summary": "Cancel Queue Item",
- "description": "Cancels a queue item. Users can only cancel their own items unless they are an admin.",
- "operationId": "cancel_queue_item",
+ "/api/v1/app/external_providers/config/{provider_id}": {
+ "post": {
+ "tags": ["app"],
+ "summary": "Set External Provider Config",
+ "operationId": "set_external_provider_config",
"security": [
{
"HTTPBearer": []
@@ -7968,35 +8242,35 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "provider_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
- "title": "Queue Id"
- },
- "description": "The queue id to perform this operation on"
- },
- {
- "name": "item_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "integer",
- "description": "The queue item to cancel",
- "title": "Item Id"
+ "description": "The external provider identifier",
+ "title": "Provider Id"
},
- "description": "The queue item to cancel"
+ "description": "The external provider identifier"
}
],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ExternalProviderConfigUpdate",
+ "description": "External provider configuration settings"
+ }
+ }
+ }
+ },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SessionQueueItem"
+ "$ref": "#/components/schemas/ExternalProviderConfigModel"
}
}
}
@@ -8012,14 +8286,11 @@
}
}
}
- }
- },
- "/api/v1/queue/{queue_id}/counts_by_destination": {
- "get": {
- "tags": ["queue"],
- "summary": "Counts By Destination",
- "description": "Gets the counts of queue items by destination. Non-admin users only see their own items.",
- "operationId": "counts_by_destination",
+ },
+ "delete": {
+ "tags": ["app"],
+ "summary": "Reset External Provider Config",
+ "operationId": "reset_external_provider_config",
"security": [
{
"HTTPBearer": []
@@ -8027,26 +8298,15 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "provider_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to query",
- "title": "Queue Id"
- },
- "description": "The queue id to query"
- },
- {
- "name": "destination",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The destination to query",
- "title": "Destination"
+ "description": "The external provider identifier",
+ "title": "Provider Id"
},
- "description": "The destination to query"
+ "description": "The external provider identifier"
}
],
"responses": {
@@ -8055,7 +8315,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SessionQueueCountsByDestination"
+ "$ref": "#/components/schemas/ExternalProviderConfigModel"
}
}
}
@@ -8073,48 +8333,48 @@
}
}
},
- "/api/v1/queue/{queue_id}/d/{destination}": {
- "delete": {
- "tags": ["queue"],
- "summary": "Delete By Destination",
- "description": "Deletes all items with the given destination. Non-admin users can only delete their own items.",
- "operationId": "delete_by_destination",
- "security": [
- {
- "HTTPBearer": []
+ "/api/v1/app/logging": {
+ "get": {
+ "tags": ["app"],
+ "summary": "Get Log Level",
+ "description": "Returns the log level",
+ "operationId": "get_log_level",
+ "responses": {
+ "200": {
+ "description": "The operation was successful",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/LogLevel"
+ }
+ }
+ }
}
- ],
- "parameters": [
- {
- "name": "queue_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The queue id to query",
- "title": "Queue Id"
- },
- "description": "The queue id to query"
+ }
+ },
+ "post": {
+ "tags": ["app"],
+ "summary": "Set Log Level",
+ "description": "Sets the log verbosity level",
+ "operationId": "set_log_level",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/LogLevel",
+ "description": "New log verbosity level"
+ }
+ }
},
- {
- "name": "destination",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The destination to query",
- "title": "Destination"
- },
- "description": "The destination to query"
- }
- ],
+ "required": true
+ },
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "The operation was successful",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/DeleteByDestinationResult"
+ "$ref": "#/components/schemas/LogLevel"
}
}
}
@@ -8132,101 +8392,86 @@
}
}
},
- "/api/v1/workflows/i/{workflow_id}": {
- "get": {
- "tags": ["workflows"],
- "summary": "Get Workflow",
- "description": "Gets a workflow",
- "operationId": "get_workflow",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "parameters": [
- {
- "name": "workflow_id",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string",
- "description": "The workflow to get",
- "title": "Workflow Id"
- },
- "description": "The workflow to get"
- }
- ],
+ "/api/v1/app/invocation_cache": {
+ "delete": {
+ "tags": ["app"],
+ "summary": "Clear Invocation Cache",
+ "description": "Clears the invocation cache",
+ "operationId": "clear_invocation_cache",
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "The operation was successful",
"content": {
"application/json": {
- "schema": {
- "$ref": "#/components/schemas/WorkflowRecordWithThumbnailDTO"
- }
+ "schema": {}
}
}
- },
- "422": {
- "description": "Validation Error",
+ }
+ }
+ }
+ },
+ "/api/v1/app/invocation_cache/enable": {
+ "put": {
+ "tags": ["app"],
+ "summary": "Enable Invocation Cache",
+ "description": "Clears the invocation cache",
+ "operationId": "enable_invocation_cache",
+ "responses": {
+ "200": {
+ "description": "The operation was successful",
"content": {
"application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
+ "schema": {}
}
}
}
}
- },
- "patch": {
- "tags": ["workflows"],
- "summary": "Update Workflow",
- "description": "Updates a workflow",
- "operationId": "update_workflow",
- "security": [
- {
- "HTTPBearer": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_workflow"
- }
- }
- }
- },
+ }
+ },
+ "/api/v1/app/invocation_cache/disable": {
+ "put": {
+ "tags": ["app"],
+ "summary": "Disable Invocation Cache",
+ "description": "Clears the invocation cache",
+ "operationId": "disable_invocation_cache",
"responses": {
"200": {
- "description": "Successful Response",
+ "description": "The operation was successful",
"content": {
"application/json": {
- "schema": {
- "$ref": "#/components/schemas/WorkflowRecordDTO"
- }
+ "schema": {}
}
}
- },
- "422": {
- "description": "Validation Error",
+ }
+ }
+ }
+ },
+ "/api/v1/app/invocation_cache/status": {
+ "get": {
+ "tags": ["app"],
+ "summary": "Get Invocation Cache Status",
+ "description": "Clears the invocation cache",
+ "operationId": "get_invocation_cache_status",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
+ "$ref": "#/components/schemas/InvocationCacheStatus"
}
}
}
}
}
- },
- "delete": {
- "tags": ["workflows"],
- "summary": "Delete Workflow",
- "description": "Deletes a workflow",
- "operationId": "delete_workflow",
+ }
+ },
+ "/api/v1/queue/{queue_id}/enqueue_batch": {
+ "post": {
+ "tags": ["queue"],
+ "summary": "Enqueue Batch",
+ "description": "Processes a batch and enqueues the output graphs for execution for the current user.",
+ "operationId": "enqueue_batch",
"security": [
{
"HTTPBearer": []
@@ -8234,48 +8479,15 @@
],
"parameters": [
{
- "name": "workflow_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The workflow to delete",
- "title": "Workflow Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The workflow to delete"
- }
- ],
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {}
- }
- }
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/workflows/": {
- "post": {
- "tags": ["workflows"],
- "summary": "Create Workflow",
- "description": "Creates a workflow",
- "operationId": "create_workflow",
- "security": [
- {
- "HTTPBearer": []
+ "description": "The queue id to perform this operation on"
}
],
"requestBody": {
@@ -8283,7 +8495,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_create_workflow"
+ "$ref": "#/components/schemas/Body_enqueue_batch"
}
}
}
@@ -8294,11 +8506,21 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/WorkflowRecordDTO"
+ "$ref": "#/components/schemas/EnqueueBatchResult"
}
}
}
},
+ "201": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/EnqueueBatchResult"
+ }
+ }
+ },
+ "description": "Created"
+ },
"422": {
"description": "Validation Error",
"content": {
@@ -8310,12 +8532,14 @@
}
}
}
- },
+ }
+ },
+ "/api/v1/queue/{queue_id}/list_all": {
"get": {
- "tags": ["workflows"],
- "summary": "List Workflows",
- "description": "Gets a page of workflows",
- "operationId": "list_workflows",
+ "tags": ["queue"],
+ "summary": "List All Queue Items",
+ "description": "Gets all queue items",
+ "operationId": "list_all_queue_items",
"security": [
{
"HTTPBearer": []
@@ -8323,170 +8547,96 @@
],
"parameters": [
{
- "name": "page",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer",
- "description": "The page to get",
- "default": 0,
- "title": "Page"
- },
- "description": "The page to get"
- },
- {
- "name": "per_page",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "description": "The number of workflows per page",
- "title": "Per Page"
- },
- "description": "The number of workflows per page"
- },
- {
- "name": "order_by",
- "in": "query",
- "required": false,
- "schema": {
- "$ref": "#/components/schemas/WorkflowRecordOrderBy",
- "description": "The attribute to order by",
- "default": "name"
- },
- "description": "The attribute to order by"
- },
- {
- "name": "direction",
- "in": "query",
- "required": false,
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
"schema": {
- "$ref": "#/components/schemas/SQLiteDirection",
- "description": "The direction to order by",
- "default": "ASC"
+ "type": "string",
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The direction to order by"
+ "description": "The queue id to perform this operation on"
},
{
- "name": "categories",
+ "name": "destination",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/WorkflowCategory"
- }
+ "type": "string"
},
{
"type": "null"
}
],
- "description": "The categories of workflow to get",
- "title": "Categories"
+ "description": "The destination of queue items to fetch",
+ "title": "Destination"
},
- "description": "The categories of workflow to get"
- },
- {
- "name": "tags",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
+ "description": "The destination of queue items to fetch"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
"type": "array",
"items": {
- "type": "string"
- }
- },
- {
- "type": "null"
+ "$ref": "#/components/schemas/SessionQueueItem"
+ },
+ "title": "Response 200 List All Queue Items"
}
- ],
- "description": "The tags of workflow to get",
- "title": "Tags"
- },
- "description": "The tags of workflow to get"
+ }
+ }
},
- {
- "name": "query",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
}
- ],
- "description": "The text to query by (matches name and description)",
- "title": "Query"
- },
- "description": "The text to query by (matches name and description)"
- },
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/queue/{queue_id}/item_ids": {
+ "get": {
+ "tags": ["queue"],
+ "summary": "Get Queue Item Ids",
+ "description": "Gets all queue item ids that match the given parameters.\n\nIDs for every user's items are returned (item ids carry no sensitive data on their own).\nWhen the corresponding items are hydrated via get_queue_items_by_item_ids, those belonging\nto other users are redacted by sanitize_queue_item_for_user. This lets a non-admin see\npartially-redacted entries for other users' jobs in the queue list, while still revealing\nonly timestamps and status for items they do not own.\n\ncurrent_user is required so the endpoint stays behind authentication in multiuser mode.",
+ "operationId": "get_queue_item_ids",
+ "security": [
{
- "name": "has_been_opened",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Whether to include/exclude recent workflows",
- "title": "Has Been Opened"
- },
- "description": "Whether to include/exclude recent workflows"
- },
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
{
- "name": "is_public",
- "in": "query",
- "required": false,
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
"schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Filter by public/shared status",
- "title": "Is Public"
+ "type": "string",
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "Filter by public/shared status"
+ "description": "The queue id to perform this operation on"
},
{
- "name": "callable",
+ "name": "order_dir",
"in": "query",
"required": false,
"schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Filter by whether workflows are callable by call_saved_workflow",
- "title": "Callable"
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The order of sort",
+ "default": "DESC"
},
- "description": "Filter by whether workflows are callable by call_saved_workflow"
+ "description": "The order of sort"
}
],
"responses": {
@@ -8495,7 +8645,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/PaginatedResults_WorkflowRecordListItemWithThumbnailDTO_"
+ "$ref": "#/components/schemas/ItemIdsResult"
}
}
}
@@ -8513,12 +8663,12 @@
}
}
},
- "/api/v1/workflows/i/{workflow_id}/thumbnail": {
- "put": {
- "tags": ["workflows"],
- "summary": "Set Workflow Thumbnail",
- "description": "Sets a workflow's thumbnail image",
- "operationId": "set_workflow_thumbnail",
+ "/api/v1/queue/{queue_id}/items_by_ids": {
+ "post": {
+ "tags": ["queue"],
+ "summary": "Get Queue Items By Item Ids",
+ "description": "Gets queue items for the specified queue item ids. Maintains order of item ids.",
+ "operationId": "get_queue_items_by_item_ids",
"security": [
{
"HTTPBearer": []
@@ -8526,23 +8676,23 @@
],
"parameters": [
{
- "name": "workflow_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The workflow to update",
- "title": "Workflow Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The workflow to update"
+ "description": "The queue id to perform this operation on"
}
],
"requestBody": {
"required": true,
"content": {
- "multipart/form-data": {
+ "application/json": {
"schema": {
- "$ref": "#/components/schemas/Body_set_workflow_thumbnail"
+ "$ref": "#/components/schemas/Body_get_queue_items_by_item_ids"
}
}
}
@@ -8553,7 +8703,11 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/WorkflowRecordDTO"
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/SessionQueueItem"
+ },
+ "title": "Response 200 Get Queue Items By Item Ids"
}
}
}
@@ -8569,12 +8723,14 @@
}
}
}
- },
- "delete": {
- "tags": ["workflows"],
- "summary": "Delete Workflow Thumbnail",
- "description": "Removes a workflow's thumbnail image",
- "operationId": "delete_workflow_thumbnail",
+ }
+ },
+ "/api/v1/queue/{queue_id}/processor/resume": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Resume",
+ "description": "Resumes session processor. Admin only.",
+ "operationId": "resume",
"security": [
{
"HTTPBearer": []
@@ -8582,15 +8738,15 @@
],
"parameters": [
{
- "name": "workflow_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The workflow to update",
- "title": "Workflow Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The workflow to update"
+ "description": "The queue id to perform this operation on"
}
],
"responses": {
@@ -8599,7 +8755,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/WorkflowRecordDTO"
+ "$ref": "#/components/schemas/SessionProcessorStatus"
}
}
}
@@ -8615,40 +8771,43 @@
}
}
}
- },
- "get": {
- "tags": ["workflows"],
- "summary": "Get Workflow Thumbnail",
- "description": "Gets a workflow's thumbnail image.\n\nThis endpoint is intentionally unauthenticated because browsers load images\nvia tags which cannot send Bearer tokens. Workflow IDs are UUIDs,\nproviding security through unguessability.",
- "operationId": "get_workflow_thumbnail",
+ }
+ },
+ "/api/v1/queue/{queue_id}/processor/pause": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Pause",
+ "description": "Pauses session processor. Admin only.",
+ "operationId": "pause",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
"parameters": [
{
- "name": "workflow_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The id of the workflow thumbnail to get",
- "title": "Workflow Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The id of the workflow thumbnail to get"
+ "description": "The queue id to perform this operation on"
}
],
"responses": {
"200": {
- "description": "The workflow thumbnail was fetched successfully",
+ "description": "Successful Response",
"content": {
"application/json": {
- "schema": {}
+ "schema": {
+ "$ref": "#/components/schemas/SessionProcessorStatus"
+ }
}
}
},
- "400": {
- "description": "Bad request"
- },
- "404": {
- "description": "The workflow thumbnail could not be found"
- },
"422": {
"description": "Validation Error",
"content": {
@@ -8662,12 +8821,12 @@
}
}
},
- "/api/v1/workflows/i/{workflow_id}/is_public": {
- "patch": {
- "tags": ["workflows"],
- "summary": "Update Workflow Is Public",
- "description": "Updates whether a workflow is shared publicly",
- "operationId": "update_workflow_is_public",
+ "/api/v1/queue/{queue_id}/cancel_all_except_current": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Cancel All Except Current",
+ "description": "Immediately cancels all queue items except in-processing items. Non-admin users can only cancel their own items.",
+ "operationId": "cancel_all_except_current",
"security": [
{
"HTTPBearer": []
@@ -8675,34 +8834,24 @@
],
"parameters": [
{
- "name": "workflow_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The workflow to update",
- "title": "Workflow Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The workflow to update"
+ "description": "The queue id to perform this operation on"
}
],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_workflow_is_public"
- }
- }
- }
- },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/WorkflowRecordDTO"
+ "$ref": "#/components/schemas/CancelAllExceptCurrentResult"
}
}
}
@@ -8720,12 +8869,12 @@
}
}
},
- "/api/v1/workflows/tags": {
- "get": {
- "tags": ["workflows"],
- "summary": "Get All Tags",
- "description": "Gets all unique tags from workflows",
- "operationId": "get_all_tags",
+ "/api/v1/queue/{queue_id}/delete_all_except_current": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Delete All Except Current",
+ "description": "Immediately deletes all queue items except in-processing items. Non-admin users can only delete their own items.",
+ "operationId": "delete_all_except_current",
"security": [
{
"HTTPBearer": []
@@ -8733,43 +8882,15 @@
],
"parameters": [
{
- "name": "categories",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/WorkflowCategory"
- }
- },
- {
- "type": "null"
- }
- ],
- "description": "The categories to include",
- "title": "Categories"
- },
- "description": "The categories to include"
- },
- {
- "name": "is_public",
- "in": "query",
- "required": false,
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
"schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Filter by public/shared status",
- "title": "Is Public"
+ "type": "string",
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "Filter by public/shared status"
+ "description": "The queue id to perform this operation on"
}
],
"responses": {
@@ -8778,11 +8899,7 @@
"content": {
"application/json": {
"schema": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "title": "Response Get All Tags"
+ "$ref": "#/components/schemas/DeleteAllExceptCurrentResult"
}
}
}
@@ -8800,12 +8917,12 @@
}
}
},
- "/api/v1/workflows/counts_by_tag": {
- "get": {
- "tags": ["workflows"],
- "summary": "Get Counts By Tag",
- "description": "Counts workflows by tag",
- "operationId": "get_counts_by_tag",
+ "/api/v1/queue/{queue_id}/cancel_by_batch_ids": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Cancel By Batch Ids",
+ "description": "Immediately cancels all queue items from the given batch ids. Non-admin users can only cancel their own items.",
+ "operationId": "cancel_by_batch_ids",
"security": [
{
"HTTPBearer": []
@@ -8813,88 +8930,34 @@
],
"parameters": [
{
- "name": "tags",
- "in": "query",
+ "name": "queue_id",
+ "in": "path",
"required": true,
"schema": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "description": "The tags to get counts for",
- "title": "Tags"
- },
- "description": "The tags to get counts for"
- },
- {
- "name": "categories",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/WorkflowCategory"
- }
- },
- {
- "type": "null"
- }
- ],
- "description": "The categories to include",
- "title": "Categories"
- },
- "description": "The categories to include"
- },
- {
- "name": "has_been_opened",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Whether to include/exclude recent workflows",
- "title": "Has Been Opened"
- },
- "description": "Whether to include/exclude recent workflows"
- },
- {
- "name": "is_public",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Filter by public/shared status",
- "title": "Is Public"
+ "type": "string",
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "Filter by public/shared status"
+ "description": "The queue id to perform this operation on"
}
],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_cancel_by_batch_ids"
+ }
+ }
+ }
+ },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "type": "object",
- "additionalProperties": {
- "type": "integer"
- },
- "title": "Response Get Counts By Tag"
+ "$ref": "#/components/schemas/CancelByBatchIDsResult"
}
}
}
@@ -8912,12 +8975,12 @@
}
}
},
- "/api/v1/workflows/counts_by_category": {
- "get": {
- "tags": ["workflows"],
- "summary": "Counts By Category",
- "description": "Counts workflows by category",
- "operationId": "counts_by_category",
+ "/api/v1/queue/{queue_id}/cancel_by_destination": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Cancel By Destination",
+ "description": "Immediately cancels all queue items with the given destination. Non-admin users can only cancel their own items.",
+ "operationId": "cancel_by_destination",
"security": [
{
"HTTPBearer": []
@@ -8925,54 +8988,26 @@
],
"parameters": [
{
- "name": "categories",
- "in": "query",
+ "name": "queue_id",
+ "in": "path",
"required": true,
"schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/WorkflowCategory"
- },
- "description": "The categories to include",
- "title": "Categories"
- },
- "description": "The categories to include"
- },
- {
- "name": "has_been_opened",
- "in": "query",
- "required": false,
- "schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Whether to include/exclude recent workflows",
- "title": "Has Been Opened"
+ "type": "string",
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "Whether to include/exclude recent workflows"
+ "description": "The queue id to perform this operation on"
},
{
- "name": "is_public",
+ "name": "destination",
"in": "query",
- "required": false,
+ "required": true,
"schema": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "description": "Filter by public/shared status",
- "title": "Is Public"
+ "type": "string",
+ "description": "The destination to cancel all queue items for",
+ "title": "Destination"
},
- "description": "Filter by public/shared status"
+ "description": "The destination to cancel all queue items for"
}
],
"responses": {
@@ -8981,11 +9016,7 @@
"content": {
"application/json": {
"schema": {
- "type": "object",
- "additionalProperties": {
- "type": "integer"
- },
- "title": "Response Counts By Category"
+ "$ref": "#/components/schemas/CancelByDestinationResult"
}
}
}
@@ -9003,12 +9034,12 @@
}
}
},
- "/api/v1/workflows/i/{workflow_id}/opened_at": {
+ "/api/v1/queue/{queue_id}/retry_items_by_id": {
"put": {
- "tags": ["workflows"],
- "summary": "Update Opened At",
- "description": "Updates the opened_at field of a workflow",
- "operationId": "update_opened_at",
+ "tags": ["queue"],
+ "summary": "Retry Items By Id",
+ "description": "Retries the given queue items. Users can only retry their own items unless they are an admin.",
+ "operationId": "retry_items_by_id",
"security": [
{
"HTTPBearer": []
@@ -9016,23 +9047,40 @@
],
"parameters": [
{
- "name": "workflow_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The workflow to update",
- "title": "Workflow Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The workflow to update"
+ "description": "The queue id to perform this operation on"
}
],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ },
+ "description": "The queue item ids to retry",
+ "title": "Item Ids"
+ }
+ }
+ }
+ },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {}
+ "schema": {
+ "$ref": "#/components/schemas/RetryItemsResult"
+ }
}
}
},
@@ -9049,12 +9097,12 @@
}
}
},
- "/api/v1/style_presets/i/{style_preset_id}": {
- "get": {
- "tags": ["style_presets"],
- "summary": "Get Style Preset",
- "description": "Gets a style preset",
- "operationId": "get_style_preset",
+ "/api/v1/queue/{queue_id}/clear": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Clear",
+ "description": "Clears the queue entirely. Admin users clear all items; non-admin users only clear their own items. If there's a currently-executing item, users can only cancel it if they own it or are an admin.",
+ "operationId": "clear",
"security": [
{
"HTTPBearer": []
@@ -9062,15 +9110,15 @@
],
"parameters": [
{
- "name": "style_preset_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The style preset to get",
- "title": "Style Preset Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The style preset to get"
+ "description": "The queue id to perform this operation on"
}
],
"responses": {
@@ -9079,7 +9127,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/StylePresetRecordWithImage"
+ "$ref": "#/components/schemas/ClearResult"
}
}
}
@@ -9095,12 +9143,14 @@
}
}
}
- },
- "patch": {
- "tags": ["style_presets"],
- "summary": "Update Style Preset",
- "description": "Updates a style preset",
- "operationId": "update_style_preset",
+ }
+ },
+ "/api/v1/queue/{queue_id}/prune": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Prune",
+ "description": "Prunes all completed or errored queue items. Non-admin users can only prune their own items.",
+ "operationId": "prune",
"security": [
{
"HTTPBearer": []
@@ -9108,34 +9158,24 @@
],
"parameters": [
{
- "name": "style_preset_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The id of the style preset to update",
- "title": "Style Preset Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The id of the style preset to update"
+ "description": "The queue id to perform this operation on"
}
],
- "requestBody": {
- "required": true,
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_update_style_preset"
- }
- }
- }
- },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/StylePresetRecordWithImage"
+ "$ref": "#/components/schemas/PruneResult"
}
}
}
@@ -9151,12 +9191,14 @@
}
}
}
- },
- "delete": {
- "tags": ["style_presets"],
- "summary": "Delete Style Preset",
- "description": "Deletes a style preset",
- "operationId": "delete_style_preset",
+ }
+ },
+ "/api/v1/queue/{queue_id}/current": {
+ "get": {
+ "tags": ["queue"],
+ "summary": "Get Current Queue Item",
+ "description": "Gets the currently execution queue item",
+ "operationId": "get_current_queue_item",
"security": [
{
"HTTPBearer": []
@@ -9164,15 +9206,15 @@
],
"parameters": [
{
- "name": "style_preset_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The style preset to delete",
- "title": "Style Preset Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The style preset to delete"
+ "description": "The queue id to perform this operation on"
}
],
"responses": {
@@ -9180,7 +9222,23 @@
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {}
+ "schema": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/SessionQueueItem"
+ },
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "#/components/schemas/SessionQueueItem"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Response 200 Get Current Queue Item"
+ }
}
}
},
@@ -9197,56 +9255,51 @@
}
}
},
- "/api/v1/style_presets/": {
+ "/api/v1/queue/{queue_id}/next": {
"get": {
- "tags": ["style_presets"],
- "summary": "List Style Presets",
- "description": "Gets the style presets visible to the current user.",
- "operationId": "list_style_presets",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "items": {
- "$ref": "#/components/schemas/StylePresetRecordWithImage"
- },
- "type": "array",
- "title": "Response 200 List Style Presets"
- }
- }
- }
- }
- },
+ "tags": ["queue"],
+ "summary": "Get Next Queue Item",
+ "description": "Gets the next queue item, without executing it",
+ "operationId": "get_next_queue_item",
"security": [
{
"HTTPBearer": []
}
- ]
- },
- "post": {
- "tags": ["style_presets"],
- "summary": "Create Style Preset",
- "description": "Creates a style preset",
- "operationId": "create_style_preset",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_create_style_preset"
- }
- }
- },
- "required": true
- },
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
+ },
+ "description": "The queue id to perform this operation on"
+ }
+ ],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/StylePresetRecordWithImage"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/SessionQueueItem"
+ },
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "#/components/schemas/SessionQueueItem"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Response 200 Get Next Queue Item"
}
}
}
@@ -9261,20 +9314,15 @@
}
}
}
- },
- "security": [
- {
- "HTTPBearer": []
- }
- ]
+ }
}
},
- "/api/v1/style_presets/i/{style_preset_id}/image": {
+ "/api/v1/queue/{queue_id}/status": {
"get": {
- "tags": ["style_presets"],
- "summary": "Get Style Preset Image",
- "description": "Gets an image file that previews the model",
- "operationId": "get_style_preset_image",
+ "tags": ["queue"],
+ "summary": "Get Queue Status",
+ "description": "Gets the status of the session queue. Returns global counts; non-admin users additionally\nget their own pending/in_progress counts (so the UI can show an X/Y badge) and cannot see the\ncurrent item's identifiers unless they own it.",
+ "operationId": "get_queue_status",
"security": [
{
"HTTPBearer": []
@@ -9282,32 +9330,28 @@
],
"parameters": [
{
- "name": "style_preset_id",
+ "name": "queue_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The id of the style preset image to get",
- "title": "Style Preset Id"
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
},
- "description": "The id of the style preset image to get"
+ "description": "The queue id to perform this operation on"
}
],
"responses": {
"200": {
- "description": "The style preset image was fetched successfully",
+ "description": "Successful Response",
"content": {
"application/json": {
- "schema": {}
+ "schema": {
+ "$ref": "#/components/schemas/SessionQueueAndProcessorStatus"
+ }
}
}
},
- "400": {
- "description": "Bad request"
- },
- "404": {
- "description": "The style preset image could not be found"
- },
"422": {
"description": "Validation Error",
"content": {
@@ -9321,77 +9365,12 @@
}
}
},
- "/api/v1/style_presets/export": {
- "get": {
- "tags": ["style_presets"],
- "summary": "Export Style Presets",
- "operationId": "export_style_presets",
- "responses": {
- "200": {
- "description": "A CSV file with the requested data.",
- "content": {
- "application/json": {
- "schema": {}
- },
- "text/csv": {}
- }
- }
- },
- "security": [
- {
- "HTTPBearer": []
- }
- ]
- }
- },
- "/api/v1/style_presets/import": {
- "post": {
- "tags": ["style_presets"],
- "summary": "Import Style Presets",
- "operationId": "import_style_presets",
- "requestBody": {
- "content": {
- "multipart/form-data": {
- "schema": {
- "$ref": "#/components/schemas/Body_import_style_presets"
- }
- }
- },
- "required": true
- },
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {}
- }
- }
- },
- "422": {
- "description": "Validation Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/HTTPValidationError"
- }
- }
- }
- }
- },
- "security": [
- {
- "HTTPBearer": []
- }
- ]
- }
- },
- "/api/v1/client_state/{queue_id}/get_by_key": {
+ "/api/v1/queue/{queue_id}/b/{batch_id}/status": {
"get": {
- "tags": ["client_state"],
- "summary": "Get Client State By Key",
- "description": "Gets the client state for the current user (or system user if not authenticated)",
- "operationId": "get_client_state_by_key",
+ "tags": ["queue"],
+ "summary": "Get Batch Status",
+ "description": "Gets the status of a batch. Non-admin users only see their own batches.",
+ "operationId": "get_batch_status",
"security": [
{
"HTTPBearer": []
@@ -9404,21 +9383,21 @@
"required": true,
"schema": {
"type": "string",
- "description": "The queue id (ignored, kept for backwards compatibility)",
+ "description": "The queue id to perform this operation on",
"title": "Queue Id"
},
- "description": "The queue id (ignored, kept for backwards compatibility)"
+ "description": "The queue id to perform this operation on"
},
{
- "name": "key",
- "in": "query",
+ "name": "batch_id",
+ "in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "Key to get",
- "title": "Key"
+ "description": "The batch to get the status of",
+ "title": "Batch Id"
},
- "description": "Key to get"
+ "description": "The batch to get the status of"
}
],
"responses": {
@@ -9427,15 +9406,7 @@
"content": {
"application/json": {
"schema": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Response Get Client State By Key"
+ "$ref": "#/components/schemas/BatchStatus"
}
}
}
@@ -9453,12 +9424,12 @@
}
}
},
- "/api/v1/client_state/{queue_id}/set_by_key": {
- "post": {
- "tags": ["client_state"],
- "summary": "Set Client State",
- "description": "Sets the client state for the current user (or system user if not authenticated)",
- "operationId": "set_client_state",
+ "/api/v1/queue/{queue_id}/i/{item_id}": {
+ "get": {
+ "tags": ["queue"],
+ "summary": "Get Queue Item",
+ "description": "Gets a queue item",
+ "operationId": "get_queue_item",
"security": [
{
"HTTPBearer": []
@@ -9471,43 +9442,30 @@
"required": true,
"schema": {
"type": "string",
- "description": "The queue id (ignored, kept for backwards compatibility)",
+ "description": "The queue id to perform this operation on",
"title": "Queue Id"
},
- "description": "The queue id (ignored, kept for backwards compatibility)"
+ "description": "The queue id to perform this operation on"
},
{
- "name": "key",
- "in": "query",
+ "name": "item_id",
+ "in": "path",
"required": true,
"schema": {
- "type": "string",
- "description": "Key to set",
- "title": "Key"
+ "type": "integer",
+ "description": "The queue item to get",
+ "title": "Item Id"
},
- "description": "Key to set"
+ "description": "The queue item to get"
}
],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "string",
- "description": "Stringified value to set",
- "title": "Value"
- }
- }
- }
- },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "type": "string",
- "title": "Response Set Client State"
+ "$ref": "#/components/schemas/SessionQueueItem"
}
}
}
@@ -9523,14 +9481,12 @@
}
}
}
- }
- },
- "/api/v1/client_state/{queue_id}/get_keys_by_prefix": {
- "get": {
- "tags": ["client_state"],
- "summary": "Get Client State Keys By Prefix",
- "description": "Gets client state keys matching a prefix for the current user",
- "operationId": "get_client_state_keys_by_prefix",
+ },
+ "delete": {
+ "tags": ["queue"],
+ "summary": "Delete Queue Item",
+ "description": "Deletes a queue item. Users can only delete their own items unless they are an admin.",
+ "operationId": "delete_queue_item",
"security": [
{
"HTTPBearer": []
@@ -9543,21 +9499,21 @@
"required": true,
"schema": {
"type": "string",
- "description": "The queue id (ignored, kept for backwards compatibility)",
+ "description": "The queue id to perform this operation on",
"title": "Queue Id"
},
- "description": "The queue id (ignored, kept for backwards compatibility)"
+ "description": "The queue id to perform this operation on"
},
{
- "name": "prefix",
- "in": "query",
+ "name": "item_id",
+ "in": "path",
"required": true,
"schema": {
- "type": "string",
- "description": "Prefix to filter keys by",
- "title": "Prefix"
+ "type": "integer",
+ "description": "The queue item to delete",
+ "title": "Item Id"
},
- "description": "Prefix to filter keys by"
+ "description": "The queue item to delete"
}
],
"responses": {
@@ -9565,13 +9521,7 @@
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "title": "Response Get Client State Keys By Prefix"
- }
+ "schema": {}
}
}
},
@@ -9588,12 +9538,12 @@
}
}
},
- "/api/v1/client_state/{queue_id}/delete_by_key": {
- "post": {
- "tags": ["client_state"],
- "summary": "Delete Client State By Key",
- "description": "Deletes a specific client state key for the current user",
- "operationId": "delete_client_state_by_key",
+ "/api/v1/queue/{queue_id}/i/{item_id}/cancel": {
+ "put": {
+ "tags": ["queue"],
+ "summary": "Cancel Queue Item",
+ "description": "Cancels a queue item. Users can only cancel their own items unless they are an admin.",
+ "operationId": "cancel_queue_item",
"security": [
{
"HTTPBearer": []
@@ -9606,21 +9556,21 @@
"required": true,
"schema": {
"type": "string",
- "description": "The queue id (ignored, kept for backwards compatibility)",
+ "description": "The queue id to perform this operation on",
"title": "Queue Id"
},
- "description": "The queue id (ignored, kept for backwards compatibility)"
+ "description": "The queue id to perform this operation on"
},
{
- "name": "key",
- "in": "query",
+ "name": "item_id",
+ "in": "path",
"required": true,
"schema": {
- "type": "string",
- "description": "Key to delete",
- "title": "Key"
+ "type": "integer",
+ "description": "The queue item to cancel",
+ "title": "Item Id"
},
- "description": "Key to delete"
+ "description": "The queue item to cancel"
}
],
"responses": {
@@ -9628,13 +9578,12 @@
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {}
+ "schema": {
+ "$ref": "#/components/schemas/SessionQueueItem"
+ }
}
}
},
- "204": {
- "description": "Client state key deleted"
- },
"422": {
"description": "Validation Error",
"content": {
@@ -9648,12 +9597,12 @@
}
}
},
- "/api/v1/client_state/{queue_id}/delete": {
- "post": {
- "tags": ["client_state"],
- "summary": "Delete Client State",
- "description": "Deletes the client state for the current user (or system user if not authenticated)",
- "operationId": "delete_client_state",
+ "/api/v1/queue/{queue_id}/counts_by_destination": {
+ "get": {
+ "tags": ["queue"],
+ "summary": "Counts By Destination",
+ "description": "Gets the counts of queue items by destination. Non-admin users only see their own items.",
+ "operationId": "counts_by_destination",
"security": [
{
"HTTPBearer": []
@@ -9666,10 +9615,21 @@
"required": true,
"schema": {
"type": "string",
- "description": "The queue id (ignored, kept for backwards compatibility)",
+ "description": "The queue id to query",
"title": "Queue Id"
},
- "description": "The queue id (ignored, kept for backwards compatibility)"
+ "description": "The queue id to query"
+ },
+ {
+ "name": "destination",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The destination to query",
+ "title": "Destination"
+ },
+ "description": "The destination to query"
}
],
"responses": {
@@ -9677,13 +9637,12 @@
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {}
+ "schema": {
+ "$ref": "#/components/schemas/SessionQueueCountsByDestination"
+ }
}
}
},
- "204": {
- "description": "Client state deleted"
- },
"422": {
"description": "Validation Error",
"content": {
@@ -9697,12 +9656,12 @@
}
}
},
- "/api/v1/recall/{queue_id}": {
- "post": {
- "tags": ["recall"],
- "summary": "Update Recall Parameters",
- "description": "Update recallable parameters that can be recalled on the frontend.\n\nThis endpoint allows updating parameters such as prompt, model, steps, and other\ngeneration settings. These parameters are stored in client state and can be\naccessed by the frontend to populate UI elements.\n\nArgs:\n queue_id: The queue ID to associate these parameters with\n parameters: The RecallParameter object containing the parameters to update\n strict: When true, parameters not included in the request body are reset\n to their defaults (cleared on the frontend). Defaults to false,\n which preserves the existing behaviour of only updating the\n parameters that are explicitly provided.\n append: When true, recalled reference images (``ip_adapters`` and\n ``reference_images``) are appended to whatever reference images the\n frontend already has, instead of replacing the whole list. Mutually\n exclusive with ``strict`` (which clears omitted parameters).\n\nReturns:\n A dictionary containing the updated parameters and status\n\nExample:\n POST /api/v1/recall/{queue_id}?strict=true\n {\n \"positive_prompt\": \"a beautiful landscape\",\n \"model\": \"sd-1.5\",\n \"steps\": 20\n }\n # In strict mode, all other parameters (reference_images, loras, etc.)\n # are cleared. In non-strict mode (default) they would be left as-is.",
- "operationId": "update_recall_parameters",
+ "/api/v1/queue/{queue_id}/d/{destination}": {
+ "delete": {
+ "tags": ["queue"],
+ "summary": "Delete By Destination",
+ "description": "Deletes all items with the given destination. Non-admin users can only delete their own items.",
+ "operationId": "delete_by_destination",
"security": [
{
"HTTPBearer": []
@@ -9715,56 +9674,30 @@
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to perform this operation on",
+ "description": "The queue id to query",
"title": "Queue Id"
},
- "description": "The queue id to perform this operation on"
- },
- {
- "name": "strict",
- "in": "query",
- "required": false,
- "schema": {
- "type": "boolean",
- "description": "When true, parameters not included in the request are reset to their defaults (cleared).",
- "default": false,
- "title": "Strict"
- },
- "description": "When true, parameters not included in the request are reset to their defaults (cleared)."
+ "description": "The queue id to query"
},
{
- "name": "append",
- "in": "query",
- "required": false,
+ "name": "destination",
+ "in": "path",
+ "required": true,
"schema": {
- "type": "boolean",
- "description": "When true, recalled reference images (ip_adapters and reference_images) are appended to the frontend's existing reference-image list instead of replacing it. Mutually exclusive with strict.",
- "default": false,
- "title": "Append"
+ "type": "string",
+ "description": "The destination to query",
+ "title": "Destination"
},
- "description": "When true, recalled reference images (ip_adapters and reference_images) are appended to the frontend's existing reference-image list instead of replacing it. Mutually exclusive with strict."
+ "description": "The destination to query"
}
],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/RecallParameter",
- "description": "Recall parameters to update"
- }
- }
- }
- },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "type": "object",
- "additionalProperties": true,
- "title": "Response Update Recall Parameters"
+ "$ref": "#/components/schemas/DeleteByDestinationResult"
}
}
}
@@ -9780,12 +9713,14 @@
}
}
}
- },
+ }
+ },
+ "/api/v1/workflows/i/{workflow_id}": {
"get": {
- "tags": ["recall"],
- "summary": "Get Recall Parameters",
- "description": "Retrieve all stored recall parameters for a given queue.\n\nReturns a dictionary of all recall parameters that have been set for the queue.\n\nArgs:\n queue_id: The queue ID to retrieve parameters for\n\nReturns:\n A dictionary containing all stored recall parameters",
- "operationId": "get_recall_parameters",
+ "tags": ["workflows"],
+ "summary": "Get Workflow",
+ "description": "Gets a workflow",
+ "operationId": "get_workflow",
"security": [
{
"HTTPBearer": []
@@ -9793,15 +9728,15 @@
],
"parameters": [
{
- "name": "queue_id",
+ "name": "workflow_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "description": "The queue id to retrieve parameters for",
- "title": "Queue Id"
+ "description": "The workflow to get",
+ "title": "Workflow Id"
},
- "description": "The queue id to retrieve parameters for"
+ "description": "The workflow to get"
}
],
"responses": {
@@ -9810,9 +9745,7 @@
"content": {
"application/json": {
"schema": {
- "type": "object",
- "additionalProperties": true,
- "title": "Response Get Recall Parameters"
+ "$ref": "#/components/schemas/WorkflowRecordWithThumbnailDTO"
}
}
}
@@ -9828,49 +9761,26 @@
}
}
}
- }
- },
- "/api/v2/custom_nodes/": {
- "get": {
- "tags": ["custom_nodes"],
- "summary": "List Custom Node Packs",
- "description": "Lists all installed custom node packs.\n\nAdmin-only: the response includes absolute filesystem paths, and non-admins have no\nlegitimate use for pack management data (install/uninstall/reload are also admin-only).",
- "operationId": "list_custom_node_packs",
- "responses": {
- "200": {
- "description": "Successful Response",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/NodePackListResponse"
- }
- }
- }
- }
- },
+ },
+ "patch": {
+ "tags": ["workflows"],
+ "summary": "Update Workflow",
+ "description": "Updates a workflow",
+ "operationId": "update_workflow",
"security": [
{
"HTTPBearer": []
}
- ]
- }
- },
- "/api/v2/custom_nodes/install": {
- "post": {
- "tags": ["custom_nodes"],
- "summary": "Install Custom Node Pack",
- "description": "Installs a custom node pack from a git URL by cloning it into the nodes directory.",
- "operationId": "install_custom_node_pack",
+ ],
"requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/InstallNodePackRequest",
- "description": "The source URL to install from."
+ "$ref": "#/components/schemas/Body_update_workflow"
}
}
- },
- "required": true
+ }
},
"responses": {
"200": {
@@ -9878,7 +9788,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/InstallNodePackResponse"
+ "$ref": "#/components/schemas/WorkflowRecordDTO"
}
}
}
@@ -9893,20 +9803,13 @@
}
}
}
- },
- "security": [
- {
- "HTTPBearer": []
- }
- ]
- }
- },
- "/api/v2/custom_nodes/{pack_name}": {
+ }
+ },
"delete": {
- "tags": ["custom_nodes"],
- "summary": "Uninstall Custom Node Pack",
- "description": "Uninstalls a custom node pack by removing its directory.\n\nNote: A restart is required for the node removal to take full effect.\nInstalled nodes from the pack will remain registered until restart.",
- "operationId": "uninstall_custom_node_pack",
+ "tags": ["workflows"],
+ "summary": "Delete Workflow",
+ "description": "Deletes a workflow",
+ "operationId": "delete_workflow",
"security": [
{
"HTTPBearer": []
@@ -9914,13 +9817,15 @@
],
"parameters": [
{
- "name": "pack_name",
+ "name": "workflow_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
- "title": "Pack Name"
- }
+ "description": "The workflow to delete",
+ "title": "Workflow Id"
+ },
+ "description": "The workflow to delete"
}
],
"responses": {
@@ -9928,9 +9833,7 @@
"description": "Successful Response",
"content": {
"application/json": {
- "schema": {
- "$ref": "#/components/schemas/UninstallNodePackResponse"
- }
+ "schema": {}
}
}
},
@@ -9947,1675 +9850,1746 @@
}
}
},
- "/api/v2/custom_nodes/reload": {
+ "/api/v1/workflows/": {
"post": {
- "tags": ["custom_nodes"],
- "summary": "Reload Custom Nodes",
- "description": "Triggers a reload of all custom nodes.\n\nThis re-scans the nodes directory and loads any new node packs.\nAlready loaded packs are skipped.",
- "operationId": "reload_custom_nodes",
+ "tags": ["workflows"],
+ "summary": "Create Workflow",
+ "description": "Creates a workflow",
+ "operationId": "create_workflow",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_create_workflow"
+ }
+ }
+ }
+ },
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
- "additionalProperties": {
- "type": "string"
- },
- "type": "object",
- "title": "Response Reload Custom Nodes"
+ "$ref": "#/components/schemas/WorkflowRecordDTO"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
- },
+ }
+ },
+ "get": {
+ "tags": ["workflows"],
+ "summary": "List Workflows",
+ "description": "Gets a page of workflows",
+ "operationId": "list_workflows",
"security": [
{
"HTTPBearer": []
}
- ]
- }
- }
- },
- "components": {
- "schemas": {
- "AddImagesToBoardResult": {
- "properties": {
- "affected_boards": {
- "items": {
- "type": "string"
+ ],
+ "parameters": [
+ {
+ "name": "page",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "description": "The page to get",
+ "default": 0,
+ "title": "Page"
},
- "type": "array",
- "title": "Affected Boards",
- "description": "The ids of boards affected by the delete operation"
+ "description": "The page to get"
},
- "added_images": {
- "items": {
- "type": "string"
+ {
+ "name": "per_page",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The number of workflows per page",
+ "title": "Per Page"
},
- "type": "array",
- "title": "Added Images",
- "description": "The image names that were added to the board"
- }
- },
- "type": "object",
- "required": ["affected_boards", "added_images"],
- "title": "AddImagesToBoardResult"
- },
- "AddInvocation": {
- "category": "math",
- "class": "invocation",
- "classification": "stable",
- "description": "Adds two numbers",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "description": "The number of workflows per page"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ {
+ "name": "order_by",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/WorkflowRecordOrderBy",
+ "description": "The attribute to order by",
+ "default": "name"
+ },
+ "description": "The attribute to order by"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ {
+ "name": "direction",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SQLiteDirection",
+ "description": "The direction to order by",
+ "default": "ASC"
+ },
+ "description": "The direction to order by"
},
- "a": {
- "default": 0,
- "description": "The first number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "A",
- "type": "integer"
+ {
+ "name": "categories",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WorkflowCategory"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories of workflow to get",
+ "title": "Categories"
+ },
+ "description": "The categories of workflow to get"
},
- "b": {
- "default": 0,
- "description": "The second number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "B",
- "type": "integer"
+ {
+ "name": "tags",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The tags of workflow to get",
+ "title": "Tags"
+ },
+ "description": "The tags of workflow to get"
},
- "type": {
- "const": "add",
- "default": "add",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["math", "add"],
- "title": "Add Integers",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/IntegerOutput"
- }
- },
- "AdminUserCreateRequest": {
- "properties": {
- "email": {
- "type": "string",
- "title": "Email",
- "description": "User email address"
+ {
+ "name": "query",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The text to query by (matches name and description)",
+ "title": "Query"
+ },
+ "description": "The text to query by (matches name and description)"
},
- "display_name": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Display Name",
- "description": "Display name"
+ {
+ "name": "has_been_opened",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to include/exclude recent workflows",
+ "title": "Has Been Opened"
+ },
+ "description": "Whether to include/exclude recent workflows"
},
- "password": {
- "type": "string",
- "title": "Password",
- "description": "User password"
+ {
+ "name": "is_public",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Filter by public/shared status",
+ "title": "Is Public"
+ },
+ "description": "Filter by public/shared status"
},
- "is_admin": {
- "type": "boolean",
- "title": "Is Admin",
- "description": "Whether user should have admin privileges",
- "default": false
+ {
+ "name": "callable",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Filter by whether workflows are callable by call_saved_workflow",
+ "title": "Callable"
+ },
+ "description": "Filter by whether workflows are callable by call_saved_workflow"
}
- },
- "type": "object",
- "required": ["email", "password"],
- "title": "AdminUserCreateRequest",
- "description": "Request body for admin to create a new user."
- },
- "AdminUserUpdateRequest": {
- "properties": {
- "display_name": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaginatedResults_WorkflowRecordListItemWithThumbnailDTO_"
+ }
}
- ],
- "title": "Display Name",
- "description": "Display name"
+ }
},
- "password": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "title": "Password",
- "description": "New password"
- },
- "is_admin": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/workflows/i/{workflow_id}/thumbnail": {
+ "put": {
+ "tags": ["workflows"],
+ "summary": "Set Workflow Thumbnail",
+ "description": "Sets a workflow's thumbnail image",
+ "operationId": "set_workflow_thumbnail",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "workflow_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The workflow to update",
+ "title": "Workflow Id"
+ },
+ "description": "The workflow to update"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_set_workflow_thumbnail"
}
- ],
- "title": "Is Admin",
- "description": "Whether user should have admin privileges"
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WorkflowRecordDTO"
+ }
+ }
+ }
},
- "is_active": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "title": "Is Active",
- "description": "Whether user account should be active"
+ }
}
- },
- "type": "object",
- "title": "AdminUserUpdateRequest",
- "description": "Request body for admin to update any user."
+ }
},
- "AlibabaCloudImageGenerationInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Generate images using an Alibaba Cloud DashScope external model.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
+ "delete": {
+ "tags": ["workflows"],
+ "summary": "Delete Workflow Thumbnail",
+ "description": "Removes a workflow's thumbnail image",
+ "operationId": "delete_workflow_thumbnail",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "workflow_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The workflow to update",
+ "title": "Workflow Id"
+ },
+ "description": "The workflow to update"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WorkflowRecordDTO"
+ }
}
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ }
},
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ }
+ }
+ }
+ },
+ "get": {
+ "tags": ["workflows"],
+ "summary": "Get Workflow Thumbnail",
+ "description": "Gets a workflow's thumbnail image.\n\nThis endpoint is intentionally unauthenticated because browsers load images\nvia tags which cannot send Bearer tokens. Workflow IDs are UUIDs,\nproviding security through unguessability.",
+ "operationId": "get_workflow_thumbnail",
+ "parameters": [
+ {
+ "name": "workflow_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The id of the workflow thumbnail to get",
+ "title": "Workflow Id"
+ },
+ "description": "The id of the workflow thumbnail to get"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The workflow thumbnail was fetched successfully",
+ "content": {
+ "application/json": {
+ "schema": {}
+ }
+ }
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "400": {
+ "description": "Bad request"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "404": {
+ "description": "The workflow thumbnail could not be found"
},
- "model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Main model (UNet, VAE, CLIP) to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_model_base": ["external"],
- "ui_model_format": ["external_api"],
- "ui_model_provider_id": ["alibabacloud"],
- "ui_model_type": ["external_image_generator"]
- },
- "mode": {
- "default": "txt2img",
- "description": "Generation mode. Not all modes are supported by every model; unsupported modes raise at runtime.",
- "enum": ["txt2img", "img2img", "inpaint"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "txt2img",
- "orig_required": false,
- "title": "Mode",
- "type": "string"
- },
- "prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/workflows/i/{workflow_id}/is_public": {
+ "patch": {
+ "tags": ["workflows"],
+ "summary": "Update Workflow Is Public",
+ "description": "Updates whether a workflow is shared publicly",
+ "operationId": "update_workflow_is_public",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "workflow_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The workflow to update",
+ "title": "Workflow Id"
+ },
+ "description": "The workflow to update"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_update_workflow_is_public"
}
- ],
- "default": null,
- "description": "Prompt",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Prompt"
- },
- "seed": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/WorkflowRecordDTO"
+ }
}
- ],
- "default": null,
- "description": "Seed for random number generation",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Seed"
- },
- "num_images": {
- "default": 1,
- "description": "Number of images to generate",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "Num Images",
- "type": "integer"
- },
- "width": {
- "default": 1024,
- "description": "Width of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
+ }
},
- "image_size": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Image size preset (e.g. 1K, 2K, 4K)",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Image Size"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/workflows/tags": {
+ "get": {
+ "tags": ["workflows"],
+ "summary": "Get All Tags",
+ "description": "Gets all unique tags from workflows",
+ "operationId": "get_all_tags",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "categories",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WorkflowCategory"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories to include",
+ "title": "Categories"
+ },
+ "description": "The categories to include"
},
- "init_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
+ {
+ "name": "is_public",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Filter by public/shared status",
+ "title": "Is Public"
+ },
+ "description": "Filter by public/shared status"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "Response Get All Tags"
+ }
}
- ],
- "default": null,
- "description": "Init image for img2img/inpaint",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
+ }
},
- "mask_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Mask image for inpaint",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
- },
- "reference_images": {
- "default": [],
- "description": "Reference images",
- "field_kind": "input",
- "input": "any",
- "items": {
- "$ref": "#/components/schemas/ImageField"
- },
- "orig_default": [],
- "orig_required": false,
- "title": "Reference Images",
- "type": "array"
- },
- "type": {
- "const": "alibabacloud_image_generation",
- "default": "alibabacloud_image_generation",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ }
}
- },
- "required": ["type", "id"],
- "tags": ["external", "generation", "alibabacloud", "dashscope"],
- "title": "Alibaba Cloud DashScope Image Generation",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageCollectionOutput"
}
- },
- "AlphaMaskToTensorInvocation": {
- "category": "mask",
- "class": "invocation",
- "classification": "stable",
- "description": "Convert a mask image to a tensor. Opaque regions are 1 and transparent regions are 0.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ }
+ },
+ "/api/v1/workflows/counts_by_tag": {
+ "get": {
+ "tags": ["workflows"],
+ "summary": "Get Counts By Tag",
+ "description": "Counts workflows by tag",
+ "operationId": "get_counts_by_tag",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "tags",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "The tags to get counts for",
+ "title": "Tags"
+ },
+ "description": "The tags to get counts for"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ {
+ "name": "categories",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WorkflowCategory"
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The categories to include",
+ "title": "Categories"
+ },
+ "description": "The categories to include"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ {
+ "name": "has_been_opened",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to include/exclude recent workflows",
+ "title": "Has Been Opened"
+ },
+ "description": "Whether to include/exclude recent workflows"
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
+ {
+ "name": "is_public",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Filter by public/shared status",
+ "title": "Is Public"
+ },
+ "description": "Filter by public/shared status"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer"
+ },
+ "title": "Response Get Counts By Tag"
+ }
}
- ],
- "default": null,
- "description": "The mask image to convert.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "invert": {
- "default": false,
- "description": "Whether to invert the mask.",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Invert",
- "type": "boolean"
+ }
},
- "type": {
- "const": "alpha_mask_to_tensor",
- "default": "alpha_mask_to_tensor",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
}
- },
- "required": ["type", "id"],
- "tags": ["conditioning"],
- "title": "Alpha Mask to Tensor",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/MaskOutput"
}
- },
- "AnimaConditioningField": {
- "description": "An Anima conditioning tensor primitive value.\n\nAnima conditioning contains Qwen3 0.6B hidden states and T5-XXL token IDs,\nwhich are combined by the LLM Adapter inside the transformer.",
- "properties": {
- "conditioning_name": {
- "description": "The name of conditioning tensor",
- "title": "Conditioning Name",
- "type": "string"
- },
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TensorField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The mask associated with this conditioning tensor for regional prompting. Excluded regions should be set to False, included regions should be set to True."
+ }
+ },
+ "/api/v1/workflows/counts_by_category": {
+ "get": {
+ "tags": ["workflows"],
+ "summary": "Counts By Category",
+ "description": "Counts workflows by category",
+ "operationId": "counts_by_category",
+ "security": [
+ {
+ "HTTPBearer": []
}
- },
- "required": ["conditioning_name"],
- "title": "AnimaConditioningField",
- "type": "object"
- },
- "AnimaConditioningOutput": {
- "class": "output",
- "description": "Base class for nodes that output an Anima text conditioning tensor.",
- "properties": {
- "conditioning": {
- "$ref": "#/components/schemas/AnimaConditioningField",
- "description": "Conditioning tensor",
- "field_kind": "output",
- "ui_hidden": false
+ ],
+ "parameters": [
+ {
+ "name": "categories",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WorkflowCategory"
+ },
+ "description": "The categories to include",
+ "title": "Categories"
+ },
+ "description": "The categories to include"
},
- "type": {
- "const": "anima_conditioning_output",
- "default": "anima_conditioning_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "conditioning", "type", "type"],
- "title": "AnimaConditioningOutput",
- "type": "object"
- },
- "AnimaDenoiseInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "prototype",
- "description": "Run the denoising process with an Anima model.\n\nUses rectified flow sampling with shift=3.0 and the Cosmos Predict2 DiT\nbackbone with integrated LLM Adapter for text conditioning.\n\nSupports txt2img, img2img (via latents input), and inpainting (via denoise_mask).",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "latents": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LatentsField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ {
+ "name": "has_been_opened",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Whether to include/exclude recent workflows",
+ "title": "Has Been Opened"
+ },
+ "description": "Whether to include/exclude recent workflows"
},
- "noise": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LatentsField"
- },
- {
- "type": "null"
+ {
+ "name": "is_public",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Filter by public/shared status",
+ "title": "Is Public"
+ },
+ "description": "Filter by public/shared status"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer"
+ },
+ "title": "Response Counts By Category"
+ }
}
- ],
- "default": null,
- "description": "Noise tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ }
},
- "denoise_mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/DenoiseMaskField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
- },
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
- },
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
- },
- "add_noise": {
- "default": true,
- "description": "Add noise based on denoising start.",
- "field_kind": "input",
- "input": "any",
- "orig_default": true,
- "orig_required": false,
- "title": "Add Noise",
- "type": "boolean"
- },
- "transformer": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TransformerField"
- },
- {
- "type": "null"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/workflows/i/{workflow_id}/opened_at": {
+ "put": {
+ "tags": ["workflows"],
+ "summary": "Update Opened At",
+ "description": "Updates the opened_at field of a workflow",
+ "operationId": "update_opened_at",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "workflow_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The workflow to update",
+ "title": "Workflow Id"
+ },
+ "description": "The workflow to update"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {}
}
- ],
- "default": null,
- "description": "Anima transformer model.",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Transformer"
+ }
},
- "positive_conditioning": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/AnimaConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/AnimaConditioningField"
- },
- "type": "array"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Positive Conditioning"
- },
- "negative_conditioning": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/AnimaConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/AnimaConditioningField"
- },
- "type": "array"
- },
- {
- "type": "null"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/style_presets/i/{style_preset_id}": {
+ "get": {
+ "tags": ["style_presets"],
+ "summary": "Get Style Preset",
+ "description": "Gets a style preset",
+ "operationId": "get_style_preset",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "style_preset_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The style preset to get",
+ "title": "Style Preset Id"
+ },
+ "description": "The style preset to get"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StylePresetRecordWithImage"
+ }
}
- ],
- "default": null,
- "description": "Negative conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Negative Conditioning"
- },
- "guidance_scale": {
- "default": 4.5,
- "description": "Guidance scale for classifier-free guidance. Recommended: 4.0-5.0 for Anima.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1.0,
- "orig_default": 4.5,
- "orig_required": false,
- "title": "Guidance Scale",
- "type": "number"
- },
- "width": {
- "default": 1024,
- "description": "Width of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 8,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 8,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
- },
- "steps": {
- "default": 30,
- "description": "Number of denoising steps. 30 recommended for Anima.",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 30,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
- },
- "seed": {
- "default": 0,
- "description": "Randomness seed for reproducibility.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ }
},
- "control_lllite": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/AnimaLLLiteField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/AnimaLLLiteField"
- },
- "type": "array"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Anima ControlNet-LLLite conditioning (e.g. inpaint adapter, control layers). Adapters are applied in a deterministic order (sorted by model key); each model may be used at most once.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Control Lllite"
- },
- "scheduler": {
- "default": "euler",
- "description": "Scheduler (sampler) for the denoising process.",
- "enum": ["euler", "heun", "dpmpp_2m", "dpmpp_2m_sde", "er_sde", "lcm"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "euler",
- "orig_required": false,
- "title": "Scheduler",
- "type": "string",
- "ui_choice_labels": {
- "dpmpp_2m": "DPM++ 2M",
- "dpmpp_2m_sde": "DPM++ 2M SDE",
- "er_sde": "ER-SDE",
- "euler": "Euler",
- "heun": "Heun (2nd order)",
- "lcm": "LCM"
}
- },
- "type": {
- "const": "anima_denoise",
- "default": "anima_denoise",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
}
- },
- "required": ["type", "id"],
- "tags": ["image", "anima"],
- "title": "Denoise - Anima",
- "type": "object",
- "version": "1.8.0",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
}
},
- "AnimaImageToLatentsInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "prototype",
- "description": "Generates latents from an image using the Anima VAE (supports Wan 2.1 and FLUX VAE).",
- "node_pack": "invokeai",
- "properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
+ "patch": {
+ "tags": ["style_presets"],
+ "summary": "Update Style Preset",
+ "description": "Updates a style preset",
+ "operationId": "update_style_preset",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "style_preset_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The id of the style preset to update",
+ "title": "Style Preset Id"
+ },
+ "description": "The id of the style preset to update"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_update_style_preset"
}
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StylePresetRecordWithImage"
+ }
}
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ }
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to encode.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "type": {
- "const": "anima_i2l",
- "default": "anima_i2l",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ }
}
- },
- "required": ["type", "id"],
- "tags": ["image", "latents", "vae", "i2l", "anima"],
- "title": "Image to Latents - Anima",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
}
},
- "AnimaLLLiteField": {
- "description": "An Anima ControlNet-LLLite conditioning field (e.g. inpaint adapter).",
- "properties": {
- "image_name": {
- "description": "The name of the conditioning image (the initial/raster image)",
- "title": "Image Name",
- "type": "string"
- },
- "mask_name": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
+ "delete": {
+ "tags": ["style_presets"],
+ "summary": "Delete Style Preset",
+ "description": "Deletes a style preset",
+ "operationId": "delete_style_preset",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "style_preset_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The style preset to delete",
+ "title": "Style Preset Id"
+ },
+ "description": "The style preset to delete"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {}
}
- ],
- "default": null,
- "description": "The name of the inpaint mask image (white = inpaint area)",
- "title": "Mask Name"
- },
- "control_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "The Anima ControlNet-LLLite adapter model"
- },
- "weight": {
- "default": 1.0,
- "description": "The strength of the LLLite adapter",
- "maximum": 10.0,
- "minimum": -10.0,
- "title": "Weight",
- "type": "number"
- },
- "begin_step_percent": {
- "default": 0.0,
- "description": "When the adapter is first applied (% of total steps)",
- "maximum": 1.0,
- "minimum": 0.0,
- "title": "Begin Step Percent",
- "type": "number"
+ }
},
- "end_step_percent": {
- "default": 1.0,
- "description": "When the adapter is last applied (% of total steps)",
- "maximum": 1.0,
- "minimum": 0.0,
- "title": "End Step Percent",
- "type": "number"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/style_presets/": {
+ "get": {
+ "tags": ["style_presets"],
+ "summary": "List Style Presets",
+ "description": "Gets the style presets visible to the current user.",
+ "operationId": "list_style_presets",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "items": {
+ "$ref": "#/components/schemas/StylePresetRecordWithImage"
+ },
+ "type": "array",
+ "title": "Response 200 List Style Presets"
+ }
+ }
+ }
}
},
- "required": ["image_name", "control_model"],
- "title": "AnimaLLLiteField",
- "type": "object"
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
},
- "AnimaLLLiteInvocation": {
- "category": "conditioning",
- "class": "invocation",
- "classification": "prototype",
- "description": "Configure an Anima ControlNet-LLLite adapter for model-level conditioning.\n\nTakes a conditioning image (the initial/raster image), an optional inpaint\nmask (white = area to inpaint), and a LLLite adapter model. Inpainting\nadapters (4-channel conditioning) require a mask; other adapters ignore it.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
+ "post": {
+ "tags": ["style_presets"],
+ "summary": "Create Style Preset",
+ "description": "Creates a style preset",
+ "operationId": "create_style_preset",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_create_style_preset"
}
- ],
- "default": null,
- "description": "The conditioning image (the initial/raster image for inpainting)",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ }
},
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StylePresetRecordWithImage"
+ }
}
- ],
- "default": null,
- "description": "The inpaint mask (white = area to inpaint). Required by inpainting adapters.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
+ }
},
- "control_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "ControlNet model to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Control Model",
- "ui_model_base": ["anima"],
- "ui_model_type": ["controlnet"]
- },
- "weight": {
- "default": 1.0,
- "description": "Strength of the LLLite adapter.",
- "field_kind": "input",
- "input": "any",
- "maximum": 10.0,
- "minimum": -10.0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Weight",
- "type": "number"
- },
- "begin_step_percent": {
- "default": 0.0,
- "description": "When the adapter is first applied (% of total steps)",
- "field_kind": "input",
- "input": "any",
- "maximum": 1.0,
- "minimum": 0.0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Begin Step Percent",
- "type": "number"
- },
- "end_step_percent": {
- "default": 1.0,
- "description": "When the adapter is last applied (% of total steps)",
- "field_kind": "input",
- "input": "any",
- "maximum": 1.0,
- "minimum": 0.0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "End Step Percent",
- "type": "number"
- },
- "type": {
- "const": "anima_lllite",
- "default": "anima_lllite",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ }
}
},
- "required": ["type", "id"],
- "tags": ["image", "anima", "control", "controlnet", "inpaint"],
- "title": "Anima ControlNet-LLLite",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/AnimaLLLiteOutput"
- }
- },
- "AnimaLLLiteOutput": {
- "class": "output",
- "description": "Anima ControlNet-LLLite output containing adapter configuration.",
- "properties": {
- "control": {
- "$ref": "#/components/schemas/AnimaLLLiteField",
- "description": "Anima ControlNet-LLLite conditioning",
- "field_kind": "output",
- "ui_hidden": false
- },
- "type": {
- "const": "anima_lllite_output",
- "default": "anima_lllite_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "security": [
+ {
+ "HTTPBearer": []
}
- },
- "required": ["output_meta", "control", "type", "type"],
- "title": "AnimaLLLiteOutput",
- "type": "object"
- },
- "AnimaLatentsToImageInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "prototype",
- "description": "Generates an image from latents using the Anima VAE.\n\nSupports the Wan 2.1 QwenImage VAE (AutoencoderKLWan) with explicit\nlatent denormalization, and FLUX VAE as fallback.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
+ ]
+ }
+ },
+ "/api/v1/style_presets/i/{style_preset_id}/image": {
+ "get": {
+ "tags": ["style_presets"],
+ "summary": "Get Style Preset Image",
+ "description": "Gets an image file that previews the model",
+ "operationId": "get_style_preset_image",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "style_preset_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The id of the style preset image to get",
+ "title": "Style Preset Id"
+ },
+ "description": "The id of the style preset image to get"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The style preset image was fetched successfully",
+ "content": {
+ "application/json": {
+ "schema": {}
}
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ }
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "400": {
+ "description": "Bad request"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "404": {
+ "description": "The style preset image could not be found"
},
- "latents": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LatentsField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/style_presets/export": {
+ "get": {
+ "tags": ["style_presets"],
+ "summary": "Export Style Presets",
+ "operationId": "export_style_presets",
+ "responses": {
+ "200": {
+ "description": "A CSV file with the requested data.",
+ "content": {
+ "application/json": {
+ "schema": {}
},
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "type": {
- "const": "anima_l2i",
- "default": "anima_l2i",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "text/csv": {}
+ }
}
},
- "required": ["type", "id"],
- "tags": ["latents", "image", "vae", "l2i", "anima"],
- "title": "Latents to Image - Anima",
- "type": "object",
- "version": "1.0.3",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "AnimaLoRACollectionLoader": {
- "category": "model",
- "class": "invocation",
- "classification": "prototype",
- "description": "Applies a collection of LoRAs to an Anima transformer.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "loras": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LoRAField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "type": "array"
- },
- {
- "type": "null"
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
+ }
+ },
+ "/api/v1/style_presets/import": {
+ "post": {
+ "tags": ["style_presets"],
+ "summary": "Import Style Presets",
+ "operationId": "import_style_presets",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/Body_import_style_presets"
}
- ],
- "default": null,
- "description": "LoRA models and weights. May be a single LoRA or collection.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "LoRAs",
- "ui_model_base": ["anima"],
- "ui_model_type": ["lora"]
+ }
},
- "transformer": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TransformerField"
- },
- {
- "type": "null"
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {}
}
- ],
- "default": null,
- "description": "Transformer",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Transformer"
+ }
},
- "qwen3_encoder": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Qwen3EncoderField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Qwen3 Encoder"
- },
- "type": {
- "const": "anima_lora_collection_loader",
- "default": "anima_lora_collection_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ }
}
},
- "required": ["type", "id"],
- "tags": ["lora", "model", "anima"],
- "title": "Apply LoRA Collection - Anima",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/AnimaLoRALoaderOutput"
- }
- },
- "AnimaLoRALoaderInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "prototype",
- "description": "Apply a LoRA model to an Anima transformer and/or Qwen3 text encoder.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
+ }
+ },
+ "/api/v1/client_state/{queue_id}/get_by_key": {
+ "get": {
+ "tags": ["client_state"],
+ "summary": "Get Client State By Key",
+ "description": "Gets the client state for the current user (or system user if not authenticated)",
+ "operationId": "get_client_state_by_key",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id (ignored, kept for backwards compatibility)",
+ "title": "Queue Id"
+ },
+ "description": "The queue id (ignored, kept for backwards compatibility)"
},
- "lora": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
+ {
+ "name": "key",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "Key to get",
+ "title": "Key"
+ },
+ "description": "Key to get"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Response Get Client State By Key"
+ }
}
- ],
- "default": null,
- "description": "LoRA model to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "LoRA",
- "ui_model_base": ["anima"],
- "ui_model_type": ["lora"]
- },
- "weight": {
- "default": 0.75,
- "description": "The weight at which the LoRA is applied to each model",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.75,
- "orig_required": false,
- "title": "Weight",
- "type": "number"
+ }
},
- "transformer": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TransformerField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Transformer",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Anima Transformer"
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/client_state/{queue_id}/set_by_key": {
+ "post": {
+ "tags": ["client_state"],
+ "summary": "Set Client State",
+ "description": "Sets the client state for the current user (or system user if not authenticated)",
+ "operationId": "set_client_state",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id (ignored, kept for backwards compatibility)",
+ "title": "Queue Id"
+ },
+ "description": "The queue id (ignored, kept for backwards compatibility)"
},
- "qwen3_encoder": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Qwen3EncoderField"
- },
- {
- "type": "null"
+ {
+ "name": "key",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "Key to set",
+ "title": "Key"
+ },
+ "description": "Key to set"
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string",
+ "description": "Stringified value to set",
+ "title": "Value"
}
- ],
- "default": null,
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Qwen3 Encoder"
- },
- "type": {
- "const": "anima_lora_loader",
- "default": "anima_lora_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ }
}
},
- "required": ["type", "id"],
- "tags": ["lora", "model", "anima"],
- "title": "Apply LoRA - Anima",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/AnimaLoRALoaderOutput"
- }
- },
- "AnimaLoRALoaderOutput": {
- "class": "output",
- "description": "Anima LoRA Loader Output",
- "properties": {
- "transformer": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TransformerField"
- },
- {
- "type": "null"
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "string",
+ "title": "Response Set Client State"
+ }
}
- ],
- "default": null,
- "description": "Transformer",
- "field_kind": "output",
- "title": "Anima Transformer",
- "ui_hidden": false
+ }
},
- "qwen3_encoder": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Qwen3EncoderField"
- },
- {
- "type": "null"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
}
- ],
- "default": null,
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "output",
- "title": "Qwen3 Encoder",
- "ui_hidden": false
- },
- "type": {
- "const": "anima_lora_loader_output",
- "default": "anima_lora_loader_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ }
}
- },
- "required": ["output_meta", "transformer", "qwen3_encoder", "type", "type"],
- "title": "AnimaLoRALoaderOutput",
- "type": "object"
- },
- "AnimaModelLoaderInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "prototype",
- "description": "Loads an Anima model, outputting its submodels.\n\nAnima uses:\n- Transformer: Cosmos Predict2 DiT + LLM Adapter (from single-file checkpoint)\n- Qwen3 Encoder: Qwen3 0.6B (standalone single-file)\n- VAE: AutoencoderKLQwenImage / Wan 2.1 VAE (standalone single-file or FLUX VAE)\n\nThe T5-XXL tokenizer needed for LLM Adapter token IDs is bundled in the package,\nso no T5-XXL encoder model needs to be installed.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Anima main model (transformer + LLM adapter).",
- "field_kind": "input",
- "input": "direct",
- "orig_required": true,
- "title": "Transformer",
- "ui_model_base": ["anima"],
- "ui_model_type": ["main"]
- },
- "vae_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Standalone VAE model. Anima uses a Wan 2.1 / QwenImage VAE (16-channel). A FLUX VAE can also be used as a compatible fallback.",
- "field_kind": "input",
- "input": "direct",
- "orig_required": true,
- "title": "VAE",
- "ui_model_type": ["vae"]
- },
- "qwen3_encoder_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Standalone Qwen3 0.6B Encoder model.",
- "field_kind": "input",
- "input": "direct",
- "orig_required": true,
- "title": "Qwen3 Encoder",
- "ui_model_type": ["qwen3_encoder"]
+ }
+ }
+ },
+ "/api/v1/client_state/{queue_id}/get_keys_by_prefix": {
+ "get": {
+ "tags": ["client_state"],
+ "summary": "Get Client State Keys By Prefix",
+ "description": "Gets client state keys matching a prefix for the current user",
+ "operationId": "get_client_state_keys_by_prefix",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id (ignored, kept for backwards compatibility)",
+ "title": "Queue Id"
+ },
+ "description": "The queue id (ignored, kept for backwards compatibility)"
},
- "type": {
- "const": "anima_model_loader",
- "default": "anima_model_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ {
+ "name": "prefix",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "Prefix to filter keys by",
+ "title": "Prefix"
+ },
+ "description": "Prefix to filter keys by"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "Response Get Client State Keys By Prefix"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/client_state/{queue_id}/delete_by_key": {
+ "post": {
+ "tags": ["client_state"],
+ "summary": "Delete Client State By Key",
+ "description": "Deletes a specific client state key for the current user",
+ "operationId": "delete_client_state_by_key",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id (ignored, kept for backwards compatibility)",
+ "title": "Queue Id"
+ },
+ "description": "The queue id (ignored, kept for backwards compatibility)"
+ },
+ {
+ "name": "key",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "Key to delete",
+ "title": "Key"
+ },
+ "description": "Key to delete"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {}
+ }
+ }
+ },
+ "204": {
+ "description": "Client state key deleted"
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/client_state/{queue_id}/delete": {
+ "post": {
+ "tags": ["client_state"],
+ "summary": "Delete Client State",
+ "description": "Deletes the client state for the current user (or system user if not authenticated)",
+ "operationId": "delete_client_state",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id (ignored, kept for backwards compatibility)",
+ "title": "Queue Id"
+ },
+ "description": "The queue id (ignored, kept for backwards compatibility)"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {}
+ }
+ }
+ },
+ "204": {
+ "description": "Client state deleted"
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/recall/{queue_id}": {
+ "post": {
+ "tags": ["recall"],
+ "summary": "Update Recall Parameters",
+ "description": "Update recallable parameters that can be recalled on the frontend.\n\nThis endpoint allows updating parameters such as prompt, model, steps, and other\ngeneration settings. These parameters are stored in client state and can be\naccessed by the frontend to populate UI elements.\n\nArgs:\n queue_id: The queue ID to associate these parameters with\n parameters: The RecallParameter object containing the parameters to update\n strict: When true, parameters not included in the request body are reset\n to their defaults (cleared on the frontend). Defaults to false,\n which preserves the existing behaviour of only updating the\n parameters that are explicitly provided.\n append: When true, recalled reference images (``ip_adapters`` and\n ``reference_images``) are appended to whatever reference images the\n frontend already has, instead of replacing the whole list. Mutually\n exclusive with ``strict`` (which clears omitted parameters).\n\nReturns:\n A dictionary containing the updated parameters and status\n\nExample:\n POST /api/v1/recall/{queue_id}?strict=true\n {\n \"positive_prompt\": \"a beautiful landscape\",\n \"model\": \"sd-1.5\",\n \"steps\": 20\n }\n # In strict mode, all other parameters (reference_images, loras, etc.)\n # are cleared. In non-strict mode (default) they would be left as-is.",
+ "operationId": "update_recall_parameters",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id to perform this operation on",
+ "title": "Queue Id"
+ },
+ "description": "The queue id to perform this operation on"
+ },
+ {
+ "name": "strict",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "When true, parameters not included in the request are reset to their defaults (cleared).",
+ "default": false,
+ "title": "Strict"
+ },
+ "description": "When true, parameters not included in the request are reset to their defaults (cleared)."
+ },
+ {
+ "name": "append",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "boolean",
+ "description": "When true, recalled reference images (ip_adapters and reference_images) are appended to the frontend's existing reference-image list instead of replacing it. Mutually exclusive with strict.",
+ "default": false,
+ "title": "Append"
+ },
+ "description": "When true, recalled reference images (ip_adapters and reference_images) are appended to the frontend's existing reference-image list instead of replacing it. Mutually exclusive with strict."
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RecallParameter",
+ "description": "Recall parameters to update"
+ }
+ }
}
},
- "required": ["model", "vae_model", "qwen3_encoder_model", "type", "id"],
- "tags": ["model", "anima"],
- "title": "Main Model - Anima",
- "type": "object",
- "version": "1.4.0",
- "output": {
- "$ref": "#/components/schemas/AnimaModelLoaderOutput"
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": true,
+ "title": "Response Update Recall Parameters"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
}
},
- "AnimaModelLoaderOutput": {
- "class": "output",
- "description": "Anima model loader output.",
- "properties": {
- "transformer": {
- "$ref": "#/components/schemas/TransformerField",
- "description": "Transformer",
- "field_kind": "output",
- "title": "Transformer",
- "ui_hidden": false
+ "get": {
+ "tags": ["recall"],
+ "summary": "Get Recall Parameters",
+ "description": "Retrieve all stored recall parameters for a given queue.\n\nReturns a dictionary of all recall parameters that have been set for the queue.\n\nArgs:\n queue_id: The queue ID to retrieve parameters for\n\nReturns:\n A dictionary containing all stored recall parameters",
+ "operationId": "get_recall_parameters",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "queue_id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "description": "The queue id to retrieve parameters for",
+ "title": "Queue Id"
+ },
+ "description": "The queue id to retrieve parameters for"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": true,
+ "title": "Response Get Recall Parameters"
+ }
+ }
+ }
},
- "qwen3_encoder": {
- "$ref": "#/components/schemas/Qwen3EncoderField",
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "output",
- "title": "Qwen3 Encoder",
- "ui_hidden": false
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/custom_nodes/": {
+ "get": {
+ "tags": ["custom_nodes"],
+ "summary": "List Custom Node Packs",
+ "description": "Lists all installed custom node packs.\n\nAdmin-only: the response includes absolute filesystem paths, and non-admins have no\nlegitimate use for pack management data (install/uninstall/reload are also admin-only).",
+ "operationId": "list_custom_node_packs",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NodePackListResponse"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
+ }
+ },
+ "/api/v2/custom_nodes/install": {
+ "post": {
+ "tags": ["custom_nodes"],
+ "summary": "Install Custom Node Pack",
+ "description": "Installs a custom node pack from a git URL by cloning it into the nodes directory.",
+ "operationId": "install_custom_node_pack",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InstallNodePackRequest",
+ "description": "The source URL to install from."
+ }
+ }
},
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InstallNodePackResponse"
+ }
+ }
+ }
},
- "type": {
- "const": "anima_model_loader_output",
- "default": "anima_model_loader_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
}
},
- "required": ["output_meta", "transformer", "qwen3_encoder", "vae", "type", "type"],
- "title": "AnimaModelLoaderOutput",
- "type": "object"
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
+ }
+ },
+ "/api/v2/custom_nodes/{pack_name}": {
+ "delete": {
+ "tags": ["custom_nodes"],
+ "summary": "Uninstall Custom Node Pack",
+ "description": "Uninstalls a custom node pack by removing its directory.\n\nNote: A restart is required for the node removal to take full effect.\nInstalled nodes from the pack will remain registered until restart.",
+ "operationId": "uninstall_custom_node_pack",
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ],
+ "parameters": [
+ {
+ "name": "pack_name",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "title": "Pack Name"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UninstallNodePackResponse"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/HTTPValidationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v2/custom_nodes/reload": {
+ "post": {
+ "tags": ["custom_nodes"],
+ "summary": "Reload Custom Nodes",
+ "description": "Triggers a reload of all custom nodes.\n\nThis re-scans the nodes directory and loads any new node packs.\nAlready loaded packs are skipped.",
+ "operationId": "reload_custom_nodes",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": "object",
+ "title": "Response Reload Custom Nodes"
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "HTTPBearer": []
+ }
+ ]
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "AddImagesToBoardResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the delete operation"
+ },
+ "added_images": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Added Images",
+ "description": "The image names that were added to the board"
+ }
+ },
+ "type": "object",
+ "required": ["affected_boards", "added_images"],
+ "title": "AddImagesToBoardResult"
},
- "AnimaTextEncoderInvocation": {
- "category": "conditioning",
+ "AddInvocation": {
+ "category": "math",
"class": "invocation",
- "classification": "prototype",
- "description": "Encodes and preps a prompt for an Anima image.\n\nUses Qwen3 0.6B for hidden state extraction and a bundled T5-XXL tokenizer for\ntoken IDs (no T5 model weights needed). Both are combined by the\nLLM Adapter inside the Anima transformer during denoising.",
+ "classification": "stable",
+ "description": "Adds two numbers",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -11642,493 +11616,162 @@
"title": "Use Cache",
"type": "boolean"
},
- "prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Text prompt to encode.",
+ "a": {
+ "default": 0,
+ "description": "The first number",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Prompt",
- "ui_component": "textarea"
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "A",
+ "type": "integer"
},
- "qwen3_encoder": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Qwen3EncoderField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Qwen3 Encoder"
- },
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TensorField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "A mask defining the region that this conditioning prompt applies to.",
+ "b": {
+ "default": 0,
+ "description": "The second number",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "B",
+ "type": "integer"
},
"type": {
- "const": "anima_text_encoder",
- "default": "anima_text_encoder",
+ "const": "add",
+ "default": "add",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["prompt", "conditioning", "anima"],
- "title": "Prompt - Anima",
+ "tags": ["math", "add"],
+ "title": "Add Integers",
"type": "object",
- "version": "1.4.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/AnimaConditioningOutput"
+ "$ref": "#/components/schemas/IntegerOutput"
}
},
- "AnyModelConfig": {
- "oneOf": [
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/ExternalApiModelConfig"
+ "AddVideosToBoardResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the operation"
},
- {
- "$ref": "#/components/schemas/Unknown_Config"
+ "added_videos": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Added Videos",
+ "description": "The video names that were added to the board"
}
- ]
+ },
+ "type": "object",
+ "required": ["affected_boards", "added_videos"],
+ "title": "AddVideosToBoardResult"
},
- "AppVersion": {
+ "AdminUserCreateRequest": {
"properties": {
- "version": {
+ "email": {
"type": "string",
- "title": "Version",
- "description": "App version"
+ "title": "Email",
+ "description": "User email address"
+ },
+ "display_name": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Display Name",
+ "description": "Display name"
+ },
+ "password": {
+ "type": "string",
+ "title": "Password",
+ "description": "User password"
+ },
+ "is_admin": {
+ "type": "boolean",
+ "title": "Is Admin",
+ "description": "Whether user should have admin privileges",
+ "default": false
}
},
"type": "object",
- "required": ["version"],
- "title": "AppVersion",
- "description": "App Version Response"
+ "required": ["email", "password"],
+ "title": "AdminUserCreateRequest",
+ "description": "Request body for admin to create a new user."
},
- "ApplyMaskTensorToImageInvocation": {
- "category": "mask",
- "class": "invocation",
- "classification": "stable",
- "description": "Applies a tensor mask to an image.\n\nThe image is converted to RGBA and the mask is applied to the alpha channel.",
- "node_pack": "invokeai",
+ "AdminUserUpdateRequest": {
"properties": {
- "board": {
+ "display_name": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Display Name",
+ "description": "Display name"
},
- "metadata": {
+ "password": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Password",
+ "description": "New password"
},
- "mask": {
+ "is_admin": {
"anyOf": [
{
- "$ref": "#/components/schemas/TensorField"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The mask tensor to apply.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Is Admin",
+ "description": "Whether user should have admin privileges"
},
- "image": {
+ "is_active": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to apply the mask to.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "invert": {
- "default": false,
- "description": "Whether to invert the mask.",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Invert",
- "type": "boolean"
- },
- "type": {
- "const": "apply_tensor_mask_to_image",
- "default": "apply_tensor_mask_to_image",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Is Active",
+ "description": "Whether user account should be active"
}
},
- "required": ["type", "id"],
- "tags": ["mask"],
- "title": "Apply Tensor Mask to Image",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "title": "AdminUserUpdateRequest",
+ "description": "Request body for admin to update any user."
},
- "ApplyMaskToImageInvocation": {
- "category": "mask",
+ "AlibabaCloudImageGenerationInvocation": {
+ "category": "image",
"class": "invocation",
"classification": "stable",
- "description": "Extracts a region from a generated image using a mask and blends it seamlessly onto a source image.\nThe mask uses black to indicate areas to keep from the generated image and white for areas to discard.",
+ "description": "Generate images using an Alibaba Cloud DashScope external model.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -12187,123 +11830,103 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "model": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image from which to extract the masked region",
+ "description": "Main model (UNet, VAE, CLIP) to load",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_required": true,
+ "ui_model_base": ["external"],
+ "ui_model_format": ["external_api"],
+ "ui_model_provider_id": ["alibabacloud"],
+ "ui_model_type": ["external_image_generator"]
},
- "mask": {
+ "mode": {
+ "default": "txt2img",
+ "description": "Generation mode. Not all modes are supported by every model; unsupported modes raise at runtime.",
+ "enum": ["txt2img", "img2img", "inpaint"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "txt2img",
+ "orig_required": false,
+ "title": "Mode",
+ "type": "string"
+ },
+ "prompt": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "The mask defining the region (black=keep, white=discard)",
+ "description": "Prompt",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_required": true,
+ "title": "Prompt"
},
- "invert_mask": {
- "default": false,
- "description": "Whether to invert the mask before applying it",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Invert Mask",
- "type": "boolean"
- },
- "type": {
- "const": "apply_mask_to_image",
- "default": "apply_mask_to_image",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "mask", "blend"],
- "title": "Apply Mask to Image",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "BaseMetadata": {
- "properties": {
- "name": {
- "type": "string",
- "title": "Name",
- "description": "model's name"
- },
- "type": {
- "type": "string",
- "const": "basemetadata",
- "title": "Type",
- "default": "basemetadata"
- }
- },
- "type": "object",
- "required": ["name"],
- "title": "BaseMetadata",
- "description": "Adds typing data for discriminated union."
- },
- "BaseModelType": {
- "type": "string",
- "enum": [
- "any",
- "sd-1",
- "sd-2",
- "sd-3",
- "sdxl",
- "sdxl-refiner",
- "flux",
- "flux2",
- "cogview4",
- "z-image",
- "external",
- "qwen-image",
- "anima",
- "unknown"
- ],
- "title": "BaseModelType",
- "description": "An enumeration of base model architectures. For example, Stable Diffusion 1.x, Stable Diffusion 2.x, FLUX, etc.\n\nEvery model config must have a base architecture type.\n\nNot all models are associated with a base architecture. For example, CLIP models are their own thing, not related\nto any particular model architecture. To simplify internal APIs and make it easier to work with models, we use a\nfallback/null value `BaseModelType.Any` for these models, instead of making the model base optional."
- },
- "Batch": {
- "properties": {
- "batch_id": {
- "type": "string",
- "title": "Batch Id",
- "description": "The ID of the batch"
- },
- "origin": {
+ "seed": {
"anyOf": [
{
- "type": "string"
+ "type": "integer"
},
{
"type": "null"
}
],
- "title": "Origin",
- "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results."
+ "default": null,
+ "description": "Seed for random number generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Seed"
},
- "destination": {
+ "num_images": {
+ "default": 1,
+ "description": "Number of images to generate",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Num Images",
+ "type": "integer"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Width of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "image_size": {
"anyOf": [
{
"type": "string"
@@ -12312,295 +11935,203 @@
"type": "null"
}
],
- "title": "Destination",
- "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results"
+ "default": null,
+ "description": "Image size preset (e.g. 1K, 2K, 4K)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Image Size"
},
- "data": {
+ "init_image": {
"anyOf": [
{
- "items": {
- "items": {
- "$ref": "#/components/schemas/BatchDatum"
- },
- "type": "array"
- },
- "type": "array"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Data",
- "description": "The batch data collection."
- },
- "graph": {
- "$ref": "#/components/schemas/Graph",
- "description": "The graph to initialize the session with"
+ "default": null,
+ "description": "Init image for img2img/inpaint",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "workflow": {
+ "mask_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/WorkflowWithoutID"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "description": "The workflow to initialize the session with"
- },
- "runs": {
- "type": "integer",
- "minimum": 1.0,
- "title": "Runs",
- "description": "Int stating how many times to iterate through all possible batch indices",
- "default": 1
- }
- },
- "type": "object",
- "required": ["graph", "runs"],
- "title": "Batch"
- },
- "BatchDatum": {
- "properties": {
- "node_path": {
- "type": "string",
- "title": "Node Path",
- "description": "The node into which this batch data collection will be substituted."
- },
- "field_name": {
- "type": "string",
- "title": "Field Name",
- "description": "The field into which this batch data collection will be substituted."
+ "default": null,
+ "description": "Mask image for inpaint",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "items": {
+ "reference_images": {
+ "default": [],
+ "description": "Reference images",
+ "field_kind": "input",
+ "input": "any",
"items": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "number"
- },
- {
- "type": "integer"
- },
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "items": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "number"
- },
- {
- "type": "integer"
- },
- {
- "$ref": "#/components/schemas/ImageField"
- }
- ]
- },
- "type": "array"
- }
- ]
+ "$ref": "#/components/schemas/ImageField"
},
- "type": "array",
- "title": "Items",
- "description": "The list of items to substitute into the node/field."
+ "orig_default": [],
+ "orig_required": false,
+ "title": "Reference Images",
+ "type": "array"
+ },
+ "type": {
+ "const": "alibabacloud_image_generation",
+ "default": "alibabacloud_image_generation",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["external", "generation", "alibabacloud", "dashscope"],
+ "title": "Alibaba Cloud DashScope Image Generation",
"type": "object",
- "required": ["node_path", "field_name"],
- "title": "BatchDatum"
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageCollectionOutput"
+ }
},
- "BatchEnqueuedEvent": {
- "description": "Event model for batch_enqueued",
+ "AlphaMaskToTensorInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Convert a mask image to a tensor. Opaque regions are 1 and transparent regions are 0.",
+ "node_pack": "invokeai",
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "queue_id": {
- "description": "The ID of the queue",
- "title": "Queue Id",
- "type": "string"
- },
- "batch_id": {
- "description": "The ID of the batch",
- "title": "Batch Id",
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
"type": "string"
},
- "enqueued": {
- "description": "The number of invocations enqueued",
- "title": "Enqueued",
- "type": "integer"
- },
- "requested": {
- "description": "The number of invocations initially requested to be enqueued (may be less than enqueued if queue was full)",
- "title": "Requested",
- "type": "integer"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "priority": {
- "description": "The priority of the batch",
- "title": "Priority",
- "type": "integer"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "origin": {
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The origin of the batch",
- "title": "Origin"
+ "description": "The mask image to convert.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "user_id": {
- "default": "system",
- "description": "The ID of the user who enqueued the batch",
- "title": "User Id",
+ "invert": {
+ "default": false,
+ "description": "Whether to invert the mask.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Invert",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "alpha_mask_to_tensor",
+ "default": "alpha_mask_to_tensor",
+ "field_kind": "node_attribute",
+ "title": "type",
"type": "string"
}
},
- "required": ["timestamp", "queue_id", "batch_id", "enqueued", "requested", "priority", "origin", "user_id"],
- "title": "BatchEnqueuedEvent",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["conditioning"],
+ "title": "Alpha Mask to Tensor",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/MaskOutput"
+ }
},
- "BatchStatus": {
+ "AnimaConditioningField": {
+ "description": "An Anima conditioning tensor primitive value.\n\nAnima conditioning contains Qwen3 0.6B hidden states and T5-XXL token IDs,\nwhich are combined by the LLM Adapter inside the transformer.",
"properties": {
- "queue_id": {
- "type": "string",
- "title": "Queue Id",
- "description": "The ID of the queue"
- },
- "batch_id": {
- "type": "string",
- "title": "Batch Id",
- "description": "The ID of the batch"
- },
- "origin": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Origin",
- "description": "The origin of the batch"
+ "conditioning_name": {
+ "description": "The name of conditioning tensor",
+ "title": "Conditioning Name",
+ "type": "string"
},
- "destination": {
+ "mask": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/TensorField"
},
{
"type": "null"
}
],
- "title": "Destination",
- "description": "The destination of the batch"
- },
- "pending": {
- "type": "integer",
- "title": "Pending",
- "description": "Number of queue items with status 'pending'"
- },
- "in_progress": {
- "type": "integer",
- "title": "In Progress",
- "description": "Number of queue items with status 'in_progress'"
- },
- "waiting": {
- "type": "integer",
- "title": "Waiting",
- "description": "Number of queue items with status 'waiting'"
- },
- "completed": {
- "type": "integer",
- "title": "Completed",
- "description": "Number of queue items with status 'complete'"
- },
- "failed": {
- "type": "integer",
- "title": "Failed",
- "description": "Number of queue items with status 'error'"
- },
- "canceled": {
- "type": "integer",
- "title": "Canceled",
- "description": "Number of queue items with status 'canceled'"
+ "default": null,
+ "description": "The mask associated with this conditioning tensor for regional prompting. Excluded regions should be set to False, included regions should be set to True."
+ }
+ },
+ "required": ["conditioning_name"],
+ "title": "AnimaConditioningField",
+ "type": "object"
+ },
+ "AnimaConditioningOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output an Anima text conditioning tensor.",
+ "properties": {
+ "conditioning": {
+ "$ref": "#/components/schemas/AnimaConditioningField",
+ "description": "Conditioning tensor",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "total": {
- "type": "integer",
- "title": "Total",
- "description": "Total number of queue items"
+ "type": {
+ "const": "anima_conditioning_output",
+ "default": "anima_conditioning_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "type": "object",
- "required": [
- "queue_id",
- "batch_id",
- "origin",
- "destination",
- "pending",
- "in_progress",
- "waiting",
- "completed",
- "failed",
- "canceled",
- "total"
- ],
- "title": "BatchStatus"
+ "required": ["output_meta", "conditioning", "type", "type"],
+ "title": "AnimaConditioningOutput",
+ "type": "object"
},
- "BlankImageInvocation": {
+ "AnimaDenoiseInvocation": {
"category": "image",
"class": "invocation",
- "classification": "stable",
- "description": "Creates a blank image and forwards it to the pipeline",
+ "classification": "prototype",
+ "description": "Run the denoising process with an Anima model.\n\nUses rectified flow sampling with shift=3.0 and the Cosmos Predict2 DiT\nbackbone with integrated LLM Adapter for text conditioning.\n\nSupports txt2img, img2img (via latents input), and inpainting (via denoise_mask).",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -12625,565 +12156,730 @@
"title": "Use Cache",
"type": "boolean"
},
- "width": {
- "default": 512,
- "description": "The width of the image",
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
"field_kind": "input",
- "input": "any",
- "orig_default": 512,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "height": {
- "default": 512,
- "description": "The height of the image",
+ "noise": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Noise tensor",
"field_kind": "input",
- "input": "any",
- "orig_default": 512,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "mode": {
- "default": "RGB",
- "description": "The mode of the image",
- "enum": ["RGB", "RGBA"],
+ "denoise_mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/DenoiseMaskField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
"field_kind": "input",
"input": "any",
- "orig_default": "RGB",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
"orig_required": false,
- "title": "Mode",
- "type": "string"
+ "title": "Denoising Start",
+ "type": "number"
},
- "color": {
- "$ref": "#/components/schemas/ColorField",
- "default": {
- "r": 0,
- "g": 0,
- "b": 0,
- "a": 255
- },
- "description": "The color of the image",
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
"field_kind": "input",
"input": "any",
- "orig_default": {
- "a": 255,
- "b": 0,
- "g": 0,
- "r": 0
- },
- "orig_required": false
- },
- "type": {
- "const": "blank_image",
- "default": "blank_image",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image"],
- "title": "Blank Image",
- "type": "object",
- "version": "1.2.2",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "BlendLatentsInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "stable",
- "description": "Blend two latents using a given alpha. If a mask is provided, the second latents will be masked before blending.\nLatents must have same size. Masking functionality added by @dwringer.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
},
- "use_cache": {
+ "add_noise": {
"default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
+ "description": "Add noise based on denoising start.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": true,
+ "orig_required": false,
+ "title": "Add Noise",
"type": "boolean"
},
- "latents_a": {
+ "transformer": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Latents tensor",
+ "description": "Anima transformer model.",
"field_kind": "input",
"input": "connection",
- "orig_required": true
+ "orig_required": true,
+ "title": "Transformer"
},
- "latents_b": {
+ "positive_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "$ref": "#/components/schemas/AnimaConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/AnimaConditioningField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Latents tensor",
+ "description": "Positive conditioning tensor",
"field_kind": "input",
"input": "connection",
- "orig_required": true
+ "orig_required": true,
+ "title": "Positive Conditioning"
},
- "mask": {
+ "negative_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/AnimaConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/AnimaConditioningField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Mask for blending in latents B",
+ "description": "Negative conditioning tensor",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_default": null,
- "orig_required": false
+ "orig_required": false,
+ "title": "Negative Conditioning"
},
- "alpha": {
- "default": 0.5,
- "description": "Blending factor. 0.0 = use input A only, 1.0 = use input B only, 0.5 = 50% mix of input A and input B.",
+ "guidance_scale": {
+ "default": 4.5,
+ "description": "Guidance scale for classifier-free guidance. Recommended: 4.0-5.0 for Anima.",
"field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 0.5,
+ "minimum": 1.0,
+ "orig_default": 4.5,
"orig_required": false,
- "title": "Alpha",
+ "title": "Guidance Scale",
"type": "number"
},
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 8,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 8,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "steps": {
+ "default": 30,
+ "description": "Number of denoising steps. 30 recommended for Anima.",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 30,
+ "orig_required": false,
+ "title": "Steps",
+ "type": "integer"
+ },
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
+ },
+ "control_lllite": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/AnimaLLLiteField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/AnimaLLLiteField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Anima ControlNet-LLLite conditioning (e.g. inpaint adapter, control layers). Adapters are applied in a deterministic order (sorted by model key); each model may be used at most once.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Control Lllite"
+ },
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler (sampler) for the denoising process.",
+ "enum": ["euler", "heun", "dpmpp_2m", "dpmpp_2m_sde", "er_sde", "lcm"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Scheduler",
+ "type": "string",
+ "ui_choice_labels": {
+ "dpmpp_2m": "DPM++ 2M",
+ "dpmpp_2m_sde": "DPM++ 2M SDE",
+ "er_sde": "ER-SDE",
+ "euler": "Euler",
+ "heun": "Heun (2nd order)",
+ "lcm": "LCM"
+ }
+ },
"type": {
- "const": "lblend",
- "default": "lblend",
+ "const": "anima_denoise",
+ "default": "anima_denoise",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["latents", "blend", "mask"],
- "title": "Blend Latents",
+ "tags": ["image", "anima"],
+ "title": "Denoise - Anima",
"type": "object",
- "version": "1.1.0",
+ "version": "1.8.0",
"output": {
"$ref": "#/components/schemas/LatentsOutput"
}
},
- "BoardChanges": {
+ "AnimaImageToLatentsInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Generates latents from an image using the Anima VAE (supports Wan 2.1 and FLUX VAE).",
+ "node_pack": "invokeai",
"properties": {
- "board_name": {
+ "board": {
"anyOf": [
{
- "type": "string",
- "maxLength": 300
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Board Name",
- "description": "The board's new name."
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "cover_image_name": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Cover Image Name",
- "description": "The name of the board's new cover image."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "archived": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "boolean"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Archived",
- "description": "Whether or not the board is archived"
+ "default": null,
+ "description": "The image to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "board_visibility": {
+ "vae": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardVisibility"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "description": "The visibility of the board."
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "type": {
+ "const": "anima_i2l",
+ "default": "anima_i2l",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "additionalProperties": false,
+ "required": ["type", "id"],
+ "tags": ["image", "latents", "vae", "i2l", "anima"],
+ "title": "Image to Latents - Anima",
"type": "object",
- "title": "BoardChanges"
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
- "BoardDTO": {
+ "AnimaLLLiteField": {
+ "description": "An Anima ControlNet-LLLite conditioning field (e.g. inpaint adapter).",
"properties": {
- "board_id": {
- "type": "string",
- "title": "Board Id",
- "description": "The unique ID of the board."
- },
- "board_name": {
- "type": "string",
- "title": "Board Name",
- "description": "The name of the board."
- },
- "user_id": {
- "type": "string",
- "title": "User Id",
- "description": "The user ID of the board owner."
+ "image_name": {
+ "description": "The name of the conditioning image (the initial/raster image)",
+ "title": "Image Name",
+ "type": "string"
},
- "created_at": {
+ "mask_name": {
"anyOf": [
{
- "type": "string",
- "format": "date-time"
+ "type": "string"
},
{
- "type": "string"
+ "type": "null"
}
],
- "title": "Created At",
- "description": "The created timestamp of the board."
+ "default": null,
+ "description": "The name of the inpaint mask image (white = inpaint area)",
+ "title": "Mask Name"
},
- "updated_at": {
+ "control_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "The Anima ControlNet-LLLite adapter model"
+ },
+ "weight": {
+ "default": 1.0,
+ "description": "The strength of the LLLite adapter",
+ "maximum": 10.0,
+ "minimum": -10.0,
+ "title": "Weight",
+ "type": "number"
+ },
+ "begin_step_percent": {
+ "default": 0.0,
+ "description": "When the adapter is first applied (% of total steps)",
+ "maximum": 1.0,
+ "minimum": 0.0,
+ "title": "Begin Step Percent",
+ "type": "number"
+ },
+ "end_step_percent": {
+ "default": 1.0,
+ "description": "When the adapter is last applied (% of total steps)",
+ "maximum": 1.0,
+ "minimum": 0.0,
+ "title": "End Step Percent",
+ "type": "number"
+ }
+ },
+ "required": ["image_name", "control_model"],
+ "title": "AnimaLLLiteField",
+ "type": "object"
+ },
+ "AnimaLLLiteInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Configure an Anima ControlNet-LLLite adapter for model-level conditioning.\n\nTakes a conditioning image (the initial/raster image), an optional inpaint\nmask (white = area to inpaint), and a LLLite adapter model. Inpainting\nadapters (4-channel conditioning) require a mask; other adapters ignore it.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string",
- "format": "date-time"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "type": "string"
+ "type": "null"
}
],
- "title": "Updated At",
- "description": "The updated timestamp of the board."
+ "default": null,
+ "description": "The conditioning image (the initial/raster image for inpainting)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "deleted_at": {
+ "mask": {
"anyOf": [
{
- "type": "string",
- "format": "date-time"
- },
- {
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Deleted At",
- "description": "The deleted timestamp of the board."
+ "default": null,
+ "description": "The inpaint mask (white = area to inpaint). Required by inpainting adapters.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "cover_image_name": {
+ "control_model": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Cover Image Name",
- "description": "The name of the board's cover image."
+ "default": null,
+ "description": "ControlNet model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Control Model",
+ "ui_model_base": ["anima"],
+ "ui_model_type": ["controlnet"]
},
- "archived": {
- "type": "boolean",
- "title": "Archived",
- "description": "Whether or not the board is archived."
- },
- "board_visibility": {
- "$ref": "#/components/schemas/BoardVisibility",
- "description": "The visibility of the board.",
- "default": "private"
+ "weight": {
+ "default": 1.0,
+ "description": "Strength of the LLLite adapter.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 10.0,
+ "minimum": -10.0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Weight",
+ "type": "number"
},
- "image_count": {
- "type": "integer",
- "title": "Image Count",
- "description": "The number of images in the board."
+ "begin_step_percent": {
+ "default": 0.0,
+ "description": "When the adapter is first applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1.0,
+ "minimum": 0.0,
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Begin Step Percent",
+ "type": "number"
},
- "asset_count": {
- "type": "integer",
- "title": "Asset Count",
- "description": "The number of assets in the board."
+ "end_step_percent": {
+ "default": 1.0,
+ "description": "When the adapter is last applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1.0,
+ "minimum": 0.0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "End Step Percent",
+ "type": "number"
},
- "owner_username": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Owner Username",
- "description": "The username of the board owner (for admin view)."
- }
- },
- "type": "object",
- "required": [
- "board_id",
- "board_name",
- "user_id",
- "created_at",
- "updated_at",
- "cover_image_name",
- "archived",
- "image_count",
- "asset_count"
- ],
- "title": "BoardDTO",
- "description": "Deserialized board record with cover image URL and image count."
- },
- "BoardField": {
- "description": "A board primitive field",
- "properties": {
- "board_id": {
- "description": "The id of the board",
- "title": "Board Id",
+ "type": {
+ "const": "anima_lllite",
+ "default": "anima_lllite",
+ "field_kind": "node_attribute",
+ "title": "type",
"type": "string"
}
},
- "required": ["board_id"],
- "title": "BoardField",
- "type": "object"
- },
- "BoardRecordOrderBy": {
- "type": "string",
- "enum": ["created_at", "board_name"],
- "title": "BoardRecordOrderBy",
- "description": "The order by options for board records"
- },
- "BoardVisibility": {
- "type": "string",
- "enum": ["private", "shared", "public"],
- "title": "BoardVisibility",
- "description": "The visibility options for a board."
- },
- "Body_add_image_to_board": {
- "properties": {
- "board_id": {
- "type": "string",
- "title": "Board Id",
- "description": "The id of the board to add to"
- },
- "image_name": {
- "type": "string",
- "title": "Image Name",
- "description": "The name of the image to add"
- }
- },
+ "required": ["type", "id"],
+ "tags": ["image", "anima", "control", "controlnet", "inpaint"],
+ "title": "Anima ControlNet-LLLite",
"type": "object",
- "required": ["board_id", "image_name"],
- "title": "Body_add_image_to_board"
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/AnimaLLLiteOutput"
+ }
},
- "Body_add_images_to_board": {
+ "AnimaLLLiteOutput": {
+ "class": "output",
+ "description": "Anima ControlNet-LLLite output containing adapter configuration.",
"properties": {
- "board_id": {
- "type": "string",
- "title": "Board Id",
- "description": "The id of the board to add to"
+ "control": {
+ "$ref": "#/components/schemas/AnimaLLLiteField",
+ "description": "Anima ControlNet-LLLite conditioning",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "image_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Image Names",
- "description": "The names of the images to add"
- }
- },
- "type": "object",
- "required": ["board_id", "image_names"],
- "title": "Body_add_images_to_board"
- },
- "Body_cancel_by_batch_ids": {
- "properties": {
- "batch_ids": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Batch Ids",
- "description": "The list of batch_ids to cancel all queue items for"
+ "type": {
+ "const": "anima_lllite_output",
+ "default": "anima_lllite_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "type": "object",
- "required": ["batch_ids"],
- "title": "Body_cancel_by_batch_ids"
+ "required": ["output_meta", "control", "type", "type"],
+ "title": "AnimaLLLiteOutput",
+ "type": "object"
},
- "Body_create_image_upload_entry": {
+ "AnimaLatentsToImageInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Generates an image from latents using the Anima VAE.\n\nSupports the Wan 2.1 QwenImage VAE (AutoencoderKLWan) with explicit\nlatent denormalization, and FLUX VAE as fallback.",
+ "node_pack": "invokeai",
"properties": {
- "width": {
- "type": "integer",
- "title": "Width",
- "description": "The width of the image"
- },
- "height": {
- "type": "integer",
- "title": "Height",
- "description": "The height of the image"
- },
- "board_id": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Board Id",
- "description": "The board to add this image to, if any"
- }
- },
- "type": "object",
- "required": ["width", "height"],
- "title": "Body_create_image_upload_entry"
- },
- "Body_create_style_preset": {
- "properties": {
- "image": {
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
"anyOf": [
{
- "type": "string",
- "format": "binary"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Image",
- "description": "The image file to upload"
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "data": {
- "type": "string",
- "title": "Data",
- "description": "The data of the style preset to create"
- }
- },
- "type": "object",
- "required": ["data"],
- "title": "Body_create_style_preset"
- },
- "Body_create_workflow": {
- "properties": {
- "workflow": {
- "$ref": "#/components/schemas/WorkflowWithoutID",
- "description": "The workflow to create"
- }
- },
- "type": "object",
- "required": ["workflow"],
- "title": "Body_create_workflow"
- },
- "Body_delete_images_from_list": {
- "properties": {
- "image_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Image Names",
- "description": "The list of names of images to delete"
- }
- },
- "type": "object",
- "required": ["image_names"],
- "title": "Body_delete_images_from_list"
- },
- "Body_do_hf_login": {
- "properties": {
- "token": {
- "type": "string",
- "title": "Token",
- "description": "Hugging Face token to use for login"
- }
- },
- "type": "object",
- "required": ["token"],
- "title": "Body_do_hf_login"
- },
- "Body_download": {
- "properties": {
- "source": {
- "type": "string",
- "minLength": 1,
- "format": "uri",
- "title": "Source",
- "description": "download source"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "dest": {
- "type": "string",
- "title": "Dest",
- "description": "download destination"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "priority": {
- "type": "integer",
- "title": "Priority",
- "description": "queue priority",
- "default": 10
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "access_token": {
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Access Token",
- "description": "token for authorization to download"
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "type": {
+ "const": "anima_l2i",
+ "default": "anima_l2i",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "l2i", "anima"],
+ "title": "Latents to Image - Anima",
"type": "object",
- "required": ["source", "dest"],
- "title": "Body_download"
+ "version": "1.0.3",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "Body_download_images_from_list": {
+ "AnimaLoRACollectionLoader": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Applies a collection of LoRAs to an Anima transformer.",
+ "node_pack": "invokeai",
"properties": {
- "image_names": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "loras": {
"anyOf": [
+ {
+ "$ref": "#/components/schemas/LoRAField"
+ },
{
"items": {
- "type": "string"
+ "$ref": "#/components/schemas/LoRAField"
},
"type": "array"
},
@@ -13191,295 +12887,72 @@
"type": "null"
}
],
- "title": "Image Names",
- "description": "The list of names of images to download"
+ "default": null,
+ "description": "LoRA models and weights. May be a single LoRA or collection.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "LoRAs",
+ "ui_model_base": ["anima"],
+ "ui_model_type": ["lora"]
},
- "board_id": {
+ "transformer": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
- "title": "Board Id",
- "description": "The board from which image should be downloaded"
- }
- },
- "type": "object",
- "title": "Body_download_images_from_list"
- },
- "Body_enqueue_batch": {
- "properties": {
- "batch": {
- "$ref": "#/components/schemas/Batch",
- "description": "Batch to process"
- },
- "prepend": {
- "type": "boolean",
- "title": "Prepend",
- "description": "Whether or not to prepend this batch in the queue",
- "default": false
- }
- },
- "type": "object",
- "required": ["batch"],
- "title": "Body_enqueue_batch"
- },
- "Body_get_images_by_names": {
- "properties": {
- "image_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Image Names",
- "description": "Object containing list of image names to fetch DTOs for"
- }
- },
- "type": "object",
- "required": ["image_names"],
- "title": "Body_get_images_by_names"
- },
- "Body_get_queue_items_by_item_ids": {
- "properties": {
- "item_ids": {
- "items": {
- "type": "integer"
- },
- "type": "array",
- "title": "Item Ids",
- "description": "Object containing list of queue item ids to fetch queue items for"
- }
- },
- "type": "object",
- "required": ["item_ids"],
- "title": "Body_get_queue_items_by_item_ids"
- },
- "Body_import_style_presets": {
- "properties": {
- "file": {
- "type": "string",
- "format": "binary",
- "title": "File",
- "description": "The file to import"
- }
- },
- "type": "object",
- "required": ["file"],
- "title": "Body_import_style_presets"
- },
- "Body_parse_dynamicprompts": {
- "properties": {
- "prompt": {
- "type": "string",
- "title": "Prompt",
- "description": "The prompt to parse with dynamicprompts"
- },
- "max_prompts": {
- "type": "integer",
- "maximum": 10000.0,
- "minimum": 1.0,
- "title": "Max Prompts",
- "description": "The max number of prompts to generate",
- "default": 1000
- },
- "combinatorial": {
- "type": "boolean",
- "title": "Combinatorial",
- "description": "Whether to use the combinatorial generator",
- "default": true
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Transformer"
},
- "seed": {
+ "qwen3_encoder": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/Qwen3EncoderField"
},
{
"type": "null"
}
],
- "title": "Seed",
- "description": "The seed to use for random generation. Only used if not combinatorial"
- }
- },
- "type": "object",
- "required": ["prompt"],
- "title": "Body_parse_dynamicprompts"
- },
- "Body_remove_image_from_board": {
- "properties": {
- "image_name": {
- "type": "string",
- "title": "Image Name",
- "description": "The name of the image to remove"
- }
- },
- "type": "object",
- "required": ["image_name"],
- "title": "Body_remove_image_from_board"
- },
- "Body_remove_images_from_board": {
- "properties": {
- "image_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Image Names",
- "description": "The names of the images to remove"
- }
- },
- "type": "object",
- "required": ["image_names"],
- "title": "Body_remove_images_from_board"
- },
- "Body_set_workflow_thumbnail": {
- "properties": {
- "image": {
- "type": "string",
- "format": "binary",
- "title": "Image",
- "description": "The image file to upload"
- }
- },
- "type": "object",
- "required": ["image"],
- "title": "Body_set_workflow_thumbnail"
- },
- "Body_star_images_in_list": {
- "properties": {
- "image_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Image Names",
- "description": "The list of names of images to star"
- }
- },
- "type": "object",
- "required": ["image_names"],
- "title": "Body_star_images_in_list"
- },
- "Body_unstar_images_in_list": {
- "properties": {
- "image_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Image Names",
- "description": "The list of names of images to unstar"
- }
- },
- "type": "object",
- "required": ["image_names"],
- "title": "Body_unstar_images_in_list"
- },
- "Body_update_model_image": {
- "properties": {
- "image": {
- "type": "string",
- "format": "binary",
- "title": "Image"
- }
- },
- "type": "object",
- "required": ["image"],
- "title": "Body_update_model_image"
- },
- "Body_update_style_preset": {
- "properties": {
- "image": {
- "anyOf": [
- {
- "type": "string",
- "format": "binary"
- },
- {
- "type": "null"
- }
- ],
- "title": "Image",
- "description": "The image file to upload"
- },
- "data": {
- "type": "string",
- "title": "Data",
- "description": "The data of the style preset to update"
- }
- },
- "type": "object",
- "required": ["data"],
- "title": "Body_update_style_preset"
- },
- "Body_update_workflow": {
- "properties": {
- "workflow": {
- "$ref": "#/components/schemas/Workflow",
- "description": "The updated workflow"
- }
- },
- "type": "object",
- "required": ["workflow"],
- "title": "Body_update_workflow"
- },
- "Body_update_workflow_is_public": {
- "properties": {
- "is_public": {
- "type": "boolean",
- "title": "Is Public",
- "description": "Whether the workflow should be shared publicly"
- }
- },
- "type": "object",
- "required": ["is_public"],
- "title": "Body_update_workflow_is_public"
- },
- "Body_upload_image": {
- "properties": {
- "file": {
- "type": "string",
- "format": "binary",
- "title": "File"
- },
- "resize_to": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Resize To",
- "description": "Dimensions to resize the image to, must be stringified tuple of 2 integers. Max total pixel count: 16777216",
- "examples": ["\"[1024,1024]\""]
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Qwen3 Encoder"
},
- "metadata": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Metadata",
- "description": "The metadata to associate with the image, must be a stringified JSON dict"
+ "type": {
+ "const": "anima_lora_collection_loader",
+ "default": "anima_lora_collection_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["lora", "model", "anima"],
+ "title": "Apply LoRA Collection - Anima",
"type": "object",
- "required": ["file"],
- "title": "Body_upload_image"
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/AnimaLoRALoaderOutput"
+ }
},
- "BooleanCollectionInvocation": {
- "category": "primitives",
+ "AnimaLoRALoaderInvocation": {
+ "category": "model",
"class": "invocation",
- "classification": "stable",
- "description": "A collection of boolean primitive values",
+ "classification": "prototype",
+ "description": "Apply a LoRA model to an Anima transformer and/or Qwen3 text encoder.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -13506,67 +12979,136 @@
"title": "Use Cache",
"type": "boolean"
},
- "collection": {
- "default": [],
- "description": "The collection of boolean values",
+ "lora": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "LoRA model to load",
"field_kind": "input",
"input": "any",
- "items": {
- "type": "boolean"
- },
- "orig_default": [],
+ "orig_required": true,
+ "title": "LoRA",
+ "ui_model_base": ["anima"],
+ "ui_model_type": ["lora"]
+ },
+ "weight": {
+ "default": 0.75,
+ "description": "The weight at which the LoRA is applied to each model",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.75,
"orig_required": false,
- "title": "Collection",
- "type": "array"
+ "title": "Weight",
+ "type": "number"
+ },
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Anima Transformer"
+ },
+ "qwen3_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Qwen3EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Qwen3 Encoder"
},
"type": {
- "const": "boolean_collection",
- "default": "boolean_collection",
+ "const": "anima_lora_loader",
+ "default": "anima_lora_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["primitives", "boolean", "collection"],
- "title": "Boolean Collection Primitive",
+ "tags": ["lora", "model", "anima"],
+ "title": "Apply LoRA - Anima",
"type": "object",
- "version": "1.0.2",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/BooleanCollectionOutput"
+ "$ref": "#/components/schemas/AnimaLoRALoaderOutput"
}
},
- "BooleanCollectionOutput": {
+ "AnimaLoRALoaderOutput": {
"class": "output",
- "description": "Base class for nodes that output a collection of booleans",
+ "description": "Anima LoRA Loader Output",
"properties": {
- "collection": {
- "description": "The output boolean collection",
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Transformer",
"field_kind": "output",
- "items": {
- "type": "boolean"
- },
- "title": "Collection",
- "type": "array",
+ "title": "Anima Transformer",
+ "ui_hidden": false
+ },
+ "qwen3_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Qwen3EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
+ "field_kind": "output",
+ "title": "Qwen3 Encoder",
"ui_hidden": false
},
"type": {
- "const": "boolean_collection_output",
- "default": "boolean_collection_output",
+ "const": "anima_lora_loader_output",
+ "default": "anima_lora_loader_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "collection", "type", "type"],
- "title": "BooleanCollectionOutput",
+ "required": ["output_meta", "transformer", "qwen3_encoder", "type", "type"],
+ "title": "AnimaLoRALoaderOutput",
"type": "object"
},
- "BooleanInvocation": {
- "category": "primitives",
+ "AnimaModelLoaderInvocation": {
+ "category": "model",
"class": "invocation",
- "classification": "stable",
- "description": "A boolean primitive value",
+ "classification": "prototype",
+ "description": "Loads an Anima model, outputting its submodels.\n\nAnima uses:\n- Transformer: Cosmos Predict2 DiT + LLM Adapter (from single-file checkpoint)\n- Qwen3 Encoder: Qwen3 0.6B (standalone single-file)\n- VAE: AutoencoderKLQwenImage / Wan 2.1 VAE (standalone single-file or FLUX VAE)\n\nThe T5-XXL tokenizer needed for LLM Adapter token IDs is bundled in the package,\nso no T5-XXL encoder model needs to be installed.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -13593,130 +13135,93 @@
"title": "Use Cache",
"type": "boolean"
},
- "value": {
- "default": false,
- "description": "The boolean value",
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Anima main model (transformer + LLM adapter).",
"field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Value",
- "type": "boolean"
+ "input": "direct",
+ "orig_required": true,
+ "title": "Transformer",
+ "ui_model_base": ["anima"],
+ "ui_model_type": ["main"]
+ },
+ "vae_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Standalone VAE model. Anima uses a Wan 2.1 / QwenImage VAE (16-channel). A FLUX VAE can also be used as a compatible fallback.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_required": true,
+ "title": "VAE",
+ "ui_model_type": ["vae"]
+ },
+ "qwen3_encoder_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Standalone Qwen3 0.6B Encoder model.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Qwen3 Encoder",
+ "ui_model_type": ["qwen3_encoder"]
},
"type": {
- "const": "boolean",
- "default": "boolean",
+ "const": "anima_model_loader",
+ "default": "anima_model_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["primitives", "boolean"],
- "title": "Boolean Primitive",
+ "required": ["model", "vae_model", "qwen3_encoder_model", "type", "id"],
+ "tags": ["model", "anima"],
+ "title": "Main Model - Anima",
"type": "object",
- "version": "1.0.1",
+ "version": "1.4.0",
"output": {
- "$ref": "#/components/schemas/BooleanOutput"
+ "$ref": "#/components/schemas/AnimaModelLoaderOutput"
}
},
- "BooleanOutput": {
+ "AnimaModelLoaderOutput": {
"class": "output",
- "description": "Base class for nodes that output a single boolean",
+ "description": "Anima model loader output.",
"properties": {
- "value": {
- "description": "The output boolean",
+ "transformer": {
+ "$ref": "#/components/schemas/TransformerField",
+ "description": "Transformer",
"field_kind": "output",
- "title": "Value",
- "type": "boolean",
+ "title": "Transformer",
"ui_hidden": false
},
- "type": {
- "const": "boolean_output",
- "default": "boolean_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "value", "type", "type"],
- "title": "BooleanOutput",
- "type": "object"
- },
- "BoundingBoxCollectionOutput": {
- "class": "output",
- "description": "Base class for nodes that output a collection of bounding boxes",
- "properties": {
- "collection": {
- "description": "The output bounding boxes.",
+ "qwen3_encoder": {
+ "$ref": "#/components/schemas/Qwen3EncoderField",
+ "description": "Qwen3 tokenizer and text encoder",
"field_kind": "output",
- "items": {
- "$ref": "#/components/schemas/BoundingBoxField"
- },
- "title": "Bounding Boxes",
- "type": "array",
+ "title": "Qwen3 Encoder",
+ "ui_hidden": false
+ },
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
"ui_hidden": false
},
"type": {
- "const": "bounding_box_collection_output",
- "default": "bounding_box_collection_output",
+ "const": "anima_model_loader_output",
+ "default": "anima_model_loader_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "collection", "type", "type"],
- "title": "BoundingBoxCollectionOutput",
- "type": "object"
- },
- "BoundingBoxField": {
- "description": "A bounding box primitive value.",
- "properties": {
- "x_min": {
- "description": "The minimum x-coordinate of the bounding box (inclusive).",
- "title": "X Min",
- "type": "integer"
- },
- "x_max": {
- "description": "The maximum x-coordinate of the bounding box (exclusive).",
- "title": "X Max",
- "type": "integer"
- },
- "y_min": {
- "description": "The minimum y-coordinate of the bounding box (inclusive).",
- "title": "Y Min",
- "type": "integer"
- },
- "y_max": {
- "description": "The maximum y-coordinate of the bounding box (exclusive).",
- "title": "Y Max",
- "type": "integer"
- },
- "score": {
- "anyOf": [
- {
- "maximum": 1.0,
- "minimum": 0.0,
- "type": "number"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The score associated with the bounding box. In the range [0, 1]. This value is typically set when the bounding box was produced by a detector and has an associated confidence score.",
- "title": "Score"
- }
- },
- "required": ["x_min", "x_max", "y_min", "y_max"],
- "title": "BoundingBoxField",
+ "required": ["output_meta", "transformer", "qwen3_encoder", "vae", "type", "type"],
+ "title": "AnimaModelLoaderOutput",
"type": "object"
},
- "BoundingBoxInvocation": {
- "category": "primitives",
+ "AnimaTextEncoderInvocation": {
+ "category": "conditioning",
"class": "invocation",
- "classification": "stable",
- "description": "Create a bounding box manually by supplying box coordinates",
+ "classification": "prototype",
+ "description": "Encodes and preps a prompt for an Anima image.\n\nUses Qwen3 0.6B for hidden state extraction and a bundled T5-XXL tokenizer for\ntoken IDs (no T5 model weights needed). Both are combined by the\nLLM Adapter inside the Anima transformer during denoising.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -13743,878 +13248,391 @@
"title": "Use Cache",
"type": "boolean"
},
- "x_min": {
- "default": 0,
- "description": "x-coordinate of the bounding box's top left vertex",
+ "prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Text prompt to encode.",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "X Min",
- "type": "integer"
+ "orig_required": true,
+ "title": "Prompt",
+ "ui_component": "textarea"
},
- "y_min": {
- "default": 0,
- "description": "y-coordinate of the bounding box's top left vertex",
+ "qwen3_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Qwen3EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
"field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Y Min",
- "type": "integer"
- },
- "x_max": {
- "default": 0,
- "description": "x-coordinate of the bounding box's bottom right vertex",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "X Max",
- "type": "integer"
+ "input": "connection",
+ "orig_required": true,
+ "title": "Qwen3 Encoder"
},
- "y_max": {
- "default": 0,
- "description": "y-coordinate of the bounding box's bottom right vertex",
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TensorField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "A mask defining the region that this conditioning prompt applies to.",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Y Max",
- "type": "integer"
+ "orig_default": null,
+ "orig_required": false
},
"type": {
- "const": "bounding_box",
- "default": "bounding_box",
+ "const": "anima_text_encoder",
+ "default": "anima_text_encoder",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["primitives", "segmentation", "collection", "bounding box"],
- "title": "Bounding Box",
+ "tags": ["prompt", "conditioning", "anima"],
+ "title": "Prompt - Anima",
"type": "object",
- "version": "1.0.0",
+ "version": "1.4.0",
"output": {
- "$ref": "#/components/schemas/BoundingBoxOutput"
+ "$ref": "#/components/schemas/AnimaConditioningOutput"
}
},
- "BoundingBoxOutput": {
- "class": "output",
- "description": "Base class for nodes that output a single bounding box",
- "properties": {
- "bounding_box": {
- "$ref": "#/components/schemas/BoundingBoxField",
- "description": "The output bounding box.",
- "field_kind": "output",
- "ui_hidden": false
+ "AnyModelConfig": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
},
- "type": {
- "const": "bounding_box_output",
- "default": "bounding_box_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "bounding_box", "type", "type"],
- "title": "BoundingBoxOutput",
- "type": "object"
- },
- "BulkDeleteModelsRequest": {
- "properties": {
- "keys": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Keys",
- "description": "List of model keys to delete"
- }
- },
- "type": "object",
- "required": ["keys"],
- "title": "BulkDeleteModelsRequest",
- "description": "Request body for bulk model deletion."
- },
- "BulkDeleteModelsResponse": {
- "properties": {
- "deleted": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Deleted",
- "description": "List of successfully deleted model keys"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
},
- "failed": {
- "items": {
- "additionalProperties": true,
- "type": "object"
- },
- "type": "array",
- "title": "Failed",
- "description": "List of failed deletions with error messages"
- }
- },
- "type": "object",
- "required": ["deleted", "failed"],
- "title": "BulkDeleteModelsResponse",
- "description": "Response body for bulk model deletion."
- },
- "BulkDownloadCompleteEvent": {
- "description": "Event model for bulk_download_complete",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
},
- "bulk_download_id": {
- "description": "The ID of the bulk image download",
- "title": "Bulk Download Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
},
- "bulk_download_item_id": {
- "description": "The ID of the bulk image download item",
- "title": "Bulk Download Item Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
},
- "bulk_download_item_name": {
- "description": "The name of the bulk image download item",
- "title": "Bulk Download Item Name",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
},
- "user_id": {
- "default": "system",
- "description": "The ID of the user who initiated the download",
- "title": "User Id",
- "type": "string"
- }
- },
- "required": ["timestamp", "bulk_download_id", "bulk_download_item_id", "bulk_download_item_name", "user_id"],
- "title": "BulkDownloadCompleteEvent",
- "type": "object"
- },
- "BulkDownloadErrorEvent": {
- "description": "Event model for bulk_download_error",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
},
- "bulk_download_id": {
- "description": "The ID of the bulk image download",
- "title": "Bulk Download Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
},
- "bulk_download_item_id": {
- "description": "The ID of the bulk image download item",
- "title": "Bulk Download Item Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
- "bulk_download_item_name": {
- "description": "The name of the bulk image download item",
- "title": "Bulk Download Item Name",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
},
- "user_id": {
- "default": "system",
- "description": "The ID of the user who initiated the download",
- "title": "User Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
- "error": {
- "description": "The error message",
- "title": "Error",
- "type": "string"
- }
- },
- "required": [
- "timestamp",
- "bulk_download_id",
- "bulk_download_item_id",
- "bulk_download_item_name",
- "user_id",
- "error"
- ],
- "title": "BulkDownloadErrorEvent",
- "type": "object"
- },
- "BulkDownloadStartedEvent": {
- "description": "Event model for bulk_download_started",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
},
- "bulk_download_id": {
- "description": "The ID of the bulk image download",
- "title": "Bulk Download Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
},
- "bulk_download_item_id": {
- "description": "The ID of the bulk image download item",
- "title": "Bulk Download Item Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
},
- "bulk_download_item_name": {
- "description": "The name of the bulk image download item",
- "title": "Bulk Download Item Name",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
},
- "user_id": {
- "default": "system",
- "description": "The ID of the user who initiated the download",
- "title": "User Id",
- "type": "string"
- }
- },
- "required": ["timestamp", "bulk_download_id", "bulk_download_item_id", "bulk_download_item_name", "user_id"],
- "title": "BulkDownloadStartedEvent",
- "type": "object"
- },
- "BulkReidentifyModelsRequest": {
- "properties": {
- "keys": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Keys",
- "description": "List of model keys to reidentify"
- }
- },
- "type": "object",
- "required": ["keys"],
- "title": "BulkReidentifyModelsRequest",
- "description": "Request body for bulk model reidentification."
- },
- "BulkReidentifyModelsResponse": {
- "properties": {
- "succeeded": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Succeeded",
- "description": "List of successfully reidentified model keys"
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
},
- "failed": {
- "items": {
- "additionalProperties": true,
- "type": "object"
- },
- "type": "array",
- "title": "Failed",
- "description": "List of failed reidentifications with error messages"
- }
- },
- "type": "object",
- "required": ["succeeded", "failed"],
- "title": "BulkReidentifyModelsResponse",
- "description": "Response body for bulk model reidentification."
- },
- "CLIPEmbed_Diffusers_G_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ {
+ "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ {
+ "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ {
+ "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
- "source_api_response": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ {
+ "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
},
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
},
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
},
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
},
- "type": {
- "type": "string",
- "const": "clip_embed",
- "title": "Type",
- "default": "clip_embed"
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
- "cpu_only": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
},
- "variant": {
- "type": "string",
- "const": "gigantic",
- "title": "Variant",
- "default": "gigantic"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "base",
- "type",
- "cpu_only",
- "variant"
- ],
- "title": "CLIPEmbed_Diffusers_G_Config"
- },
- "CLIPEmbed_Diffusers_L_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
},
- "source_api_response": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
},
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
},
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
},
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
},
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
},
- "type": {
- "type": "string",
- "const": "clip_embed",
- "title": "Type",
- "default": "clip_embed"
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
},
- "cpu_only": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
},
- "variant": {
- "type": "string",
- "const": "large",
- "title": "Variant",
- "default": "large"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "base",
- "type",
- "cpu_only",
- "variant"
- ],
- "title": "CLIPEmbed_Diffusers_L_Config"
- },
- "CLIPField": {
- "properties": {
- "tokenizer": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load tokenizer submodel"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
},
- "text_encoder": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load text_encoder submodel"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
},
- "skipped_layers": {
- "description": "Number of skipped layers in text_encoder",
- "title": "Skipped Layers",
- "type": "integer"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
},
- "loras": {
- "description": "LoRAs to apply on model loading",
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "title": "Loras",
- "type": "array"
- }
- },
- "required": ["tokenizer", "text_encoder", "skipped_layers", "loras"],
- "title": "CLIPField",
- "type": "object"
- },
- "CLIPOutput": {
- "class": "output",
- "description": "Base class for invocations that output a CLIP field",
- "properties": {
- "clip": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP",
- "ui_hidden": false
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
},
- "type": {
- "const": "clip_output",
- "default": "clip_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "clip", "type", "type"],
- "title": "CLIPOutput",
- "type": "object"
- },
- "CLIPSkipInvocation": {
- "category": "prompt",
- "class": "invocation",
- "classification": "stable",
- "description": "Skip layers in clip text_encoder model.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
- "clip": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/CLIPField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "CLIP"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
},
- "skipped_layers": {
- "default": 0,
- "description": "Number of layers to skip in text encoder",
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "Skipped Layers",
- "type": "integer"
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
- "type": {
- "const": "clip_skip",
- "default": "clip_skip",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["clipskip", "clip", "skip"],
- "title": "Apply CLIP Skip - SD1.5, SDXL",
- "type": "object",
- "version": "1.1.1",
- "output": {
- "$ref": "#/components/schemas/CLIPSkipInvocationOutput"
- }
- },
- "CLIPSkipInvocationOutput": {
- "class": "output",
- "description": "CLIP skip node output",
- "properties": {
- "clip": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/CLIPField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP",
- "ui_hidden": false
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
},
- "type": {
- "const": "clip_skip_output",
- "default": "clip_skip_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "clip", "type", "type"],
- "title": "CLIPSkipInvocationOutput",
- "type": "object"
- },
- "CLIPVision_Diffusers_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ {
+ "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
},
- "source_api_response": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ {
+ "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ {
+ "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
},
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
},
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
},
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
},
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
},
- "type": {
- "type": "string",
- "const": "clip_vision",
- "title": "Type",
- "default": "clip_vision"
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
- "cpu_only": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalApiModelConfig"
+ },
+ {
+ "$ref": "#/components/schemas/Unknown_Config"
+ }
+ ]
+ },
+ "AppVersion": {
+ "properties": {
+ "version": {
+ "type": "string",
+ "title": "Version",
+ "description": "App version"
}
},
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "base",
- "type",
- "cpu_only"
- ],
- "title": "CLIPVision_Diffusers_Config",
- "description": "Model config for CLIPVision."
+ "required": ["version"],
+ "title": "AppVersion",
+ "description": "App Version Response"
},
- "CV2InfillInvocation": {
- "category": "inpaint",
+ "ApplyMaskTensorToImageInvocation": {
+ "category": "mask",
"class": "invocation",
"classification": "stable",
- "description": "Infills transparent areas of an image using OpenCV Inpainting",
+ "description": "Applies a tensor mask to an image.\n\nThe image is converted to RGBA and the mask is applied to the alpha channel.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -14673,532 +13691,68 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/TensorField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to process",
+ "description": "The mask tensor to apply.",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "type": {
- "const": "infill_cv2",
- "default": "infill_cv2",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "inpaint"],
- "title": "CV2 Infill",
- "type": "object",
- "version": "1.2.2",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "CacheStats": {
- "properties": {
- "hits": {
- "type": "integer",
- "title": "Hits",
- "default": 0
- },
- "misses": {
- "type": "integer",
- "title": "Misses",
- "default": 0
- },
- "high_watermark": {
- "type": "integer",
- "title": "High Watermark",
- "default": 0
- },
- "in_cache": {
- "type": "integer",
- "title": "In Cache",
- "default": 0
- },
- "cleared": {
- "type": "integer",
- "title": "Cleared",
- "default": 0
- },
- "cache_size": {
- "type": "integer",
- "title": "Cache Size",
- "default": 0
- },
- "loaded_model_sizes": {
- "additionalProperties": {
- "type": "integer"
- },
- "type": "object",
- "title": "Loaded Model Sizes"
- }
- },
- "type": "object",
- "title": "CacheStats",
- "description": "Collect statistics on cache performance."
- },
- "CalculateImageTilesEvenSplitInvocation": {
- "category": "tiles",
- "class": "invocation",
- "classification": "stable",
- "description": "Calculate the coordinates and overlaps of tiles that cover a target image shape.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "image_width": {
- "default": 1024,
- "description": "The image width, in pixels, to calculate tiles for.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Image Width",
- "type": "integer"
- },
- "image_height": {
- "default": 1024,
- "description": "The image height, in pixels, to calculate tiles for.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Image Height",
- "type": "integer"
- },
- "num_tiles_x": {
- "default": 2,
- "description": "Number of tiles to divide image into on the x axis",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 2,
- "orig_required": false,
- "title": "Num Tiles X",
- "type": "integer"
- },
- "num_tiles_y": {
- "default": 2,
- "description": "Number of tiles to divide image into on the y axis",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 2,
- "orig_required": false,
- "title": "Num Tiles Y",
- "type": "integer"
- },
- "overlap": {
- "default": 128,
- "description": "The overlap, in pixels, between adjacent tiles.",
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "multipleOf": 8,
- "orig_default": 128,
- "orig_required": false,
- "title": "Overlap",
- "type": "integer"
- },
- "type": {
- "const": "calculate_image_tiles_even_split",
- "default": "calculate_image_tiles_even_split",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["tiles"],
- "title": "Calculate Image Tiles Even Split",
- "type": "object",
- "version": "1.1.1",
- "output": {
- "$ref": "#/components/schemas/CalculateImageTilesOutput"
- }
- },
- "CalculateImageTilesInvocation": {
- "category": "tiles",
- "class": "invocation",
- "classification": "stable",
- "description": "Calculate the coordinates and overlaps of tiles that cover a target image shape.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "image_width": {
- "default": 1024,
- "description": "The image width, in pixels, to calculate tiles for.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Image Width",
- "type": "integer"
- },
- "image_height": {
- "default": 1024,
- "description": "The image height, in pixels, to calculate tiles for.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Image Height",
- "type": "integer"
- },
- "tile_width": {
- "default": 576,
- "description": "The tile width, in pixels.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 576,
- "orig_required": false,
- "title": "Tile Width",
- "type": "integer"
- },
- "tile_height": {
- "default": 576,
- "description": "The tile height, in pixels.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 576,
- "orig_required": false,
- "title": "Tile Height",
- "type": "integer"
- },
- "overlap": {
- "default": 128,
- "description": "The target overlap, in pixels, between adjacent tiles. Adjacent tiles will overlap by at least this amount",
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to apply the mask to.",
"field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 128,
- "orig_required": false,
- "title": "Overlap",
- "type": "integer"
- },
- "type": {
- "const": "calculate_image_tiles",
- "default": "calculate_image_tiles",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["tiles"],
- "title": "Calculate Image Tiles",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/CalculateImageTilesOutput"
- }
- },
- "CalculateImageTilesMinimumOverlapInvocation": {
- "category": "tiles",
- "class": "invocation",
- "classification": "stable",
- "description": "Calculate the coordinates and overlaps of tiles that cover a target image shape.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "orig_required": true
},
- "is_intermediate": {
+ "invert": {
"default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "image_width": {
- "default": 1024,
- "description": "The image width, in pixels, to calculate tiles for.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Image Width",
- "type": "integer"
- },
- "image_height": {
- "default": 1024,
- "description": "The image height, in pixels, to calculate tiles for.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Image Height",
- "type": "integer"
- },
- "tile_width": {
- "default": 576,
- "description": "The tile width, in pixels.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 576,
- "orig_required": false,
- "title": "Tile Width",
- "type": "integer"
- },
- "tile_height": {
- "default": 576,
- "description": "The tile height, in pixels.",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 576,
- "orig_required": false,
- "title": "Tile Height",
- "type": "integer"
- },
- "min_overlap": {
- "default": 128,
- "description": "Minimum overlap between adjacent tiles, in pixels.",
+ "description": "Whether to invert the mask.",
"field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 128,
+ "orig_default": false,
"orig_required": false,
- "title": "Min Overlap",
- "type": "integer"
- },
- "type": {
- "const": "calculate_image_tiles_min_overlap",
- "default": "calculate_image_tiles_min_overlap",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["tiles"],
- "title": "Calculate Image Tiles Minimum Overlap",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/CalculateImageTilesOutput"
- }
- },
- "CalculateImageTilesOutput": {
- "class": "output",
- "properties": {
- "tiles": {
- "description": "The tiles coordinates that cover a particular image shape.",
- "field_kind": "output",
- "items": {
- "$ref": "#/components/schemas/Tile"
- },
- "title": "Tiles",
- "type": "array",
- "ui_hidden": false
- },
- "type": {
- "const": "calculate_image_tiles_output",
- "default": "calculate_image_tiles_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "tiles", "type", "type"],
- "title": "CalculateImageTilesOutput",
- "type": "object"
- },
- "CallSavedWorkflowInvocation": {
- "category": "workflow",
- "class": "invocation",
- "classification": "beta",
- "description": "Displays and later executes against a selected saved workflow.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": false,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
+ "title": "Invert",
"type": "boolean"
},
- "workflow_id": {
- "default": "",
- "description": "The selected saved workflow ID, managed by the workflow editor UI.",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Workflow Id",
- "type": "string",
- "ui_type": "SavedWorkflowField"
- },
- "workflow_inputs": {
- "additionalProperties": true,
- "default": {},
- "description": "Literal values for the selected workflow's exposed inputs, managed by the workflow editor UI.",
- "field_kind": "input",
- "input": "any",
- "orig_default": {},
- "orig_required": false,
- "title": "Workflow Inputs",
- "type": "object",
- "ui_hidden": true
- },
"type": {
- "const": "call_saved_workflow",
- "default": "call_saved_workflow",
+ "const": "apply_tensor_mask_to_image",
+ "default": "apply_tensor_mask_to_image",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["workflow", "saved", "library"],
- "title": "Call Saved Workflow",
+ "tags": ["mask"],
+ "title": "Apply Tensor Mask to Image",
"type": "object",
"version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/WorkflowReturnOutput"
+ "$ref": "#/components/schemas/ImageOutput"
}
},
- "CancelAllExceptCurrentResult": {
- "properties": {
- "canceled": {
- "type": "integer",
- "title": "Canceled",
- "description": "Number of queue items canceled"
- }
- },
- "type": "object",
- "required": ["canceled"],
- "title": "CancelAllExceptCurrentResult",
- "description": "Result of canceling all except current"
- },
- "CancelByBatchIDsResult": {
- "properties": {
- "canceled": {
- "type": "integer",
- "title": "Canceled",
- "description": "Number of queue items canceled"
- }
- },
- "type": "object",
- "required": ["canceled"],
- "title": "CancelByBatchIDsResult",
- "description": "Result of canceling by list of batch ids"
- },
- "CancelByDestinationResult": {
- "properties": {
- "canceled": {
- "type": "integer",
- "title": "Canceled",
- "description": "Number of queue items canceled"
- }
- },
- "type": "object",
- "required": ["canceled"],
- "title": "CancelByDestinationResult",
- "description": "Result of canceling by a destination"
- },
- "CannyEdgeDetectionInvocation": {
- "category": "controlnet_preprocessors",
+ "ApplyMaskToImageInvocation": {
+ "category": "mask",
"class": "invocation",
"classification": "stable",
- "description": "Geneartes an edge map using a cv2's Canny algorithm.",
+ "description": "Extracts a region from a generated image using a mask and blends it seamlessly onto a source image.\nThe mask uses black to indicate areas to keep from the generated image and white for areas to discard.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -15267,256 +13821,383 @@
}
],
"default": null,
- "description": "The image to process",
+ "description": "The image from which to extract the masked region",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "low_threshold": {
- "default": 100,
- "description": "The low threshold of the Canny pixel gradient (0-255)",
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The mask defining the region (black=keep, white=discard)",
"field_kind": "input",
"input": "any",
- "maximum": 255,
- "minimum": 0,
- "orig_default": 100,
- "orig_required": false,
- "title": "Low Threshold",
- "type": "integer"
+ "orig_required": true
},
- "high_threshold": {
- "default": 200,
- "description": "The high threshold of the Canny pixel gradient (0-255)",
+ "invert_mask": {
+ "default": false,
+ "description": "Whether to invert the mask before applying it",
"field_kind": "input",
"input": "any",
- "maximum": 255,
- "minimum": 0,
- "orig_default": 200,
+ "orig_default": false,
"orig_required": false,
- "title": "High Threshold",
- "type": "integer"
+ "title": "Invert Mask",
+ "type": "boolean"
},
"type": {
- "const": "canny_edge_detection",
- "default": "canny_edge_detection",
+ "const": "apply_mask_to_image",
+ "default": "apply_mask_to_image",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["controlnet", "canny"],
- "title": "Canny Edge Detection",
+ "tags": ["image", "mask", "blend"],
+ "title": "Apply Mask to Image",
"type": "object",
"version": "1.0.0",
"output": {
"$ref": "#/components/schemas/ImageOutput"
}
},
- "CanvasOutputInvocation": {
- "category": "canvas",
- "class": "invocation",
- "classification": "stable",
- "description": "Outputs an image to the canvas staging area.\n\nUse this node in workflows intended for canvas workflow integration.\nConnect the final image of your workflow to this node to send it\nto the canvas staging area when run via 'Run Workflow on Canvas'.",
- "node_pack": "invokeai",
+ "BaseMetadata": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "model's name"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "type": {
+ "type": "string",
+ "const": "basemetadata",
+ "title": "Type",
+ "default": "basemetadata"
+ }
+ },
+ "type": "object",
+ "required": ["name"],
+ "title": "BaseMetadata",
+ "description": "Adds typing data for discriminated union."
+ },
+ "BaseModelType": {
+ "type": "string",
+ "enum": [
+ "any",
+ "sd-1",
+ "sd-2",
+ "sd-3",
+ "sdxl",
+ "sdxl-refiner",
+ "flux",
+ "flux2",
+ "cogview4",
+ "z-image",
+ "external",
+ "qwen-image",
+ "anima",
+ "wan",
+ "unknown"
+ ],
+ "title": "BaseModelType",
+ "description": "An enumeration of base model architectures. For example, Stable Diffusion 1.x, Stable Diffusion 2.x, FLUX, etc.\n\nEvery model config must have a base architecture type.\n\nNot all models are associated with a base architecture. For example, CLIP models are their own thing, not related\nto any particular model architecture. To simplify internal APIs and make it easier to work with models, we use a\nfallback/null value `BaseModelType.Any` for these models, instead of making the model base optional."
+ },
+ "Batch": {
+ "properties": {
+ "batch_id": {
+ "type": "string",
+ "title": "Batch Id",
+ "description": "The ID of the batch"
},
- "use_cache": {
- "default": false,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "origin": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Origin",
+ "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results."
},
- "image": {
+ "destination": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to process",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Destination",
+ "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results"
},
- "type": {
- "const": "canvas_output",
- "default": "canvas_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["canvas", "output", "image"],
- "title": "Canvas Output",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "CanvasPasteBackInvocation": {
- "category": "canvas",
- "class": "invocation",
- "classification": "stable",
- "description": "Combines two images by using the mask provided. Intended for use on the Unified Canvas.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "data": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "items": {
+ "items": {
+ "$ref": "#/components/schemas/BatchDatum"
+ },
+ "type": "array"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Data",
+ "description": "The batch data collection."
},
- "metadata": {
+ "graph": {
+ "$ref": "#/components/schemas/Graph",
+ "description": "The graph to initialize the session with"
+ },
+ "workflow": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "$ref": "#/components/schemas/WorkflowWithoutID"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "description": "The workflow to initialize the session with"
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
+ "runs": {
+ "type": "integer",
+ "minimum": 1.0,
+ "title": "Runs",
+ "description": "Int stating how many times to iterate through all possible batch indices",
+ "default": 1
+ }
+ },
+ "type": "object",
+ "required": ["graph", "runs"],
+ "title": "Batch"
+ },
+ "BatchDatum": {
+ "properties": {
+ "node_path": {
+ "type": "string",
+ "title": "Node Path",
+ "description": "The node into which this batch data collection will be substituted."
+ },
+ "field_name": {
+ "type": "string",
+ "title": "Field Name",
+ "description": "The field into which this batch data collection will be substituted."
+ },
+ "items": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "number"
+ },
+ {
+ "type": "integer"
+ },
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "$ref": "#/components/schemas/VideoField"
+ },
+ {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "number"
+ },
+ {
+ "type": "integer"
+ },
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "$ref": "#/components/schemas/VideoField"
+ }
+ ]
+ },
+ "type": "array"
+ }
+ ]
+ },
+ "type": "array",
+ "title": "Items",
+ "description": "The list of items to substitute into the node/field."
+ }
+ },
+ "type": "object",
+ "required": ["node_path", "field_name"],
+ "title": "BatchDatum"
+ },
+ "BatchEnqueuedEvent": {
+ "description": "Event model for batch_enqueued",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "queue_id": {
+ "description": "The ID of the queue",
+ "title": "Queue Id",
"type": "string"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "batch_id": {
+ "description": "The ID of the batch",
+ "title": "Batch Id",
+ "type": "string"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "enqueued": {
+ "description": "The number of invocations enqueued",
+ "title": "Enqueued",
+ "type": "integer"
},
- "source_image": {
+ "requested": {
+ "description": "The number of invocations initially requested to be enqueued (may be less than enqueued if queue was full)",
+ "title": "Requested",
+ "type": "integer"
+ },
+ "priority": {
+ "description": "The priority of the batch",
+ "title": "Priority",
+ "type": "integer"
+ },
+ "origin": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "The source image",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "description": "The origin of the batch",
+ "title": "Origin"
},
- "target_image": {
+ "user_id": {
+ "default": "system",
+ "description": "The ID of the user who enqueued the batch",
+ "title": "User Id",
+ "type": "string"
+ }
+ },
+ "required": ["timestamp", "queue_id", "batch_id", "enqueued", "requested", "priority", "origin", "user_id"],
+ "title": "BatchEnqueuedEvent",
+ "type": "object"
+ },
+ "BatchStatus": {
+ "properties": {
+ "queue_id": {
+ "type": "string",
+ "title": "Queue Id",
+ "description": "The ID of the queue"
+ },
+ "batch_id": {
+ "type": "string",
+ "title": "Batch Id",
+ "description": "The ID of the batch"
+ },
+ "origin": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The target image",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Origin",
+ "description": "The origin of the batch"
},
- "mask": {
+ "destination": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The mask to use when pasting",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Destination",
+ "description": "The destination of the batch"
},
- "mask_blur": {
- "default": 0,
- "description": "The amount to blur the mask by",
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "Mask Blur",
- "type": "integer"
+ "pending": {
+ "type": "integer",
+ "title": "Pending",
+ "description": "Number of queue items with status 'pending'"
},
- "type": {
- "const": "canvas_paste_back",
- "default": "canvas_paste_back",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "in_progress": {
+ "type": "integer",
+ "title": "In Progress",
+ "description": "Number of queue items with status 'in_progress'"
+ },
+ "waiting": {
+ "type": "integer",
+ "title": "Waiting",
+ "description": "Number of queue items with status 'waiting'"
+ },
+ "completed": {
+ "type": "integer",
+ "title": "Completed",
+ "description": "Number of queue items with status 'complete'"
+ },
+ "failed": {
+ "type": "integer",
+ "title": "Failed",
+ "description": "Number of queue items with status 'error'"
+ },
+ "canceled": {
+ "type": "integer",
+ "title": "Canceled",
+ "description": "Number of queue items with status 'canceled'"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of queue items"
}
},
- "required": ["type", "id"],
- "tags": ["image", "combine"],
- "title": "Canvas Paste Back",
"type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": [
+ "queue_id",
+ "batch_id",
+ "origin",
+ "destination",
+ "pending",
+ "in_progress",
+ "waiting",
+ "completed",
+ "failed",
+ "canceled",
+ "total"
+ ],
+ "title": "BatchStatus"
},
- "CanvasV2MaskAndCropInvocation": {
- "category": "canvas",
+ "BlankImageInvocation": {
+ "category": "image",
"class": "invocation",
- "classification": "deprecated",
- "description": "Handles Canvas V2 image output masking and cropping",
+ "classification": "stable",
+ "description": "Creates a blank image and forwards it to the pipeline",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -15575,85 +14256,78 @@
"title": "Use Cache",
"type": "boolean"
},
- "source_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The source image onto which the masked generated image is pasted. If omitted, the masked generated image is returned with transparency.",
+ "width": {
+ "default": 512,
+ "description": "The width of the image",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
},
- "generated_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to apply the mask to",
+ "height": {
+ "default": 512,
+ "description": "The height of the image",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
},
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The mask to apply",
+ "mode": {
+ "default": "RGB",
+ "description": "The mode of the image",
+ "enum": ["RGB", "RGBA"],
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": "RGB",
+ "orig_required": false,
+ "title": "Mode",
+ "type": "string"
},
- "mask_blur": {
- "default": 0,
- "description": "The amount to blur the mask by",
- "field_kind": "input",
+ "color": {
+ "$ref": "#/components/schemas/ColorField",
+ "default": {
+ "r": 0,
+ "g": 0,
+ "b": 0,
+ "a": 255
+ },
+ "description": "The color of the image",
+ "field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "Mask Blur",
- "type": "integer"
+ "orig_default": {
+ "a": 255,
+ "b": 0,
+ "g": 0,
+ "r": 0
+ },
+ "orig_required": false
},
"type": {
- "const": "canvas_v2_mask_and_crop",
- "default": "canvas_v2_mask_and_crop",
+ "const": "blank_image",
+ "default": "blank_image",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "mask", "id"],
- "title": "Canvas V2 Mask and Crop",
+ "tags": ["image"],
+ "title": "Blank Image",
"type": "object",
- "version": "1.0.0",
+ "version": "1.2.2",
"output": {
"$ref": "#/components/schemas/ImageOutput"
}
},
- "CenterPadCropInvocation": {
- "category": "image",
+ "BlendLatentsInvocation": {
+ "category": "latents",
"class": "invocation",
"classification": "stable",
- "description": "Pad or crop an image's sides from the center by specified pixels. Positive values are outside of the image.",
+ "description": "Blend two latents using a given alpha. If a mask is provided, the second latents will be masked before blending.\nLatents must have same size. Masking functionality added by @dwringer.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -15680,798 +14354,862 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "latents_a": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to crop",
+ "description": "Latents tensor",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_required": true
},
- "left": {
- "default": 0,
- "description": "Number of pixels to pad/crop from the left (negative values crop inwards, positive values pad outwards)",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Left",
- "type": "integer"
- },
- "right": {
- "default": 0,
- "description": "Number of pixels to pad/crop from the right (negative values crop inwards, positive values pad outwards)",
+ "latents_b": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
"field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Right",
- "type": "integer"
+ "input": "connection",
+ "orig_required": true
},
- "top": {
- "default": 0,
- "description": "Number of pixels to pad/crop from the top (negative values crop inwards, positive values pad outwards)",
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Mask for blending in latents B",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Top",
- "type": "integer"
+ "orig_default": null,
+ "orig_required": false
},
- "bottom": {
- "default": 0,
- "description": "Number of pixels to pad/crop from the bottom (negative values crop inwards, positive values pad outwards)",
+ "alpha": {
+ "default": 0.5,
+ "description": "Blending factor. 0.0 = use input A only, 1.0 = use input B only, 0.5 = 50% mix of input A and input B.",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
+ "minimum": 0,
+ "orig_default": 0.5,
"orig_required": false,
- "title": "Bottom",
- "type": "integer"
+ "title": "Alpha",
+ "type": "number"
},
"type": {
- "const": "img_pad_crop",
- "default": "img_pad_crop",
+ "const": "lblend",
+ "default": "lblend",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "pad", "crop"],
- "title": "Center Pad or Crop Image",
+ "tags": ["latents", "blend", "mask"],
+ "title": "Blend Latents",
"type": "object",
- "version": "1.0.0",
+ "version": "1.1.0",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/LatentsOutput"
}
},
- "Classification": {
- "description": "The classification of an Invocation.\n- `Stable`: The invocation, including its inputs/outputs and internal logic, is stable. You may build workflows with it, having confidence that they will not break because of a change in this invocation.\n- `Beta`: The invocation is not yet stable, but is planned to be stable in the future. Workflows built around this invocation may break, but we are committed to supporting this invocation long-term.\n- `Prototype`: The invocation is not yet stable and may be removed from the application at any time. Workflows built around this invocation may break, and we are *not* committed to supporting this invocation.\n- `Deprecated`: The invocation is deprecated and may be removed in a future version.\n- `Internal`: The invocation is not intended for use by end-users. It may be changed or removed at any time, but is exposed for users to play with.\n- `Special`: The invocation is a special case and does not fit into any of the other classifications.",
- "enum": ["stable", "beta", "prototype", "deprecated", "internal", "special"],
- "title": "Classification",
- "type": "string"
- },
- "ClearResult": {
- "properties": {
- "deleted": {
- "type": "integer",
- "title": "Deleted",
- "description": "Number of queue items deleted"
- }
- },
- "type": "object",
- "required": ["deleted"],
- "title": "ClearResult",
- "description": "Result of clearing the session queue"
- },
- "ClipVariantType": {
- "type": "string",
- "enum": ["large", "gigantic"],
- "title": "ClipVariantType",
- "description": "Variant type."
- },
- "CogView4ConditioningField": {
- "description": "A conditioning tensor primitive value",
- "properties": {
- "conditioning_name": {
- "description": "The name of conditioning tensor",
- "title": "Conditioning Name",
- "type": "string"
- }
- },
- "required": ["conditioning_name"],
- "title": "CogView4ConditioningField",
- "type": "object"
- },
- "CogView4ConditioningOutput": {
- "class": "output",
- "description": "Base class for nodes that output a CogView text conditioning tensor.",
- "properties": {
- "conditioning": {
- "$ref": "#/components/schemas/CogView4ConditioningField",
- "description": "Conditioning tensor",
- "field_kind": "output",
- "ui_hidden": false
- },
- "type": {
- "const": "cogview4_conditioning_output",
- "default": "cogview4_conditioning_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "conditioning", "type", "type"],
- "title": "CogView4ConditioningOutput",
- "type": "object"
- },
- "CogView4DenoiseInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "prototype",
- "description": "Run the denoising process with a CogView4 model.",
- "node_pack": "invokeai",
+ "BoardChanges": {
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
+ "board_name": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string",
+ "maxLength": 300
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Board Name",
+ "description": "The board's new name."
},
- "latents": {
+ "cover_image_name": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Cover Image Name",
+ "description": "The name of the board's new cover image."
},
- "noise": {
+ "archived": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Noise tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Archived",
+ "description": "Whether or not the board is archived"
},
- "denoise_mask": {
+ "board_visibility": {
"anyOf": [
{
- "$ref": "#/components/schemas/DenoiseMaskField"
+ "$ref": "#/components/schemas/BoardVisibility"
},
{
"type": "null"
}
],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "description": "The visibility of the board."
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "title": "BoardChanges"
+ },
+ "BoardDTO": {
+ "properties": {
+ "board_id": {
+ "type": "string",
+ "title": "Board Id",
+ "description": "The unique ID of the board."
},
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
+ "board_name": {
+ "type": "string",
+ "title": "Board Name",
+ "description": "The name of the board."
},
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
+ "user_id": {
+ "type": "string",
+ "title": "User Id",
+ "description": "The user ID of the board owner."
},
- "transformer": {
+ "created_at": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "type": "string",
+ "format": "date-time"
},
{
- "type": "null"
+ "type": "string"
}
],
- "default": null,
- "description": "CogView4 model (Transformer) to load",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Transformer"
+ "title": "Created At",
+ "description": "The created timestamp of the board."
},
- "positive_conditioning": {
+ "updated_at": {
"anyOf": [
{
- "$ref": "#/components/schemas/CogView4ConditioningField"
+ "type": "string",
+ "format": "date-time"
},
{
- "type": "null"
+ "type": "string"
}
],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Updated At",
+ "description": "The updated timestamp of the board."
},
- "negative_conditioning": {
+ "deleted_at": {
"anyOf": [
{
- "$ref": "#/components/schemas/CogView4ConditioningField"
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Negative conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Deleted At",
+ "description": "The deleted timestamp of the board."
},
- "cfg_scale": {
+ "cover_image_name": {
"anyOf": [
{
- "type": "number"
+ "type": "string"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
],
- "default": 3.5,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 3.5,
- "orig_required": false,
- "title": "CFG Scale"
- },
- "width": {
- "default": 1024,
- "description": "Width of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 32,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 32,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
+ "title": "Cover Image Name",
+ "description": "The name of the board's cover image."
},
- "steps": {
- "default": 25,
- "description": "Number of steps to run",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 25,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
+ "archived": {
+ "type": "boolean",
+ "title": "Archived",
+ "description": "Whether or not the board is archived."
},
- "seed": {
- "default": 0,
- "description": "Randomness seed for reproducibility.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "board_visibility": {
+ "$ref": "#/components/schemas/BoardVisibility",
+ "description": "The visibility of the board.",
+ "default": "private"
},
- "type": {
- "const": "cogview4_denoise",
- "default": "cogview4_denoise",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "cogview4"],
- "title": "Denoise - CogView4",
- "type": "object",
- "version": "1.1.0",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
- },
- "CogView4ImageToLatentsInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "prototype",
- "description": "Generates latents from an image.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "cover_video_name": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Cover Video Name",
+ "description": "The name of the board's cover video, when the most recent item is a video."
},
- "metadata": {
+ "image_count": {
+ "type": "integer",
+ "title": "Image Count",
+ "description": "The number of images in the board."
+ },
+ "video_count": {
+ "type": "integer",
+ "title": "Video Count",
+ "description": "The number of videos in the board.",
+ "default": 0
+ },
+ "asset_count": {
+ "type": "integer",
+ "title": "Asset Count",
+ "description": "The number of assets in the board."
+ },
+ "owner_username": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to encode.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Owner Username",
+ "description": "The username of the board owner (for admin view)."
+ }
+ },
+ "type": "object",
+ "required": [
+ "board_id",
+ "board_name",
+ "user_id",
+ "created_at",
+ "updated_at",
+ "cover_image_name",
+ "archived",
+ "image_count",
+ "asset_count"
+ ],
+ "title": "BoardDTO",
+ "description": "Deserialized board record with cover image URL and image count."
+ },
+ "BoardField": {
+ "description": "A board primitive field",
+ "properties": {
+ "board_id": {
+ "description": "The id of the board",
+ "title": "Board Id",
+ "type": "string"
+ }
+ },
+ "required": ["board_id"],
+ "title": "BoardField",
+ "type": "object"
+ },
+ "BoardRecordOrderBy": {
+ "type": "string",
+ "enum": ["created_at", "board_name"],
+ "title": "BoardRecordOrderBy",
+ "description": "The order by options for board records"
+ },
+ "BoardVisibility": {
+ "type": "string",
+ "enum": ["private", "shared", "public"],
+ "title": "BoardVisibility",
+ "description": "The visibility options for a board."
+ },
+ "Body_add_image_to_board": {
+ "properties": {
+ "board_id": {
+ "type": "string",
+ "title": "Board Id",
+ "description": "The id of the board to add to"
},
- "vae": {
+ "image_name": {
+ "type": "string",
+ "title": "Image Name",
+ "description": "The name of the image to add"
+ }
+ },
+ "type": "object",
+ "required": ["board_id", "image_name"],
+ "title": "Body_add_image_to_board"
+ },
+ "Body_add_images_to_board": {
+ "properties": {
+ "board_id": {
+ "type": "string",
+ "title": "Board Id",
+ "description": "The id of the board to add to"
+ },
+ "image_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Image Names",
+ "description": "The names of the images to add"
+ }
+ },
+ "type": "object",
+ "required": ["board_id", "image_names"],
+ "title": "Body_add_images_to_board"
+ },
+ "Body_cancel_by_batch_ids": {
+ "properties": {
+ "batch_ids": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Batch Ids",
+ "description": "The list of batch_ids to cancel all queue items for"
+ }
+ },
+ "type": "object",
+ "required": ["batch_ids"],
+ "title": "Body_cancel_by_batch_ids"
+ },
+ "Body_create_image_upload_entry": {
+ "properties": {
+ "width": {
+ "type": "integer",
+ "title": "Width",
+ "description": "The width of the image"
+ },
+ "height": {
+ "type": "integer",
+ "title": "Height",
+ "description": "The height of the image"
+ },
+ "board_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "type": {
- "const": "cogview4_i2l",
- "default": "cogview4_i2l",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Board Id",
+ "description": "The board to add this image to, if any"
}
},
- "required": ["type", "id"],
- "tags": ["image", "latents", "vae", "i2l", "cogview4"],
- "title": "Image to Latents - CogView4",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
+ "required": ["width", "height"],
+ "title": "Body_create_image_upload_entry"
},
- "CogView4LatentsToImageInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "prototype",
- "description": "Generates an image from latents.",
- "node_pack": "invokeai",
+ "Body_create_style_preset": {
"properties": {
- "board": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string",
+ "format": "binary"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Image",
+ "description": "The image file to upload"
},
- "metadata": {
+ "data": {
+ "type": "string",
+ "title": "Data",
+ "description": "The data of the style preset to create"
+ }
+ },
+ "type": "object",
+ "required": ["data"],
+ "title": "Body_create_style_preset"
+ },
+ "Body_create_workflow": {
+ "properties": {
+ "workflow": {
+ "$ref": "#/components/schemas/WorkflowWithoutID",
+ "description": "The workflow to create"
+ }
+ },
+ "type": "object",
+ "required": ["workflow"],
+ "title": "Body_create_workflow"
+ },
+ "Body_delete_images_from_list": {
+ "properties": {
+ "image_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Image Names",
+ "description": "The list of names of images to delete"
+ }
+ },
+ "type": "object",
+ "required": ["image_names"],
+ "title": "Body_delete_images_from_list"
+ },
+ "Body_delete_videos_from_list": {
+ "properties": {
+ "video_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Video Names",
+ "description": "The list of names of videos to delete"
+ }
+ },
+ "type": "object",
+ "required": ["video_names"],
+ "title": "Body_delete_videos_from_list"
+ },
+ "Body_do_hf_login": {
+ "properties": {
+ "token": {
+ "type": "string",
+ "title": "Token",
+ "description": "Hugging Face token to use for login"
+ }
+ },
+ "type": "object",
+ "required": ["token"],
+ "title": "Body_do_hf_login"
+ },
+ "Body_download": {
+ "properties": {
+ "source": {
+ "type": "string",
+ "minLength": 1,
+ "format": "uri",
+ "title": "Source",
+ "description": "download source"
+ },
+ "dest": {
+ "type": "string",
+ "title": "Dest",
+ "description": "download destination"
+ },
+ "priority": {
+ "type": "integer",
+ "title": "Priority",
+ "description": "queue priority",
+ "default": 10
+ },
+ "access_token": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "latents": {
+ "title": "Access Token",
+ "description": "token for authorization to download"
+ }
+ },
+ "type": "object",
+ "required": ["source", "dest"],
+ "title": "Body_download"
+ },
+ "Body_download_images_from_list": {
+ "properties": {
+ "image_names": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Image Names",
+ "description": "The list of names of images to download"
},
- "vae": {
+ "board_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "type": {
- "const": "cogview4_l2i",
- "default": "cogview4_l2i",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Board Id",
+ "description": "The board from which image should be downloaded"
}
},
- "required": ["type", "id"],
- "tags": ["latents", "image", "vae", "l2i", "cogview4"],
- "title": "Latents to Image - CogView4",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "title": "Body_download_images_from_list"
},
- "CogView4ModelLoaderInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "prototype",
- "description": "Loads a CogView4 base model, outputting its submodels.",
- "node_pack": "invokeai",
+ "Body_enqueue_batch": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "batch": {
+ "$ref": "#/components/schemas/Batch",
+ "description": "Batch to process"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
+ "prepend": {
"type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "CogView4 model (Transformer) to load",
- "field_kind": "input",
- "input": "direct",
- "orig_required": true,
- "ui_model_base": ["cogview4"],
- "ui_model_type": ["main"]
- },
- "type": {
- "const": "cogview4_model_loader",
- "default": "cogview4_model_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Prepend",
+ "description": "Whether or not to prepend this batch in the queue",
+ "default": false
}
},
- "required": ["model", "type", "id"],
- "tags": ["model", "cogview4"],
- "title": "Main Model - CogView4",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/CogView4ModelLoaderOutput"
- }
+ "required": ["batch"],
+ "title": "Body_enqueue_batch"
},
- "CogView4ModelLoaderOutput": {
- "class": "output",
- "description": "CogView4 base model loader output.",
+ "Body_get_images_by_names": {
"properties": {
- "transformer": {
- "$ref": "#/components/schemas/TransformerField",
- "description": "Transformer",
- "field_kind": "output",
- "title": "Transformer",
- "ui_hidden": false
- },
- "glm_encoder": {
- "$ref": "#/components/schemas/GlmEncoderField",
- "description": "GLM (THUDM) tokenizer and text encoder",
- "field_kind": "output",
- "title": "GLM Encoder",
- "ui_hidden": false
- },
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
- },
- "type": {
- "const": "cogview4_model_loader_output",
- "default": "cogview4_model_loader_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "image_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Image Names",
+ "description": "Object containing list of image names to fetch DTOs for"
}
},
- "required": ["output_meta", "transformer", "glm_encoder", "vae", "type", "type"],
- "title": "CogView4ModelLoaderOutput",
- "type": "object"
+ "type": "object",
+ "required": ["image_names"],
+ "title": "Body_get_images_by_names"
},
- "CogView4TextEncoderInvocation": {
- "category": "prompt",
- "class": "invocation",
- "classification": "prototype",
- "description": "Encodes and preps a prompt for a cogview4 image.",
- "node_pack": "invokeai",
+ "Body_get_queue_items_by_item_ids": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "item_ids": {
+ "items": {
+ "type": "integer"
+ },
+ "type": "array",
+ "title": "Item Ids",
+ "description": "Object containing list of queue item ids to fetch queue items for"
+ }
+ },
+ "type": "object",
+ "required": ["item_ids"],
+ "title": "Body_get_queue_items_by_item_ids"
+ },
+ "Body_import_style_presets": {
+ "properties": {
+ "file": {
+ "type": "string",
+ "format": "binary",
+ "title": "File",
+ "description": "The file to import"
+ }
+ },
+ "type": "object",
+ "required": ["file"],
+ "title": "Body_import_style_presets"
+ },
+ "Body_parse_dynamicprompts": {
+ "properties": {
+ "prompt": {
+ "type": "string",
+ "title": "Prompt",
+ "description": "The prompt to parse with dynamicprompts"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "max_prompts": {
+ "type": "integer",
+ "maximum": 10000.0,
+ "minimum": 1.0,
+ "title": "Max Prompts",
+ "description": "The max number of prompts to generate",
+ "default": 1000
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "combinatorial": {
+ "type": "boolean",
+ "title": "Combinatorial",
+ "description": "Whether to use the combinatorial generator",
+ "default": true
},
- "prompt": {
+ "seed": {
"anyOf": [
{
- "type": "string"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Text prompt to encode.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Prompt",
- "ui_component": "textarea"
+ "title": "Seed",
+ "description": "The seed to use for random generation. Only used if not combinatorial"
+ }
+ },
+ "type": "object",
+ "required": ["prompt"],
+ "title": "Body_parse_dynamicprompts"
+ },
+ "Body_remove_image_from_board": {
+ "properties": {
+ "image_name": {
+ "type": "string",
+ "title": "Image Name",
+ "description": "The name of the image to remove"
+ }
+ },
+ "type": "object",
+ "required": ["image_name"],
+ "title": "Body_remove_image_from_board"
+ },
+ "Body_remove_images_from_board": {
+ "properties": {
+ "image_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Image Names",
+ "description": "The names of the images to remove"
+ }
+ },
+ "type": "object",
+ "required": ["image_names"],
+ "title": "Body_remove_images_from_board"
+ },
+ "Body_remove_video_from_board": {
+ "properties": {
+ "video_name": {
+ "type": "string",
+ "title": "Video Name",
+ "description": "The name of the video to remove from its board"
+ }
+ },
+ "type": "object",
+ "required": ["video_name"],
+ "title": "Body_remove_video_from_board"
+ },
+ "Body_set_workflow_thumbnail": {
+ "properties": {
+ "image": {
+ "type": "string",
+ "format": "binary",
+ "title": "Image",
+ "description": "The image file to upload"
+ }
+ },
+ "type": "object",
+ "required": ["image"],
+ "title": "Body_set_workflow_thumbnail"
+ },
+ "Body_star_images_in_list": {
+ "properties": {
+ "image_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Image Names",
+ "description": "The list of names of images to star"
+ }
+ },
+ "type": "object",
+ "required": ["image_names"],
+ "title": "Body_star_images_in_list"
+ },
+ "Body_star_videos_in_list": {
+ "properties": {
+ "video_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Video Names",
+ "description": "The list of names of videos to star"
+ }
+ },
+ "type": "object",
+ "required": ["video_names"],
+ "title": "Body_star_videos_in_list"
+ },
+ "Body_unstar_images_in_list": {
+ "properties": {
+ "image_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Image Names",
+ "description": "The list of names of images to unstar"
+ }
+ },
+ "type": "object",
+ "required": ["image_names"],
+ "title": "Body_unstar_images_in_list"
+ },
+ "Body_unstar_videos_in_list": {
+ "properties": {
+ "video_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Video Names",
+ "description": "The list of names of videos to unstar"
+ }
+ },
+ "type": "object",
+ "required": ["video_names"],
+ "title": "Body_unstar_videos_in_list"
+ },
+ "Body_update_model_image": {
+ "properties": {
+ "image": {
+ "type": "string",
+ "format": "binary",
+ "title": "Image"
+ }
+ },
+ "type": "object",
+ "required": ["image"],
+ "title": "Body_update_model_image"
+ },
+ "Body_update_style_preset": {
+ "properties": {
+ "image": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "binary"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Image",
+ "description": "The image file to upload"
},
- "glm_encoder": {
+ "data": {
+ "type": "string",
+ "title": "Data",
+ "description": "The data of the style preset to update"
+ }
+ },
+ "type": "object",
+ "required": ["data"],
+ "title": "Body_update_style_preset"
+ },
+ "Body_update_workflow": {
+ "properties": {
+ "workflow": {
+ "$ref": "#/components/schemas/Workflow",
+ "description": "The updated workflow"
+ }
+ },
+ "type": "object",
+ "required": ["workflow"],
+ "title": "Body_update_workflow"
+ },
+ "Body_update_workflow_is_public": {
+ "properties": {
+ "is_public": {
+ "type": "boolean",
+ "title": "Is Public",
+ "description": "Whether the workflow should be shared publicly"
+ }
+ },
+ "type": "object",
+ "required": ["is_public"],
+ "title": "Body_update_workflow_is_public"
+ },
+ "Body_upload_image": {
+ "properties": {
+ "file": {
+ "type": "string",
+ "format": "binary",
+ "title": "File"
+ },
+ "resize_to": {
"anyOf": [
{
- "$ref": "#/components/schemas/GlmEncoderField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "GLM (THUDM) tokenizer and text encoder",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "GLM Encoder"
+ "title": "Resize To",
+ "description": "Dimensions to resize the image to, must be stringified tuple of 2 integers. Max total pixel count: 16777216",
+ "examples": ["\"[1024,1024]\""]
},
- "type": {
- "const": "cogview4_text_encoder",
- "default": "cogview4_text_encoder",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "metadata": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Metadata",
+ "description": "The metadata to associate with the image, must be a stringified JSON dict"
}
},
- "required": ["type", "id"],
- "tags": ["prompt", "conditioning", "cogview4"],
- "title": "Prompt - CogView4",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/CogView4ConditioningOutput"
- }
+ "required": ["file"],
+ "title": "Body_upload_image"
},
- "CollectInvocation": {
+ "Body_upload_video": {
+ "properties": {
+ "file": {
+ "type": "string",
+ "format": "binary",
+ "title": "File"
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Metadata",
+ "description": "The metadata to associate with the video, must be a stringified JSON dict"
+ }
+ },
+ "type": "object",
+ "required": ["file"],
+ "title": "Body_upload_video"
+ },
+ "BooleanCollectionInvocation": {
+ "category": "primitives",
"class": "invocation",
"classification": "stable",
- "description": "Collects values into a collection",
+ "description": "A collection of boolean primitive values",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -16498,139 +15236,219 @@
"title": "Use Cache",
"type": "boolean"
},
- "item": {
- "anyOf": [
- {},
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The item to collect (all inputs must be of the same type)",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Collection Item",
- "ui_type": "CollectionItemField"
- },
"collection": {
"default": [],
- "description": "An optional collection to append to",
+ "description": "The collection of boolean values",
"field_kind": "input",
- "input": "connection",
- "items": {},
+ "input": "any",
+ "items": {
+ "type": "boolean"
+ },
"orig_default": [],
"orig_required": false,
"title": "Collection",
- "type": "array",
- "ui_type": "CollectionField"
+ "type": "array"
},
"type": {
- "const": "collect",
- "default": "collect",
+ "const": "boolean_collection",
+ "default": "boolean_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "title": "CollectInvocation",
+ "tags": ["primitives", "boolean", "collection"],
+ "title": "Boolean Collection Primitive",
"type": "object",
- "version": "1.1.0",
+ "version": "1.0.2",
"output": {
- "$ref": "#/components/schemas/CollectInvocationOutput"
+ "$ref": "#/components/schemas/BooleanCollectionOutput"
}
},
- "CollectInvocationOutput": {
+ "BooleanCollectionOutput": {
"class": "output",
+ "description": "Base class for nodes that output a collection of booleans",
"properties": {
"collection": {
- "description": "The collection of input items",
+ "description": "The output boolean collection",
"field_kind": "output",
- "items": {},
+ "items": {
+ "type": "boolean"
+ },
"title": "Collection",
"type": "array",
- "ui_hidden": false,
- "ui_type": "CollectionField"
+ "ui_hidden": false
},
"type": {
- "const": "collect_output",
- "default": "collect_output",
+ "const": "boolean_collection_output",
+ "default": "boolean_collection_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["output_meta", "collection", "type", "type"],
- "title": "CollectInvocationOutput",
+ "title": "BooleanCollectionOutput",
"type": "object"
},
- "ColorCollectionOutput": {
+ "BooleanInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "A boolean primitive value",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "value": {
+ "default": false,
+ "description": "The boolean value",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Value",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "boolean",
+ "default": "boolean",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["primitives", "boolean"],
+ "title": "Boolean Primitive",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/BooleanOutput"
+ }
+ },
+ "BooleanOutput": {
"class": "output",
- "description": "Base class for nodes that output a collection of colors",
+ "description": "Base class for nodes that output a single boolean",
+ "properties": {
+ "value": {
+ "description": "The output boolean",
+ "field_kind": "output",
+ "title": "Value",
+ "type": "boolean",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "boolean_output",
+ "default": "boolean_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "value", "type", "type"],
+ "title": "BooleanOutput",
+ "type": "object"
+ },
+ "BoundingBoxCollectionOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a collection of bounding boxes",
"properties": {
"collection": {
- "description": "The output colors",
+ "description": "The output bounding boxes.",
"field_kind": "output",
"items": {
- "$ref": "#/components/schemas/ColorField"
+ "$ref": "#/components/schemas/BoundingBoxField"
},
- "title": "Collection",
+ "title": "Bounding Boxes",
"type": "array",
"ui_hidden": false
},
"type": {
- "const": "color_collection_output",
- "default": "color_collection_output",
+ "const": "bounding_box_collection_output",
+ "default": "bounding_box_collection_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["output_meta", "collection", "type", "type"],
- "title": "ColorCollectionOutput",
+ "title": "BoundingBoxCollectionOutput",
"type": "object"
},
- "ColorCorrectInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Matches the color histogram of a base image to a reference image, optionally\nusing a mask to only color-correct certain regions of the base image.",
- "node_pack": "invokeai",
+ "BoundingBoxField": {
+ "description": "A bounding box primitive value.",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "x_min": {
+ "description": "The minimum x-coordinate of the bounding box (inclusive).",
+ "title": "X Min",
+ "type": "integer"
},
- "metadata": {
+ "x_max": {
+ "description": "The maximum x-coordinate of the bounding box (exclusive).",
+ "title": "X Max",
+ "type": "integer"
+ },
+ "y_min": {
+ "description": "The minimum y-coordinate of the bounding box (inclusive).",
+ "title": "Y Min",
+ "type": "integer"
+ },
+ "y_max": {
+ "description": "The maximum y-coordinate of the bounding box (exclusive).",
+ "title": "Y Max",
+ "type": "integer"
+ },
+ "score": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "maximum": 1.0,
+ "minimum": 0.0,
+ "type": "number"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
+ "description": "The score associated with the bounding box. In the range [0, 1]. This value is typically set when the bounding box was produced by a detector and has an associated confidence score.",
+ "title": "Score"
+ }
+ },
+ "required": ["x_min", "x_max", "y_min", "y_max"],
+ "title": "BoundingBoxField",
+ "type": "object"
+ },
+ "BoundingBoxInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Create a bounding box manually by supplying box coordinates",
+ "node_pack": "invokeai",
+ "properties": {
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -16655,719 +15473,388 @@
"title": "Use Cache",
"type": "boolean"
},
- "base_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to color-correct",
+ "x_min": {
+ "default": 0,
+ "description": "x-coordinate of the bounding box's top left vertex",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "X Min",
+ "type": "integer"
},
- "color_reference": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Reference image for color-correction",
+ "y_min": {
+ "default": 0,
+ "description": "y-coordinate of the bounding box's top left vertex",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Y Min",
+ "type": "integer"
},
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional mask to limit color correction area",
+ "x_max": {
+ "default": 0,
+ "description": "x-coordinate of the bounding box's bottom right vertex",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "X Max",
+ "type": "integer"
},
- "colorspace": {
- "default": "RGB",
- "description": "Colorspace in which to apply histogram matching",
- "enum": ["RGB", "YCbCr", "YCbCr-Chroma", "YCbCr-Luma"],
+ "y_max": {
+ "default": 0,
+ "description": "y-coordinate of the bounding box's bottom right vertex",
"field_kind": "input",
"input": "any",
- "orig_default": "RGB",
+ "orig_default": 0,
"orig_required": false,
- "title": "Color Space",
- "type": "string"
+ "title": "Y Max",
+ "type": "integer"
},
"type": {
- "const": "color_correct",
- "default": "color_correct",
+ "const": "bounding_box",
+ "default": "bounding_box",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "color"],
- "title": "Color Correct",
+ "tags": ["primitives", "segmentation", "collection", "bounding box"],
+ "title": "Bounding Box",
"type": "object",
- "version": "2.0.0",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/BoundingBoxOutput"
}
},
- "ColorField": {
- "description": "A color primitive field",
+ "BoundingBoxOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a single bounding box",
"properties": {
- "r": {
- "description": "The red component",
- "maximum": 255,
- "minimum": 0,
- "title": "R",
- "type": "integer"
+ "bounding_box": {
+ "$ref": "#/components/schemas/BoundingBoxField",
+ "description": "The output bounding box.",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "g": {
- "description": "The green component",
- "maximum": 255,
- "minimum": 0,
- "title": "G",
- "type": "integer"
+ "type": {
+ "const": "bounding_box_output",
+ "default": "bounding_box_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "bounding_box", "type", "type"],
+ "title": "BoundingBoxOutput",
+ "type": "object"
+ },
+ "BulkDeleteModelsRequest": {
+ "properties": {
+ "keys": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Keys",
+ "description": "List of model keys to delete"
+ }
+ },
+ "type": "object",
+ "required": ["keys"],
+ "title": "BulkDeleteModelsRequest",
+ "description": "Request body for bulk model deletion."
+ },
+ "BulkDeleteModelsResponse": {
+ "properties": {
+ "deleted": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted",
+ "description": "List of successfully deleted model keys"
},
- "b": {
- "description": "The blue component",
- "maximum": 255,
- "minimum": 0,
- "title": "B",
+ "failed": {
+ "items": {
+ "additionalProperties": true,
+ "type": "object"
+ },
+ "type": "array",
+ "title": "Failed",
+ "description": "List of failed deletions with error messages"
+ }
+ },
+ "type": "object",
+ "required": ["deleted", "failed"],
+ "title": "BulkDeleteModelsResponse",
+ "description": "Response body for bulk model deletion."
+ },
+ "BulkDownloadCompleteEvent": {
+ "description": "Event model for bulk_download_complete",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
"type": "integer"
},
- "a": {
- "description": "The alpha component",
- "maximum": 255,
- "minimum": 0,
- "title": "A",
- "type": "integer"
+ "bulk_download_id": {
+ "description": "The ID of the bulk image download",
+ "title": "Bulk Download Id",
+ "type": "string"
+ },
+ "bulk_download_item_id": {
+ "description": "The ID of the bulk image download item",
+ "title": "Bulk Download Item Id",
+ "type": "string"
+ },
+ "bulk_download_item_name": {
+ "description": "The name of the bulk image download item",
+ "title": "Bulk Download Item Name",
+ "type": "string"
+ },
+ "user_id": {
+ "default": "system",
+ "description": "The ID of the user who initiated the download",
+ "title": "User Id",
+ "type": "string"
}
},
- "required": ["r", "g", "b", "a"],
- "title": "ColorField",
+ "required": ["timestamp", "bulk_download_id", "bulk_download_item_id", "bulk_download_item_name", "user_id"],
+ "title": "BulkDownloadCompleteEvent",
"type": "object"
},
- "ColorInvocation": {
- "category": "primitives",
- "class": "invocation",
- "classification": "stable",
- "description": "A color primitive value",
- "node_pack": "invokeai",
+ "BulkDownloadErrorEvent": {
+ "description": "Event model for bulk_download_error",
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "bulk_download_id": {
+ "description": "The ID of the bulk image download",
+ "title": "Bulk Download Id",
"type": "string"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "bulk_download_item_id": {
+ "description": "The ID of the bulk image download item",
+ "title": "Bulk Download Item Id",
+ "type": "string"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "bulk_download_item_name": {
+ "description": "The name of the bulk image download item",
+ "title": "Bulk Download Item Name",
+ "type": "string"
},
- "color": {
- "$ref": "#/components/schemas/ColorField",
- "default": {
- "r": 0,
- "g": 0,
- "b": 0,
- "a": 255
- },
- "description": "The color value",
- "field_kind": "input",
- "input": "any",
- "orig_default": {
- "a": 255,
- "b": 0,
- "g": 0,
- "r": 0
- },
- "orig_required": false
+ "user_id": {
+ "default": "system",
+ "description": "The ID of the user who initiated the download",
+ "title": "User Id",
+ "type": "string"
},
- "type": {
- "const": "color",
- "default": "color",
- "field_kind": "node_attribute",
- "title": "type",
+ "error": {
+ "description": "The error message",
+ "title": "Error",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["primitives", "color"],
- "title": "Color Primitive",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/ColorOutput"
- }
+ "required": [
+ "timestamp",
+ "bulk_download_id",
+ "bulk_download_item_id",
+ "bulk_download_item_name",
+ "user_id",
+ "error"
+ ],
+ "title": "BulkDownloadErrorEvent",
+ "type": "object"
},
- "ColorMapInvocation": {
- "category": "controlnet_preprocessors",
- "class": "invocation",
- "classification": "stable",
- "description": "Generates a color map from the provided image.",
- "node_pack": "invokeai",
+ "BulkDownloadStartedEvent": {
+ "description": "Event model for bulk_download_started",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
+ "bulk_download_id": {
+ "description": "The ID of the bulk image download",
+ "title": "Bulk Download Id",
"type": "string"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to process",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "bulk_download_item_id": {
+ "description": "The ID of the bulk image download item",
+ "title": "Bulk Download Item Id",
+ "type": "string"
},
- "tile_size": {
- "default": 64,
- "description": "Tile size",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 64,
- "orig_required": false,
- "title": "Tile Size",
- "type": "integer"
+ "bulk_download_item_name": {
+ "description": "The name of the bulk image download item",
+ "title": "Bulk Download Item Name",
+ "type": "string"
},
- "type": {
- "const": "color_map",
- "default": "color_map",
- "field_kind": "node_attribute",
- "title": "type",
+ "user_id": {
+ "default": "system",
+ "description": "The ID of the user who initiated the download",
+ "title": "User Id",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["controlnet"],
- "title": "Color Map",
+ "required": ["timestamp", "bulk_download_id", "bulk_download_item_id", "bulk_download_item_name", "user_id"],
+ "title": "BulkDownloadStartedEvent",
+ "type": "object"
+ },
+ "BulkReidentifyModelsRequest": {
+ "properties": {
+ "keys": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Keys",
+ "description": "List of model keys to reidentify"
+ }
+ },
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": ["keys"],
+ "title": "BulkReidentifyModelsRequest",
+ "description": "Request body for bulk model reidentification."
},
- "ColorOutput": {
- "class": "output",
- "description": "Base class for nodes that output a single color",
+ "BulkReidentifyModelsResponse": {
"properties": {
- "color": {
- "$ref": "#/components/schemas/ColorField",
- "description": "The output color",
- "field_kind": "output",
- "ui_hidden": false
+ "succeeded": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Succeeded",
+ "description": "List of successfully reidentified model keys"
},
- "type": {
- "const": "color_output",
- "default": "color_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "failed": {
+ "items": {
+ "additionalProperties": true,
+ "type": "object"
+ },
+ "type": "array",
+ "title": "Failed",
+ "description": "List of failed reidentifications with error messages"
}
},
- "required": ["output_meta", "color", "type", "type"],
- "title": "ColorOutput",
- "type": "object"
+ "type": "object",
+ "required": ["succeeded", "failed"],
+ "title": "BulkReidentifyModelsResponse",
+ "description": "Response body for bulk model reidentification."
},
- "CompelInvocation": {
- "category": "prompt",
- "class": "invocation",
- "classification": "stable",
- "description": "Parse prompt using compel package to conditioning.",
- "node_pack": "invokeai",
+ "CLIPEmbed_Diffusers_G_Config": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "prompt": {
- "default": "",
- "description": "Prompt to be parsed by Compel to create a conditioning tensor",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Prompt",
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
"type": "string",
- "ui_component": "textarea"
+ "title": "Name",
+ "description": "Name of the model."
},
- "clip": {
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "CLIP"
+ "title": "Description",
+ "description": "Model description"
},
- "mask": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/TensorField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "A mask defining the region that this conditioning prompt applies to.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "type": {
- "const": "compel",
- "default": "compel",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["prompt", "compel"],
- "title": "Prompt - SD1.5",
- "type": "object",
- "version": "1.2.1",
- "output": {
- "$ref": "#/components/schemas/ConditioningOutput"
- }
- },
- "ConditioningCollectionInvocation": {
- "category": "primitives",
- "class": "invocation",
- "classification": "stable",
- "description": "A collection of conditioning tensor primitive values",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "collection": {
- "default": [],
- "description": "The collection of conditioning tensors",
- "field_kind": "input",
- "input": "any",
- "items": {
- "$ref": "#/components/schemas/ConditioningField"
- },
- "orig_default": [],
- "orig_required": false,
- "title": "Collection",
- "type": "array"
- },
- "type": {
- "const": "conditioning_collection",
- "default": "conditioning_collection",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["primitives", "conditioning", "collection"],
- "title": "Conditioning Collection Primitive",
- "type": "object",
- "version": "1.0.2",
- "output": {
- "$ref": "#/components/schemas/ConditioningCollectionOutput"
- }
- },
- "ConditioningCollectionOutput": {
- "class": "output",
- "description": "Base class for nodes that output a collection of conditioning tensors",
- "properties": {
- "collection": {
- "description": "The output conditioning tensors",
- "field_kind": "output",
- "items": {
- "$ref": "#/components/schemas/ConditioningField"
- },
- "title": "Collection",
- "type": "array",
- "ui_hidden": false
- },
- "type": {
- "const": "conditioning_collection_output",
- "default": "conditioning_collection_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "collection", "type", "type"],
- "title": "ConditioningCollectionOutput",
- "type": "object"
- },
- "ConditioningField": {
- "description": "A conditioning tensor primitive value",
- "properties": {
- "conditioning_name": {
- "description": "The name of conditioning tensor",
- "title": "Conditioning Name",
- "type": "string"
- },
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TensorField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True."
- }
- },
- "required": ["conditioning_name"],
- "title": "ConditioningField",
- "type": "object"
- },
- "ConditioningInvocation": {
- "category": "primitives",
- "class": "invocation",
- "classification": "stable",
- "description": "A conditioning tensor primitive value",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "conditioning": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ConditioningField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "type": {
- "const": "conditioning",
- "default": "conditioning",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["primitives", "conditioning"],
- "title": "Conditioning Primitive",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/ConditioningOutput"
- }
- },
- "ConditioningOutput": {
- "class": "output",
- "description": "Base class for nodes that output a single conditioning tensor",
- "properties": {
- "conditioning": {
- "$ref": "#/components/schemas/ConditioningField",
- "description": "Conditioning tensor",
- "field_kind": "output",
- "ui_hidden": false
- },
- "type": {
- "const": "conditioning_output",
- "default": "conditioning_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "conditioning", "type", "type"],
- "title": "ConditioningOutput",
- "type": "object"
- },
- "ContentShuffleInvocation": {
- "category": "controlnet_preprocessors",
- "class": "invocation",
- "classification": "stable",
- "description": "Shuffles the image, similar to a 'liquify' filter.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "metadata": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to process",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
- "scale_factor": {
- "default": 256,
- "description": "The scale factor used for the shuffle",
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 256,
- "orig_required": false,
- "title": "Scale Factor",
- "type": "integer"
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
"type": {
- "const": "content_shuffle",
- "default": "content_shuffle",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["controlnet", "normal"],
- "title": "Content Shuffle",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "ControlAdapterDefaultSettings": {
- "properties": {
- "preprocessor": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Preprocessor"
+ "type": "string",
+ "const": "clip_embed",
+ "title": "Type",
+ "default": "clip_embed"
},
- "fp8_storage": {
+ "cpu_only": {
"anyOf": [
{
"type": "boolean"
@@ -17376,97 +15863,39 @@
"type": "null"
}
],
- "title": "Fp8 Storage",
- "description": "Store weights in FP8 to reduce VRAM usage (~50% savings). Weights are cast to compute dtype during inference."
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": ["preprocessor"],
- "title": "ControlAdapterDefaultSettings"
- },
- "ControlField": {
- "properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The control image"
- },
- "control_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "The ControlNet model to use"
- },
- "control_weight": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "items": {
- "type": "number"
- },
- "type": "array"
- }
- ],
- "default": 1,
- "description": "The weight given to the ControlNet",
- "title": "Control Weight"
- },
- "begin_step_percent": {
- "default": 0,
- "description": "When the ControlNet is first applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "Begin Step Percent",
- "type": "number"
- },
- "end_step_percent": {
- "default": 1,
- "description": "When the ControlNet is last applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "End Step Percent",
- "type": "number"
- },
- "control_mode": {
- "default": "balanced",
- "description": "The control mode to use",
- "enum": ["balanced", "more_prompt", "more_control", "unbalanced"],
- "title": "Control Mode",
- "type": "string"
- },
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode to use",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
- "title": "Resize Mode",
- "type": "string"
- }
- },
- "required": ["image", "control_model"],
- "title": "ControlField",
- "type": "object"
- },
- "ControlLoRAField": {
- "properties": {
- "lora": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load lora model"
- },
- "weight": {
- "description": "Weight to apply to lora model",
- "title": "Weight",
- "type": "number"
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
},
- "img": {
- "$ref": "#/components/schemas/ImageField",
- "description": "Image to use in structural conditioning"
+ "variant": {
+ "type": "string",
+ "const": "gigantic",
+ "title": "Variant",
+ "default": "gigantic"
}
},
- "required": ["lora", "weight", "img"],
- "title": "ControlLoRAField",
- "type": "object"
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "base",
+ "type",
+ "cpu_only",
+ "variant"
+ ],
+ "title": "CLIPEmbed_Diffusers_G_Config"
},
- "ControlLoRA_LyCORIS_FLUX_Config": {
+ "CLIPEmbed_Diffusers_L_Config": {
"properties": {
"key": {
"type": "string",
@@ -17550,48 +15979,45 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
"base": {
"type": "string",
- "const": "flux",
+ "const": "any",
"title": "Base",
- "default": "flux"
+ "default": "any"
},
"type": {
"type": "string",
- "const": "control_lora",
+ "const": "clip_embed",
"title": "Type",
- "default": "control_lora"
- },
- "format": {
- "type": "string",
- "const": "lycoris",
- "title": "Format",
- "default": "lycoris"
+ "default": "clip_embed"
},
- "trigger_phrases": {
+ "cpu_only": {
"anyOf": [
{
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true
+ "type": "boolean"
},
{
"type": "null"
}
],
- "title": "Trigger Phrases"
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
+ },
+ "variant": {
+ "type": "string",
+ "const": "large",
+ "title": "Variant",
+ "default": "large"
}
},
"type": "object",
@@ -17607,26 +16033,77 @@
"source_api_response",
"source_url",
"cover_image",
- "default_settings",
+ "format",
+ "repo_variant",
"base",
"type",
- "format",
- "trigger_phrases"
+ "cpu_only",
+ "variant"
],
- "title": "ControlLoRA_LyCORIS_FLUX_Config",
- "description": "Model config for Control LoRA models."
+ "title": "CLIPEmbed_Diffusers_L_Config"
},
- "ControlNetInvocation": {
- "category": "conditioning",
- "class": "invocation",
- "classification": "stable",
- "description": "Collects ControlNet info to pass to other nodes",
- "node_pack": "invokeai",
+ "CLIPField": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
+ "tokenizer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load tokenizer submodel"
+ },
+ "text_encoder": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load text_encoder submodel"
+ },
+ "skipped_layers": {
+ "description": "Number of skipped layers in text_encoder",
+ "title": "Skipped Layers",
+ "type": "integer"
+ },
+ "loras": {
+ "description": "LoRAs to apply on model loading",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "Loras",
+ "type": "array"
+ }
+ },
+ "required": ["tokenizer", "text_encoder", "skipped_layers", "loras"],
+ "title": "CLIPField",
+ "type": "object"
+ },
+ "CLIPOutput": {
+ "class": "output",
+ "description": "Base class for invocations that output a CLIP field",
+ "properties": {
+ "clip": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "clip_output",
+ "default": "clip_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "clip", "type", "type"],
+ "title": "CLIPOutput",
+ "type": "object"
+ },
+ "CLIPSkipInvocation": {
+ "category": "prompt",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Skip layers in clip text_encoder model.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
"type": "string"
},
"is_intermediate": {
@@ -17647,271 +16124,82 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The control image",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "control_model": {
+ "clip": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
- "description": "ControlNet model to load",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_required": true,
- "ui_model_base": ["sd-1", "sd-2", "sdxl"],
- "ui_model_type": ["controlnet"]
- },
- "control_weight": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "items": {
- "type": "number"
- },
- "type": "array"
- }
- ],
- "default": 1.0,
- "description": "The weight given to the ControlNet",
- "field_kind": "input",
- "ge": -1,
- "input": "any",
- "le": 2,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Control Weight"
+ "title": "CLIP"
},
- "begin_step_percent": {
+ "skipped_layers": {
"default": 0,
- "description": "When the ControlNet is first applied (% of total steps)",
+ "description": "Number of layers to skip in text encoder",
"field_kind": "input",
"input": "any",
- "maximum": 1,
"minimum": 0,
"orig_default": 0,
"orig_required": false,
- "title": "Begin Step Percent",
- "type": "number"
- },
- "end_step_percent": {
- "default": 1,
- "description": "When the ControlNet is last applied (% of total steps)",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1,
- "orig_required": false,
- "title": "End Step Percent",
- "type": "number"
- },
- "control_mode": {
- "default": "balanced",
- "description": "The control mode used",
- "enum": ["balanced", "more_prompt", "more_control", "unbalanced"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "balanced",
- "orig_required": false,
- "title": "Control Mode",
- "type": "string"
- },
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode used",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "just_resize",
- "orig_required": false,
- "title": "Resize Mode",
- "type": "string"
+ "title": "Skipped Layers",
+ "type": "integer"
},
"type": {
- "const": "controlnet",
- "default": "controlnet",
+ "const": "clip_skip",
+ "default": "clip_skip",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["controlnet"],
- "title": "ControlNet - SD1.5, SD2, SDXL",
+ "tags": ["clipskip", "clip", "skip"],
+ "title": "Apply CLIP Skip - SD1.5, SDXL",
"type": "object",
- "version": "1.1.3",
+ "version": "1.1.1",
"output": {
- "$ref": "#/components/schemas/ControlOutput"
+ "$ref": "#/components/schemas/CLIPSkipInvocationOutput"
}
},
- "ControlNetMetadataField": {
+ "CLIPSkipInvocationOutput": {
+ "class": "output",
+ "description": "CLIP skip node output",
"properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The control image"
- },
- "processed_image": {
+ "clip": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The control image, after processing."
- },
- "control_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "The ControlNet model to use"
- },
- "control_weight": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "items": {
- "type": "number"
- },
- "type": "array"
- }
- ],
- "default": 1,
- "description": "The weight given to the ControlNet",
- "title": "Control Weight"
- },
- "begin_step_percent": {
- "default": 0,
- "description": "When the ControlNet is first applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "Begin Step Percent",
- "type": "number"
- },
- "end_step_percent": {
- "default": 1,
- "description": "When the ControlNet is last applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "End Step Percent",
- "type": "number"
- },
- "control_mode": {
- "default": "balanced",
- "description": "The control mode to use",
- "enum": ["balanced", "more_prompt", "more_control", "unbalanced"],
- "title": "Control Mode",
- "type": "string"
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP",
+ "ui_hidden": false
},
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode to use",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
- "title": "Resize Mode",
+ "type": {
+ "const": "clip_skip_output",
+ "default": "clip_skip_output",
+ "field_kind": "node_attribute",
+ "title": "type",
"type": "string"
}
},
- "required": ["image", "control_model"],
- "title": "ControlNetMetadataField",
+ "required": ["output_meta", "clip", "type", "type"],
+ "title": "CLIPSkipInvocationOutput",
"type": "object"
},
- "ControlNetRecallParameter": {
- "properties": {
- "model_name": {
- "type": "string",
- "title": "Model Name",
- "description": "The name of the ControlNet/T2I Adapter/Control LoRA model"
- },
- "image_name": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Image Name",
- "description": "The filename of the control image in outputs/images"
- },
- "weight": {
- "type": "number",
- "maximum": 2.0,
- "minimum": -1.0,
- "title": "Weight",
- "description": "The weight for the control adapter",
- "default": 1.0
- },
- "begin_step_percent": {
- "anyOf": [
- {
- "type": "number",
- "maximum": 1.0,
- "minimum": 0.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Begin Step Percent",
- "description": "When the control adapter is first applied (% of total steps)"
- },
- "end_step_percent": {
- "anyOf": [
- {
- "type": "number",
- "maximum": 1.0,
- "minimum": 0.0
- },
- {
- "type": "null"
- }
- ],
- "title": "End Step Percent",
- "description": "When the control adapter is last applied (% of total steps)"
- },
- "control_mode": {
- "anyOf": [
- {
- "type": "string",
- "enum": ["balanced", "more_prompt", "more_control"]
- },
- {
- "type": "null"
- }
- ],
- "title": "Control Mode",
- "description": "The control mode (ControlNet only)"
- }
- },
- "type": "object",
- "required": ["model_name"],
- "title": "ControlNetRecallParameter",
- "description": "ControlNet configuration for recall"
- },
- "ControlNet_Checkpoint_Anima_Config": {
+ "CLIPVision_Diffusers_Config": {
"properties": {
"key": {
"type": "string",
@@ -17995,57 +16283,39 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "config_path": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
- },
"format": {
"type": "string",
- "const": "checkpoint",
+ "const": "diffusers",
"title": "Format",
- "default": "checkpoint"
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
"base": {
"type": "string",
- "const": "anima",
+ "const": "any",
"title": "Base",
- "default": "anima"
+ "default": "any"
},
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
+ "type": {
+ "type": "string",
+ "const": "clip_vision",
+ "title": "Type",
+ "default": "clip_vision"
},
- "cond_in_channels": {
+ "cpu_only": {
"anyOf": [
{
- "type": "integer"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "title": "Cond In Channels",
- "description": "Number of conditioning image channels (3 = RGB control image, 4 = RGB + inpaint mask). None for models installed before this field was recorded."
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
}
},
"type": "object",
@@ -18061,1327 +16331,1797 @@
"source_api_response",
"source_url",
"cover_image",
- "config_path",
- "type",
"format",
+ "repo_variant",
"base",
- "default_settings",
- "cond_in_channels"
+ "type",
+ "cpu_only"
],
- "title": "ControlNet_Checkpoint_Anima_Config",
- "description": "Model config for Anima ControlNet-LLLite adapter models (Safetensors checkpoint).\n\nAnima LLLite adapters are standalone adapters consisting of a shared conditioning trunk\n(lllite_conditioning1) that encodes a conditioning image, plus tiny per-Linear modules\n(lllite_dit_blocks_*) that perturb the inputs of target Linears in the Anima DiT."
+ "title": "CLIPVision_Diffusers_Config",
+ "description": "Model config for CLIPVision."
},
- "ControlNet_Checkpoint_FLUX_Config": {
+ "CV2InfillInvocation": {
+ "category": "inpaint",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Infills transparent areas of an image using OpenCV Inpainting",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "config_path": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "The image to process",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
"type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
- },
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
- },
- "base": {
- "type": "string",
- "const": "flux",
- "title": "Base",
- "default": "flux"
+ "const": "infill_cv2",
+ "default": "infill_cv2",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["image", "inpaint"],
+ "title": "CV2 Infill",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Checkpoint_FLUX_Config"
+ "version": "1.2.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "ControlNet_Checkpoint_SD1_Config": {
+ "CacheStats": {
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "hits": {
+ "type": "integer",
+ "title": "Hits",
+ "default": 0
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "misses": {
+ "type": "integer",
+ "title": "Misses",
+ "default": 0
},
- "file_size": {
+ "high_watermark": {
"type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "title": "High Watermark",
+ "default": 0
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "in_cache": {
+ "type": "integer",
+ "title": "In Cache",
+ "default": 0
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "cleared": {
+ "type": "integer",
+ "title": "Cleared",
+ "default": 0
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "cache_size": {
+ "type": "integer",
+ "title": "Cache Size",
+ "default": 0
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "loaded_model_sizes": {
+ "additionalProperties": {
+ "type": "integer"
+ },
+ "type": "object",
+ "title": "Loaded Model Sizes"
+ }
+ },
+ "type": "object",
+ "title": "CacheStats",
+ "description": "Collect statistics on cache performance."
+ },
+ "CalculateImageTilesEvenSplitInvocation": {
+ "category": "tiles",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Calculate the coordinates and overlaps of tiles that cover a target image shape.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source_api_response": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "image_width": {
+ "default": 1024,
+ "description": "The image width, in pixels, to calculate tiles for.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Image Width",
+ "type": "integer"
},
- "config_path": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "image_height": {
+ "default": 1024,
+ "description": "The image height, in pixels, to calculate tiles for.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Image Height",
+ "type": "integer"
},
- "type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
+ "num_tiles_x": {
+ "default": 2,
+ "description": "Number of tiles to divide image into on the x axis",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 2,
+ "orig_required": false,
+ "title": "Num Tiles X",
+ "type": "integer"
},
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "num_tiles_y": {
+ "default": 2,
+ "description": "Number of tiles to divide image into on the y axis",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 2,
+ "orig_required": false,
+ "title": "Num Tiles Y",
+ "type": "integer"
},
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
+ "overlap": {
+ "default": 128,
+ "description": "The overlap, in pixels, between adjacent tiles.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "multipleOf": 8,
+ "orig_default": 128,
+ "orig_required": false,
+ "title": "Overlap",
+ "type": "integer"
},
- "base": {
- "type": "string",
- "const": "sd-1",
- "title": "Base",
- "default": "sd-1"
+ "type": {
+ "const": "calculate_image_tiles_even_split",
+ "default": "calculate_image_tiles_even_split",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["tiles"],
+ "title": "Calculate Image Tiles Even Split",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Checkpoint_SD1_Config"
+ "version": "1.1.1",
+ "output": {
+ "$ref": "#/components/schemas/CalculateImageTilesOutput"
+ }
},
- "ControlNet_Checkpoint_SD2_Config": {
+ "CalculateImageTilesInvocation": {
+ "category": "tiles",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Calculate the coordinates and overlaps of tiles that cover a target image shape.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "image_width": {
+ "default": 1024,
+ "description": "The image width, in pixels, to calculate tiles for.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Image Width",
+ "type": "integer"
},
- "source_api_response": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "image_height": {
+ "default": 1024,
+ "description": "The image height, in pixels, to calculate tiles for.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Image Height",
+ "type": "integer"
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "tile_width": {
+ "default": 576,
+ "description": "The tile width, in pixels.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 576,
+ "orig_required": false,
+ "title": "Tile Width",
+ "type": "integer"
},
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "tile_height": {
+ "default": 576,
+ "description": "The tile height, in pixels.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 576,
+ "orig_required": false,
+ "title": "Tile Height",
+ "type": "integer"
},
- "config_path": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "overlap": {
+ "default": 128,
+ "description": "The target overlap, in pixels, between adjacent tiles. Adjacent tiles will overlap by at least this amount",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 128,
+ "orig_required": false,
+ "title": "Overlap",
+ "type": "integer"
},
"type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
- },
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
- },
- "base": {
- "type": "string",
- "const": "sd-2",
- "title": "Base",
- "default": "sd-2"
+ "const": "calculate_image_tiles",
+ "default": "calculate_image_tiles",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["tiles"],
+ "title": "Calculate Image Tiles",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Checkpoint_SD2_Config"
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/CalculateImageTilesOutput"
+ }
},
- "ControlNet_Checkpoint_SDXL_Config": {
+ "CalculateImageTilesMinimumOverlapInvocation": {
+ "category": "tiles",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Calculate the coordinates and overlaps of tiles that cover a target image shape.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "image_width": {
+ "default": 1024,
+ "description": "The image width, in pixels, to calculate tiles for.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Image Width",
+ "type": "integer"
},
- "name": {
+ "image_height": {
+ "default": 1024,
+ "description": "The image height, in pixels, to calculate tiles for.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Image Height",
+ "type": "integer"
+ },
+ "tile_width": {
+ "default": 576,
+ "description": "The tile width, in pixels.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 576,
+ "orig_required": false,
+ "title": "Tile Width",
+ "type": "integer"
+ },
+ "tile_height": {
+ "default": 576,
+ "description": "The tile height, in pixels.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 576,
+ "orig_required": false,
+ "title": "Tile Height",
+ "type": "integer"
+ },
+ "min_overlap": {
+ "default": 128,
+ "description": "Minimum overlap between adjacent tiles, in pixels.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 128,
+ "orig_required": false,
+ "title": "Min Overlap",
+ "type": "integer"
+ },
+ "type": {
+ "const": "calculate_image_tiles_min_overlap",
+ "default": "calculate_image_tiles_min_overlap",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["tiles"],
+ "title": "Calculate Image Tiles Minimum Overlap",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/CalculateImageTilesOutput"
+ }
+ },
+ "CalculateImageTilesOutput": {
+ "class": "output",
+ "properties": {
+ "tiles": {
+ "description": "The tiles coordinates that cover a particular image shape.",
+ "field_kind": "output",
+ "items": {
+ "$ref": "#/components/schemas/Tile"
+ },
+ "title": "Tiles",
+ "type": "array",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "calculate_image_tiles_output",
+ "default": "calculate_image_tiles_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "tiles", "type", "type"],
+ "title": "CalculateImageTilesOutput",
+ "type": "object"
+ },
+ "CallSavedWorkflowInvocation": {
+ "category": "workflow",
+ "class": "invocation",
+ "classification": "beta",
+ "description": "Displays and later executes against a selected saved workflow.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": false,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "workflow_id": {
+ "default": "",
+ "description": "The selected saved workflow ID, managed by the workflow editor UI.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Workflow Id",
"type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "ui_type": "SavedWorkflowField"
},
- "description": {
+ "workflow_inputs": {
+ "additionalProperties": true,
+ "default": {},
+ "description": "Literal values for the selected workflow's exposed inputs, managed by the workflow editor UI.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": {},
+ "orig_required": false,
+ "title": "Workflow Inputs",
+ "type": "object",
+ "ui_hidden": true
+ },
+ "type": {
+ "const": "call_saved_workflow",
+ "default": "call_saved_workflow",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["workflow", "saved", "library"],
+ "title": "Call Saved Workflow",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/WorkflowReturnOutput"
+ }
+ },
+ "CancelAllExceptCurrentResult": {
+ "properties": {
+ "canceled": {
+ "type": "integer",
+ "title": "Canceled",
+ "description": "Number of queue items canceled"
+ }
+ },
+ "type": "object",
+ "required": ["canceled"],
+ "title": "CancelAllExceptCurrentResult",
+ "description": "Result of canceling all except current"
+ },
+ "CancelByBatchIDsResult": {
+ "properties": {
+ "canceled": {
+ "type": "integer",
+ "title": "Canceled",
+ "description": "Number of queue items canceled"
+ }
+ },
+ "type": "object",
+ "required": ["canceled"],
+ "title": "CancelByBatchIDsResult",
+ "description": "Result of canceling by list of batch ids"
+ },
+ "CancelByDestinationResult": {
+ "properties": {
+ "canceled": {
+ "type": "integer",
+ "title": "Canceled",
+ "description": "Number of queue items canceled"
+ }
+ },
+ "type": "object",
+ "required": ["canceled"],
+ "title": "CancelByDestinationResult",
+ "description": "Result of canceling by a destination"
+ },
+ "CannyEdgeDetectionInvocation": {
+ "category": "controlnet_preprocessors",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Geneartes an edge map using a cv2's Canny algorithm.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source_api_response": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "The image to process",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "source_url": {
+ "low_threshold": {
+ "default": 100,
+ "description": "The low threshold of the Canny pixel gradient (0-255)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 255,
+ "minimum": 0,
+ "orig_default": 100,
+ "orig_required": false,
+ "title": "Low Threshold",
+ "type": "integer"
+ },
+ "high_threshold": {
+ "default": 200,
+ "description": "The high threshold of the Canny pixel gradient (0-255)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 255,
+ "minimum": 0,
+ "orig_default": 200,
+ "orig_required": false,
+ "title": "High Threshold",
+ "type": "integer"
+ },
+ "type": {
+ "const": "canny_edge_detection",
+ "default": "canny_edge_detection",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["controlnet", "canny"],
+ "title": "Canny Edge Detection",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "CanvasOutputInvocation": {
+ "category": "canvas",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Outputs an image to the canvas staging area.\n\nUse this node in workflows intended for canvas workflow integration.\nConnect the final image of your workflow to this node to send it\nto the canvas staging area when run via 'Run Workflow on Canvas'.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": false,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The image to process",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "cover_image": {
+ "type": {
+ "const": "canvas_output",
+ "default": "canvas_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["canvas", "output", "image"],
+ "title": "Canvas Output",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "CanvasPasteBackInvocation": {
+ "category": "canvas",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Combines two images by using the mask provided. Intended for use on the Unified Canvas.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "config_path": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "default_settings": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "source_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
- ]
+ ],
+ "default": null,
+ "description": "The source image",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "base": {
- "type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Checkpoint_SDXL_Config"
- },
- "ControlNet_Checkpoint_ZImage_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "target_image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The target image",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "description": {
+ "mask": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
+ "default": null,
+ "description": "The mask to use when pasting",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "mask_blur": {
+ "default": 0,
+ "description": "The amount to blur the mask by",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Mask Blur",
+ "type": "integer"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "type": {
+ "const": "canvas_paste_back",
+ "default": "canvas_paste_back",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "combine"],
+ "title": "Canvas Paste Back",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "CanvasV2MaskAndCropInvocation": {
+ "category": "canvas",
+ "class": "invocation",
+ "classification": "deprecated",
+ "description": "Handles Canvas V2 image output masking and cropping",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "source_image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The source image onto which the masked generated image is pasted. If omitted, the masked generated image is returned with transparency.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "cover_image": {
+ "generated_image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "The image to apply the mask to",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "config_path": {
+ "mask": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "The mask to apply",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "mask_blur": {
+ "default": 0,
+ "description": "The amount to blur the mask by",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Mask Blur",
+ "type": "integer"
},
"type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
+ "const": "canvas_v2_mask_and_crop",
+ "default": "canvas_v2_mask_and_crop",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "mask", "id"],
+ "title": "Canvas V2 Mask and Crop",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "CenterPadCropInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Pad or crop an image's sides from the center by specified pixels. Positive values are outside of the image.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "base": {
- "type": "string",
- "const": "z-image",
- "title": "Base",
- "default": "z-image"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "default_settings": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
- ]
+ ],
+ "default": null,
+ "description": "The image to crop",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "left": {
+ "default": 0,
+ "description": "Number of pixels to pad/crop from the left (negative values crop inwards, positive values pad outwards)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Left",
+ "type": "integer"
+ },
+ "right": {
+ "default": 0,
+ "description": "Number of pixels to pad/crop from the right (negative values crop inwards, positive values pad outwards)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Right",
+ "type": "integer"
+ },
+ "top": {
+ "default": 0,
+ "description": "Number of pixels to pad/crop from the top (negative values crop inwards, positive values pad outwards)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Top",
+ "type": "integer"
+ },
+ "bottom": {
+ "default": 0,
+ "description": "Number of pixels to pad/crop from the bottom (negative values crop inwards, positive values pad outwards)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Bottom",
+ "type": "integer"
+ },
+ "type": {
+ "const": "img_pad_crop",
+ "default": "img_pad_crop",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["image", "pad", "crop"],
+ "title": "Center Pad or Crop Image",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base",
- "default_settings"
- ],
- "title": "ControlNet_Checkpoint_ZImage_Config",
- "description": "Model config for Z-Image Control adapter models (Safetensors checkpoint).\n\nZ-Image Control models are standalone adapters containing only the control layers\n(control_layers, control_all_x_embedder, control_noise_refiner) that extend\nthe base Z-Image transformer with spatial conditioning capabilities.\n\nSupports: Canny, HED, Depth, Pose, MLSD.\nRecommended control_context_scale: 0.65-0.80."
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "ControlNet_Diffusers_FLUX_Config": {
+ "Classification": {
+ "description": "The classification of an Invocation.\n- `Stable`: The invocation, including its inputs/outputs and internal logic, is stable. You may build workflows with it, having confidence that they will not break because of a change in this invocation.\n- `Beta`: The invocation is not yet stable, but is planned to be stable in the future. Workflows built around this invocation may break, but we are committed to supporting this invocation long-term.\n- `Prototype`: The invocation is not yet stable and may be removed from the application at any time. Workflows built around this invocation may break, and we are *not* committed to supporting this invocation.\n- `Deprecated`: The invocation is deprecated and may be removed in a future version.\n- `Internal`: The invocation is not intended for use by end-users. It may be changed or removed at any time, but is exposed for users to play with.\n- `Special`: The invocation is a special case and does not fit into any of the other classifications.",
+ "enum": ["stable", "beta", "prototype", "deprecated", "internal", "special"],
+ "title": "Classification",
+ "type": "string"
+ },
+ "ClearResult": {
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
+ "deleted": {
"type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "title": "Deleted",
+ "description": "Number of queue items deleted"
+ }
+ },
+ "type": "object",
+ "required": ["deleted"],
+ "title": "ClearResult",
+ "description": "Result of clearing the session queue"
+ },
+ "ClipVariantType": {
+ "type": "string",
+ "enum": ["large", "gigantic"],
+ "title": "ClipVariantType",
+ "description": "Variant type."
+ },
+ "CogView4ConditioningField": {
+ "description": "A conditioning tensor primitive value",
+ "properties": {
+ "conditioning_name": {
+ "description": "The name of conditioning tensor",
+ "title": "Conditioning Name",
+ "type": "string"
+ }
+ },
+ "required": ["conditioning_name"],
+ "title": "CogView4ConditioningField",
+ "type": "object"
+ },
+ "CogView4ConditioningOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a CogView text conditioning tensor.",
+ "properties": {
+ "conditioning": {
+ "$ref": "#/components/schemas/CogView4ConditioningField",
+ "description": "Conditioning tensor",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "description": {
+ "type": {
+ "const": "cogview4_conditioning_output",
+ "default": "cogview4_conditioning_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "conditioning", "type", "type"],
+ "title": "CogView4ConditioningOutput",
+ "type": "object"
+ },
+ "CogView4DenoiseInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Run the denoising process with a CogView4 model.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "cover_image": {
+ "noise": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
- },
- "type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
+ "default": null,
+ "description": "Noise tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "default_settings": {
+ "denoise_mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ "$ref": "#/components/schemas/DenoiseMaskField"
},
{
"type": "null"
}
- ]
- },
- "base": {
- "type": "string",
- "const": "flux",
- "title": "Base",
- "default": "flux"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Diffusers_FLUX_Config"
- },
- "ControlNet_Diffusers_SD1_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ ],
+ "default": null,
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Denoising Start",
+ "type": "number"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
},
- "description": {
+ "transformer": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "CogView4 model (Transformer) to load",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Transformer"
},
- "source_api_response": {
+ "positive_conditioning": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/CogView4ConditioningField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "source_url": {
+ "negative_conditioning": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/CogView4ConditioningField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "Negative conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "cover_image": {
+ "cfg_scale": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": 3.5,
+ "description": "Classifier-Free Guidance scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 3.5,
+ "orig_required": false,
+ "title": "CFG Scale"
},
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 32,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
},
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 32,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
},
- "type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
+ "steps": {
+ "default": 25,
+ "description": "Number of steps to run",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 25,
+ "orig_required": false,
+ "title": "Steps",
+ "type": "integer"
},
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
},
- "base": {
- "type": "string",
- "const": "sd-1",
- "title": "Base",
- "default": "sd-1"
+ "type": {
+ "const": "cogview4_denoise",
+ "default": "cogview4_denoise",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["image", "cogview4"],
+ "title": "Denoise - CogView4",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Diffusers_SD1_Config"
+ "version": "1.1.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
- "ControlNet_Diffusers_SD2_Config": {
+ "CogView4ImageToLatentsInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Generates latents from an image.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The image to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "cover_image": {
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
"type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
- },
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
- },
- "base": {
- "type": "string",
- "const": "sd-2",
- "title": "Base",
- "default": "sd-2"
+ "const": "cogview4_i2l",
+ "default": "cogview4_i2l",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["image", "latents", "vae", "i2l", "cogview4"],
+ "title": "Image to Latents - CogView4",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Diffusers_SD2_Config"
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
- "ControlNet_Diffusers_SDXL_Config": {
+ "CogView4LatentsToImageInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Generates an image from latents.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "cover_image": {
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
+ "type": {
+ "const": "cogview4_l2i",
+ "default": "cogview4_l2i",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "l2i", "cogview4"],
+ "title": "Latents to Image - CogView4",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "CogView4ModelLoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Loads a CogView4 base model, outputting its submodels.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "type": {
- "type": "string",
- "const": "controlnet",
- "title": "Type",
- "default": "controlnet"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "CogView4 model (Transformer) to load",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_required": true,
+ "ui_model_base": ["cogview4"],
+ "ui_model_type": ["main"]
},
- "base": {
- "type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
+ "type": {
+ "const": "cogview4_model_loader",
+ "default": "cogview4_model_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["model", "type", "id"],
+ "tags": ["model", "cogview4"],
+ "title": "Main Model - CogView4",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "default_settings",
- "base"
- ],
- "title": "ControlNet_Diffusers_SDXL_Config"
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/CogView4ModelLoaderOutput"
+ }
},
- "ControlOutput": {
+ "CogView4ModelLoaderOutput": {
"class": "output",
- "description": "node output for ControlNet info",
+ "description": "CogView4 base model loader output.",
"properties": {
- "control": {
- "$ref": "#/components/schemas/ControlField",
- "description": "ControlNet(s) to apply",
+ "transformer": {
+ "$ref": "#/components/schemas/TransformerField",
+ "description": "Transformer",
+ "field_kind": "output",
+ "title": "Transformer",
+ "ui_hidden": false
+ },
+ "glm_encoder": {
+ "$ref": "#/components/schemas/GlmEncoderField",
+ "description": "GLM (THUDM) tokenizer and text encoder",
"field_kind": "output",
+ "title": "GLM Encoder",
+ "ui_hidden": false
+ },
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
"ui_hidden": false
},
"type": {
- "const": "control_output",
- "default": "control_output",
+ "const": "cogview4_model_loader_output",
+ "default": "cogview4_model_loader_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "control", "type", "type"],
- "title": "ControlOutput",
+ "required": ["output_meta", "transformer", "glm_encoder", "vae", "type", "type"],
+ "title": "CogView4ModelLoaderOutput",
"type": "object"
},
- "CoreMetadataInvocation": {
- "additionalProperties": true,
- "category": "metadata",
+ "CogView4TextEncoderInvocation": {
+ "category": "prompt",
"class": "invocation",
- "classification": "internal",
- "description": "Used internally by Invoke to collect metadata for generations.",
+ "classification": "prototype",
+ "description": "Encodes and preps a prompt for a cogview4 image.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -19408,47 +18148,9 @@
"title": "Use Cache",
"type": "boolean"
},
- "generation_mode": {
+ "prompt": {
"anyOf": [
{
- "enum": [
- "txt2img",
- "img2img",
- "inpaint",
- "outpaint",
- "sdxl_txt2img",
- "sdxl_img2img",
- "sdxl_inpaint",
- "sdxl_outpaint",
- "flux_txt2img",
- "flux_img2img",
- "flux_inpaint",
- "flux_outpaint",
- "flux2_txt2img",
- "flux2_img2img",
- "flux2_inpaint",
- "flux2_outpaint",
- "sd3_txt2img",
- "sd3_img2img",
- "sd3_inpaint",
- "sd3_outpaint",
- "cogview4_txt2img",
- "cogview4_img2img",
- "cogview4_inpaint",
- "cogview4_outpaint",
- "z_image_txt2img",
- "z_image_img2img",
- "z_image_inpaint",
- "z_image_outpaint",
- "qwen_image_txt2img",
- "qwen_image_img2img",
- "qwen_image_inpaint",
- "qwen_image_outpaint",
- "anima_txt2img",
- "anima_img2img",
- "anima_inpaint",
- "anima_outpaint"
- ],
"type": "string"
},
{
@@ -19456,414 +18158,935 @@
}
],
"default": null,
- "description": "The generation mode that output this image",
+ "description": "Text prompt to encode.",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Generation Mode"
+ "orig_required": true,
+ "title": "Prompt",
+ "ui_component": "textarea"
},
- "positive_prompt": {
+ "glm_encoder": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/GlmEncoderField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The positive prompt parameter",
+ "description": "GLM (THUDM) tokenizer and text encoder",
"field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Positive Prompt"
+ "input": "connection",
+ "orig_required": true,
+ "title": "GLM Encoder"
},
- "negative_prompt": {
+ "type": {
+ "const": "cogview4_text_encoder",
+ "default": "cogview4_text_encoder",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "conditioning", "cogview4"],
+ "title": "Prompt - CogView4",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/CogView4ConditioningOutput"
+ }
+ },
+ "CollectInvocation": {
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Collects values into a collection",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "item": {
"anyOf": [
- {
- "type": "string"
- },
+ {},
{
"type": "null"
}
],
"default": null,
- "description": "The negative prompt parameter",
+ "description": "The item to collect (all inputs must be of the same type)",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Negative Prompt"
+ "title": "Collection Item",
+ "ui_type": "CollectionItemField"
},
- "width": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The width parameter",
+ "collection": {
+ "default": [],
+ "description": "An optional collection to append to",
"field_kind": "input",
- "input": "any",
- "orig_default": null,
+ "input": "connection",
+ "items": {},
+ "orig_default": [],
"orig_required": false,
- "title": "Width"
+ "title": "Collection",
+ "type": "array",
+ "ui_type": "CollectionField"
},
- "height": {
+ "type": {
+ "const": "collect",
+ "default": "collect",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "title": "CollectInvocation",
+ "type": "object",
+ "version": "1.1.0",
+ "output": {
+ "$ref": "#/components/schemas/CollectInvocationOutput"
+ }
+ },
+ "CollectInvocationOutput": {
+ "class": "output",
+ "properties": {
+ "collection": {
+ "description": "The collection of input items",
+ "field_kind": "output",
+ "items": {},
+ "title": "Collection",
+ "type": "array",
+ "ui_hidden": false,
+ "ui_type": "CollectionField"
+ },
+ "type": {
+ "const": "collect_output",
+ "default": "collect_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "collection", "type", "type"],
+ "title": "CollectInvocationOutput",
+ "type": "object"
+ },
+ "ColorCollectionOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a collection of colors",
+ "properties": {
+ "collection": {
+ "description": "The output colors",
+ "field_kind": "output",
+ "items": {
+ "$ref": "#/components/schemas/ColorField"
+ },
+ "title": "Collection",
+ "type": "array",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "color_collection_output",
+ "default": "color_collection_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "collection", "type", "type"],
+ "title": "ColorCollectionOutput",
+ "type": "object"
+ },
+ "ColorCorrectInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Matches the color histogram of a base image to a reference image, optionally\nusing a mask to only color-correct certain regions of the base image.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The height parameter",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
"orig_required": false,
- "title": "Height"
+ "ui_hidden": false
},
- "seed": {
+ "metadata": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The seed used for noise generation",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
"orig_required": false,
- "title": "Seed"
+ "ui_hidden": false
},
- "rand_device": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "base_image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The device used for random number generation",
+ "description": "The image to color-correct",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Rand Device"
+ "orig_required": true
},
- "cfg_scale": {
+ "color_reference": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The classifier-free guidance scale parameter",
+ "description": "Reference image for color-correction",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Cfg Scale"
+ "orig_required": true
},
- "cfg_rescale_multiplier": {
+ "mask": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
+ "description": "Optional mask to limit color correction area",
"field_kind": "input",
"input": "any",
"orig_default": null,
- "orig_required": false,
- "title": "Cfg Rescale Multiplier"
+ "orig_required": false
},
- "steps": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The number of steps used for inference",
+ "colorspace": {
+ "default": "RGB",
+ "description": "Colorspace in which to apply histogram matching",
+ "enum": ["RGB", "YCbCr", "YCbCr-Chroma", "YCbCr-Luma"],
"field_kind": "input",
"input": "any",
- "orig_default": null,
+ "orig_default": "RGB",
"orig_required": false,
- "title": "Steps"
+ "title": "Color Space",
+ "type": "string"
},
- "scheduler": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The scheduler used for inference",
+ "type": {
+ "const": "color_correct",
+ "default": "color_correct",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "color"],
+ "title": "Color Correct",
+ "type": "object",
+ "version": "2.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "ColorField": {
+ "description": "A color primitive field",
+ "properties": {
+ "r": {
+ "description": "The red component",
+ "maximum": 255,
+ "minimum": 0,
+ "title": "R",
+ "type": "integer"
+ },
+ "g": {
+ "description": "The green component",
+ "maximum": 255,
+ "minimum": 0,
+ "title": "G",
+ "type": "integer"
+ },
+ "b": {
+ "description": "The blue component",
+ "maximum": 255,
+ "minimum": 0,
+ "title": "B",
+ "type": "integer"
+ },
+ "a": {
+ "description": "The alpha component",
+ "maximum": 255,
+ "minimum": 0,
+ "title": "A",
+ "type": "integer"
+ }
+ },
+ "required": ["r", "g", "b", "a"],
+ "title": "ColorField",
+ "type": "object"
+ },
+ "ColorInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "A color primitive value",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "color": {
+ "$ref": "#/components/schemas/ColorField",
+ "default": {
+ "r": 0,
+ "g": 0,
+ "b": 0,
+ "a": 255
+ },
+ "description": "The color value",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Scheduler"
+ "orig_default": {
+ "a": 255,
+ "b": 0,
+ "g": 0,
+ "r": 0
+ },
+ "orig_required": false
},
- "seamless_x": {
+ "type": {
+ "const": "color",
+ "default": "color",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["primitives", "color"],
+ "title": "Color Primitive",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/ColorOutput"
+ }
+ },
+ "ColorMapInvocation": {
+ "category": "controlnet_preprocessors",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generates a color map from the provided image.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "boolean"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Whether seamless tiling was used on the X axis",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
"orig_required": false,
- "title": "Seamless X"
+ "ui_hidden": false
},
- "seamless_y": {
+ "metadata": {
"anyOf": [
{
- "type": "boolean"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Whether seamless tiling was used on the Y axis",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
"orig_required": false,
- "title": "Seamless Y"
+ "ui_hidden": false
},
- "clip_skip": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The number of skipped CLIP layers",
+ "description": "The image to process",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Clip Skip"
+ "orig_required": true
},
- "model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The main model used for inference",
+ "tile_size": {
+ "default": 64,
+ "description": "Tile size",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false
+ "minimum": 1,
+ "orig_default": 64,
+ "orig_required": false,
+ "title": "Tile Size",
+ "type": "integer"
},
- "controlnets": {
- "anyOf": [
- {
- "items": {
- "$ref": "#/components/schemas/ControlNetMetadataField"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The ControlNets used for inference",
+ "type": {
+ "const": "color_map",
+ "default": "color_map",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["controlnet"],
+ "title": "Color Map",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "ColorOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a single color",
+ "properties": {
+ "color": {
+ "$ref": "#/components/schemas/ColorField",
+ "description": "The output color",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "color_output",
+ "default": "color_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "color", "type", "type"],
+ "title": "ColorOutput",
+ "type": "object"
+ },
+ "CompelInvocation": {
+ "category": "prompt",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Parse prompt using compel package to conditioning.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "prompt": {
+ "default": "",
+ "description": "Prompt to be parsed by Compel to create a conditioning tensor",
"field_kind": "input",
"input": "any",
- "orig_default": null,
+ "orig_default": "",
"orig_required": false,
- "title": "Controlnets"
+ "title": "Prompt",
+ "type": "string",
+ "ui_component": "textarea"
},
- "ipAdapters": {
+ "clip": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/IPAdapterMetadataField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The IP Adapters used for inference",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Ipadapters"
+ "orig_required": true,
+ "title": "CLIP"
},
- "t2iAdapters": {
+ "mask": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/T2IAdapterMetadataField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/TensorField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The IP Adapters used for inference",
+ "description": "A mask defining the region that this conditioning prompt applies to.",
"field_kind": "input",
"input": "any",
"orig_default": null,
+ "orig_required": false
+ },
+ "type": {
+ "const": "compel",
+ "default": "compel",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "compel"],
+ "title": "Prompt - SD1.5",
+ "type": "object",
+ "version": "1.2.1",
+ "output": {
+ "$ref": "#/components/schemas/ConditioningOutput"
+ }
+ },
+ "ConditioningCollectionInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "A collection of conditioning tensor primitive values",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "collection": {
+ "default": [],
+ "description": "The collection of conditioning tensors",
+ "field_kind": "input",
+ "input": "any",
+ "items": {
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ "orig_default": [],
"orig_required": false,
- "title": "T2Iadapters"
+ "title": "Collection",
+ "type": "array"
},
- "loras": {
+ "type": {
+ "const": "conditioning_collection",
+ "default": "conditioning_collection",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["primitives", "conditioning", "collection"],
+ "title": "Conditioning Collection Primitive",
+ "type": "object",
+ "version": "1.0.2",
+ "output": {
+ "$ref": "#/components/schemas/ConditioningCollectionOutput"
+ }
+ },
+ "ConditioningCollectionOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a collection of conditioning tensors",
+ "properties": {
+ "collection": {
+ "description": "The output conditioning tensors",
+ "field_kind": "output",
+ "items": {
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ "title": "Collection",
+ "type": "array",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "conditioning_collection_output",
+ "default": "conditioning_collection_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "collection", "type", "type"],
+ "title": "ConditioningCollectionOutput",
+ "type": "object"
+ },
+ "ConditioningField": {
+ "description": "A conditioning tensor primitive value",
+ "properties": {
+ "conditioning_name": {
+ "description": "The name of conditioning tensor",
+ "title": "Conditioning Name",
+ "type": "string"
+ },
+ "mask": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/LoRAMetadataField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/TensorField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The LoRAs used for inference",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Loras"
+ "description": "The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True."
+ }
+ },
+ "required": ["conditioning_name"],
+ "title": "ConditioningField",
+ "type": "object"
+ },
+ "ConditioningInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "A conditioning tensor primitive value",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "strength": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "conditioning": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/ConditioningField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The strength used for latents-to-latents",
+ "description": "Conditioning tensor",
"field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Strength"
+ "input": "connection",
+ "orig_required": true
},
- "init_image": {
+ "type": {
+ "const": "conditioning",
+ "default": "conditioning",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["primitives", "conditioning"],
+ "title": "Conditioning Primitive",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/ConditioningOutput"
+ }
+ },
+ "ConditioningOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a single conditioning tensor",
+ "properties": {
+ "conditioning": {
+ "$ref": "#/components/schemas/ConditioningField",
+ "description": "Conditioning tensor",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "conditioning_output",
+ "default": "conditioning_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "conditioning", "type", "type"],
+ "title": "ConditioningOutput",
+ "type": "object"
+ },
+ "ContentShuffleInvocation": {
+ "category": "controlnet_preprocessors",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Shuffles the image, similar to a 'liquify' filter.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The name of the initial image",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
"orig_required": false,
- "title": "Init Image"
+ "ui_hidden": false
},
- "vae": {
+ "metadata": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The VAE used for decoding, if the main model's default was not used",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "qwen3_encoder": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The Qwen3 text encoder model used for Z-Image inference",
+ "description": "The image to process",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false
+ "orig_required": true
},
- "hrf_enabled": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Whether or not high resolution fix was enabled.",
+ "scale_factor": {
+ "default": 256,
+ "description": "The scale factor used for the shuffle",
"field_kind": "input",
"input": "any",
- "orig_default": null,
+ "minimum": 0,
+ "orig_default": 256,
"orig_required": false,
- "title": "Hrf Enabled"
+ "title": "Scale Factor",
+ "type": "integer"
},
- "hrf_method": {
+ "type": {
+ "const": "content_shuffle",
+ "default": "content_shuffle",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["controlnet", "normal"],
+ "title": "Content Shuffle",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "ControlAdapterDefaultSettings": {
+ "properties": {
+ "preprocessor": {
"anyOf": [
{
"type": "string"
@@ -19872,49 +19095,135 @@
"type": "null"
}
],
- "default": null,
- "description": "The high resolution fix upscale method.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Hrf Method"
+ "title": "Preprocessor"
},
- "hrf_strength": {
+ "fp8_storage": {
"anyOf": [
{
- "type": "number"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The high resolution fix img2img strength used in the upscale pass.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Hrf Strength"
+ "title": "Fp8 Storage",
+ "description": "Store weights in FP8 to reduce VRAM usage (~50% savings). Weights are cast to compute dtype during inference."
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "required": ["preprocessor"],
+ "title": "ControlAdapterDefaultSettings"
+ },
+ "ControlField": {
+ "properties": {
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The control image"
},
- "positive_style_prompt": {
+ "control_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "The ControlNet model to use"
+ },
+ "control_weight": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "default": null,
- "description": "The positive style prompt parameter",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Positive Style Prompt"
+ "default": 1,
+ "description": "The weight given to the ControlNet",
+ "title": "Control Weight"
},
- "negative_style_prompt": {
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the ControlNet is first applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "Begin Step Percent",
+ "type": "number"
+ },
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the ControlNet is last applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "End Step Percent",
+ "type": "number"
+ },
+ "control_mode": {
+ "default": "balanced",
+ "description": "The control mode to use",
+ "enum": ["balanced", "more_prompt", "more_control", "unbalanced"],
+ "title": "Control Mode",
+ "type": "string"
+ },
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode to use",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "title": "Resize Mode",
+ "type": "string"
+ }
+ },
+ "required": ["image", "control_model"],
+ "title": "ControlField",
+ "type": "object"
+ },
+ "ControlLoRAField": {
+ "properties": {
+ "lora": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load lora model"
+ },
+ "weight": {
+ "description": "Weight to apply to lora model",
+ "title": "Weight",
+ "type": "number"
+ },
+ "img": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "Image to use in structural conditioning"
+ }
+ },
+ "required": ["lora", "weight", "img"],
+ "title": "ControlLoRAField",
+ "type": "object"
+ },
+ "ControlLoRA_LyCORIS_FLUX_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
"type": "string"
@@ -19923,65 +19232,31 @@
"type": "null"
}
],
- "default": null,
- "description": "The negative style prompt parameter",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Negative Style Prompt"
+ "title": "Description",
+ "description": "Model description"
},
- "refiner_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The SDXL Refiner model used",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "refiner_cfg_scale": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The classifier-free guidance scale parameter used for the refiner",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Refiner Cfg Scale"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "refiner_steps": {
+ "source_api_response": {
"anyOf": [
{
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The number of steps used for the refiner",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Refiner Steps"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "refiner_scheduler": {
+ "source_url": {
"anyOf": [
{
"type": "string"
@@ -19990,87 +19265,92 @@
"type": "null"
}
],
- "default": null,
- "description": "The scheduler used for the refiner",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Refiner Scheduler"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "refiner_positive_aesthetic_score": {
+ "cover_image": {
"anyOf": [
{
- "type": "number"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The aesthetic score used for the refiner",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Refiner Positive Aesthetic Score"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "refiner_negative_aesthetic_score": {
+ "default_settings": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "The aesthetic score used for the refiner",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Refiner Negative Aesthetic Score"
+ ]
},
- "refiner_start": {
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
+ },
+ "type": {
+ "type": "string",
+ "const": "control_lora",
+ "title": "Type",
+ "default": "control_lora"
+ },
+ "format": {
+ "type": "string",
+ "const": "lycoris",
+ "title": "Format",
+ "default": "lycoris"
+ },
+ "trigger_phrases": {
"anyOf": [
{
- "type": "number"
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
},
{
"type": "null"
}
],
- "default": null,
- "description": "The start value used for refiner denoising",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Refiner Start"
- },
- "type": {
- "const": "core_metadata",
- "default": "core_metadata",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Trigger Phrases"
}
},
- "required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Core Metadata",
"type": "object",
- "version": "2.1.0",
- "output": {
- "$ref": "#/components/schemas/MetadataOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "default_settings",
+ "base",
+ "type",
+ "format",
+ "trigger_phrases"
+ ],
+ "title": "ControlLoRA_LyCORIS_FLUX_Config",
+ "description": "Model config for Control LoRA models."
},
- "CreateDenoiseMaskInvocation": {
- "category": "mask",
+ "ControlNetInvocation": {
+ "category": "conditioning",
"class": "invocation",
"classification": "stable",
- "description": "Creates mask for denoising model run.",
+ "description": "Collects ControlNet info to pass to other nodes",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -20097,126 +19377,130 @@
"title": "Use Cache",
"type": "boolean"
},
- "vae": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "VAE",
+ "description": "The control image",
"field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "ui_order": 0
+ "input": "any",
+ "orig_required": true
},
- "image": {
+ "control_model": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Image which will be masked",
+ "description": "ControlNet model to load",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "ui_order": 1
+ "orig_required": true,
+ "ui_model_base": ["sd-1", "sd-2", "sdxl"],
+ "ui_model_type": ["controlnet"]
},
- "mask": {
+ "control_weight": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "default": null,
- "description": "The mask to use when pasting",
+ "default": 1.0,
+ "description": "The weight given to the ControlNet",
"field_kind": "input",
+ "ge": -1,
"input": "any",
- "orig_required": true,
- "ui_order": 2
+ "le": 2,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Control Weight"
},
- "tiled": {
- "default": false,
- "description": "Processing using overlapping tiles (reduce memory consumption)",
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the ControlNet is first applied (% of total steps)",
"field_kind": "input",
"input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Tiled",
- "type": "boolean",
- "ui_order": 3
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Begin Step Percent",
+ "type": "number"
},
- "fp32": {
- "default": false,
- "description": "Whether or not to use full float32 precision",
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the ControlNet is last applied (% of total steps)",
"field_kind": "input",
"input": "any",
- "orig_default": false,
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1,
"orig_required": false,
- "title": "Fp32",
- "type": "boolean",
- "ui_order": 4
+ "title": "End Step Percent",
+ "type": "number"
+ },
+ "control_mode": {
+ "default": "balanced",
+ "description": "The control mode used",
+ "enum": ["balanced", "more_prompt", "more_control", "unbalanced"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "balanced",
+ "orig_required": false,
+ "title": "Control Mode",
+ "type": "string"
+ },
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode used",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "just_resize",
+ "orig_required": false,
+ "title": "Resize Mode",
+ "type": "string"
},
"type": {
- "const": "create_denoise_mask",
- "default": "create_denoise_mask",
+ "const": "controlnet",
+ "default": "controlnet",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["mask", "denoise"],
- "title": "Create Denoise Mask",
+ "tags": ["controlnet"],
+ "title": "ControlNet - SD1.5, SD2, SDXL",
"type": "object",
- "version": "1.0.2",
+ "version": "1.1.3",
"output": {
- "$ref": "#/components/schemas/DenoiseMaskOutput"
+ "$ref": "#/components/schemas/ControlOutput"
}
},
- "CreateGradientMaskInvocation": {
- "category": "mask",
- "class": "invocation",
- "classification": "stable",
- "description": "Creates mask for denoising.",
- "node_pack": "invokeai",
+ "ControlNetMetadataField": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The control image"
},
- "mask": {
+ "processed_image": {
"anyOf": [
{
"$ref": "#/components/schemas/ImageField"
@@ -20226,1859 +19510,1227 @@
}
],
"default": null,
- "description": "Image which will be masked",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_order": 1
- },
- "edge_radius": {
- "default": 16,
- "description": "How far to expand the edges of the mask",
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 16,
- "orig_required": false,
- "title": "Edge Radius",
- "type": "integer",
- "ui_order": 2
- },
- "coherence_mode": {
- "default": "Gaussian Blur",
- "enum": ["Gaussian Blur", "Box Blur", "Staged"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "Gaussian Blur",
- "orig_required": false,
- "title": "Coherence Mode",
- "type": "string",
- "ui_order": 3
- },
- "minimum_denoise": {
- "default": 0.0,
- "description": "Minimum denoise level for the coherence region",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Minimum Denoise",
- "type": "number",
- "ui_order": 4
+ "description": "The control image, after processing."
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "[OPTIONAL] Image",
- "ui_order": 6
+ "control_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "The ControlNet model to use"
},
- "unet": {
+ "control_weight": {
"anyOf": [
{
- "$ref": "#/components/schemas/UNetField"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "default": null,
- "description": "OPTIONAL: If the Unet is a specialized Inpainting model, masked_latents will be generated from the image with the VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "[OPTIONAL] UNet",
- "ui_order": 5
+ "default": 1,
+ "description": "The weight given to the ControlNet",
+ "title": "Control Weight"
},
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "[OPTIONAL] VAE",
- "ui_order": 7
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the ControlNet is first applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "Begin Step Percent",
+ "type": "number"
},
- "tiled": {
- "default": false,
- "description": "Processing using overlapping tiles (reduce memory consumption)",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Tiled",
- "type": "boolean",
- "ui_order": 8
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the ControlNet is last applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "End Step Percent",
+ "type": "number"
},
- "fp32": {
- "default": false,
- "description": "Whether or not to use full float32 precision",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Fp32",
- "type": "boolean",
- "ui_order": 9
+ "control_mode": {
+ "default": "balanced",
+ "description": "The control mode to use",
+ "enum": ["balanced", "more_prompt", "more_control", "unbalanced"],
+ "title": "Control Mode",
+ "type": "string"
},
- "type": {
- "const": "create_gradient_mask",
- "default": "create_gradient_mask",
- "field_kind": "node_attribute",
- "title": "type",
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode to use",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "title": "Resize Mode",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["mask", "denoise"],
- "title": "Create Gradient Mask",
- "type": "object",
- "version": "1.3.0",
- "output": {
- "$ref": "#/components/schemas/GradientMaskOutput"
- }
+ "required": ["image", "control_model"],
+ "title": "ControlNetMetadataField",
+ "type": "object"
},
- "CropImageToBoundingBoxInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Crop an image to the given bounding box. If the bounding box is omitted, the image is cropped to the non-transparent pixels.",
- "node_pack": "invokeai",
+ "ControlNetRecallParameter": {
"properties": {
- "board": {
+ "model_name": {
+ "type": "string",
+ "title": "Model Name",
+ "description": "The name of the ControlNet/T2I Adapter/Control LoRA model"
+ },
+ "image_name": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Image Name",
+ "description": "The filename of the control image in outputs/images"
},
- "metadata": {
+ "weight": {
+ "type": "number",
+ "maximum": 2.0,
+ "minimum": -1.0,
+ "title": "Weight",
+ "description": "The weight for the control adapter",
+ "default": 1.0
+ },
+ "begin_step_percent": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "number",
+ "maximum": 1.0,
+ "minimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Begin Step Percent",
+ "description": "When the control adapter is first applied (% of total steps)"
},
- "image": {
+ "end_step_percent": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "number",
+ "maximum": 1.0,
+ "minimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to crop",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "End Step Percent",
+ "description": "When the control adapter is last applied (% of total steps)"
},
- "bounding_box": {
+ "control_mode": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoundingBoxField"
+ "type": "string",
+ "enum": ["balanced", "more_prompt", "more_control"]
},
{
"type": "null"
}
],
- "default": null,
- "description": "The bounding box to crop the image to",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
- },
- "type": {
- "const": "crop_image_to_bounding_box",
- "default": "crop_image_to_bounding_box",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Control Mode",
+ "description": "The control mode (ControlNet only)"
}
},
- "required": ["type", "id"],
- "tags": ["image", "crop"],
- "title": "Crop Image to Bounding Box",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": ["model_name"],
+ "title": "ControlNetRecallParameter",
+ "description": "ControlNet configuration for recall"
},
- "CropLatentsCoreInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "stable",
- "description": "Crops a latent-space tensor to a box specified in image-space. The box dimensions and coordinates must be\ndivisible by the latent scale factor of 8.",
- "node_pack": "invokeai",
+ "ControlNet_Checkpoint_Anima_Config": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "latents": {
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Description",
+ "description": "Model description"
},
- "x": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "minimum": 0,
- "multipleOf": 8,
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The left x coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "X"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "y": {
+ "source_url": {
"anyOf": [
{
- "minimum": 0,
- "multipleOf": 8,
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The top y coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Y"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "width": {
+ "cover_image": {
"anyOf": [
{
- "minimum": 1,
- "multipleOf": 8,
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The width (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Width"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "height": {
+ "config_path": {
"anyOf": [
{
- "minimum": 1,
- "multipleOf": 8,
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The height (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Height"
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
"type": {
- "const": "crop_latents",
- "default": "crop_latents",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["latents", "crop"],
- "title": "Crop Latents",
- "type": "object",
- "version": "1.0.2",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
- },
- "CvInpaintInvocation": {
- "category": "inpaint",
- "class": "invocation",
- "classification": "stable",
- "description": "Simple inpaint using opencv.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ },
+ "base": {
+ "type": "string",
+ "const": "anima",
+ "title": "Base",
+ "default": "anima"
+ },
+ "default_settings": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "cond_in_channels": {
+ "anyOf": [
+ {
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Cond In Channels",
+ "description": "Number of conditioning image channels (3 = RGB control image, 4 = RGB + inpaint mask). None for models installed before this field was recorded."
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "type",
+ "format",
+ "base",
+ "default_settings",
+ "cond_in_channels"
+ ],
+ "title": "ControlNet_Checkpoint_Anima_Config",
+ "description": "Model config for Anima ControlNet-LLLite adapter models (Safetensors checkpoint).\n\nAnima LLLite adapters are standalone adapters consisting of a shared conditioning trunk\n(lllite_conditioning1) that encodes a conditioning image, plus tiny per-Linear modules\n(lllite_dit_blocks_*) that perturb the inputs of target Linears in the Anima DiT."
+ },
+ "ControlNet_Checkpoint_FLUX_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "metadata": {
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "image": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to inpaint",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "mask": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The mask to use when inpainting",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
"type": {
- "const": "cv_inpaint",
- "default": "cv_inpaint",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
}
},
- "required": ["type", "id"],
- "tags": ["opencv", "inpaint"],
- "title": "OpenCV Inpaint",
"type": "object",
- "version": "1.3.1",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "type",
+ "format",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Checkpoint_FLUX_Config"
},
- "DWOpenposeDetectionInvocation": {
- "category": "controlnet_preprocessors",
- "class": "invocation",
- "classification": "stable",
- "description": "Generates an openpose pose from an image using DWPose",
- "node_pack": "invokeai",
+ "ControlNet_Checkpoint_SD1_Config": {
"properties": {
- "board": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
},
- "metadata": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "image": {
+ "config_path": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to process",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
- "draw_body": {
- "default": true,
- "field_kind": "input",
- "input": "any",
- "orig_default": true,
- "orig_required": false,
- "title": "Draw Body",
- "type": "boolean"
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
},
- "draw_face": {
- "default": false,
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Draw Face",
- "type": "boolean"
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
},
- "draw_hands": {
- "default": false,
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Draw Hands",
- "type": "boolean"
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ]
},
- "type": {
- "const": "dw_openpose_detection",
- "default": "dw_openpose_detection",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "base": {
+ "type": "string",
+ "const": "sd-1",
+ "title": "Base",
+ "default": "sd-1"
}
},
- "required": ["type", "id"],
- "tags": ["controlnet", "dwpose", "openpose"],
- "title": "DW Openpose Detection",
"type": "object",
- "version": "1.1.1",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "type",
+ "format",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Checkpoint_SD1_Config"
},
- "DecodeInvisibleWatermarkInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Decode an invisible watermark from an image.",
- "node_pack": "invokeai",
+ "ControlNet_Checkpoint_SD2_Config": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "image": {
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to decode the watermark from",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Description",
+ "description": "Model description"
},
- "length": {
- "default": 8,
- "description": "The expected watermark length in bytes",
- "field_kind": "input",
- "input": "any",
- "orig_default": 8,
- "orig_required": false,
- "title": "Length",
- "type": "integer"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "type": {
- "const": "decode_watermark",
- "default": "decode_watermark",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "watermark"],
- "title": "Decode Invisible Watermark",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/StringOutput"
- }
- },
- "DeleteAllExceptCurrentResult": {
- "properties": {
- "deleted": {
- "type": "integer",
- "title": "Deleted",
- "description": "Number of queue items deleted"
- }
- },
- "type": "object",
- "required": ["deleted"],
- "title": "DeleteAllExceptCurrentResult",
- "description": "Result of deleting all except current"
- },
- "DeleteBoardResult": {
- "properties": {
- "board_id": {
- "type": "string",
- "title": "Board Id",
- "description": "The id of the board that was deleted."
- },
- "deleted_board_images": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Deleted Board Images",
- "description": "The image names of the board-images relationships that were deleted."
- },
- "deleted_images": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Deleted Images",
- "description": "The names of the images that were deleted."
- }
- },
- "type": "object",
- "required": ["board_id", "deleted_board_images", "deleted_images"],
- "title": "DeleteBoardResult"
- },
- "DeleteByDestinationResult": {
- "properties": {
- "deleted": {
- "type": "integer",
- "title": "Deleted",
- "description": "Number of queue items deleted"
- }
- },
- "type": "object",
- "required": ["deleted"],
- "title": "DeleteByDestinationResult",
- "description": "Result of deleting by a destination"
- },
- "DeleteImagesResult": {
- "properties": {
- "affected_boards": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Affected Boards",
- "description": "The ids of boards affected by the delete operation"
- },
- "deleted_images": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Deleted Images",
- "description": "The names of the images that were deleted"
- }
- },
- "type": "object",
- "required": ["affected_boards", "deleted_images"],
- "title": "DeleteImagesResult"
- },
- "DeleteOrphanedModelsRequest": {
- "properties": {
- "paths": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Paths",
- "description": "List of relative paths to delete"
- }
- },
- "type": "object",
- "required": ["paths"],
- "title": "DeleteOrphanedModelsRequest",
- "description": "Request to delete specific orphaned model directories."
- },
- "DeleteOrphanedModelsResponse": {
- "properties": {
- "deleted": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Deleted",
- "description": "Paths that were successfully deleted"
- },
- "errors": {
- "additionalProperties": {
- "type": "string"
- },
- "type": "object",
- "title": "Errors",
- "description": "Paths that had errors, with error messages"
- }
- },
- "type": "object",
- "required": ["deleted", "errors"],
- "title": "DeleteOrphanedModelsResponse",
- "description": "Response from deleting orphaned models."
- },
- "DenoiseLatentsInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "stable",
- "description": "Denoises noisy latents to decodable images",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "positive_conditioning": {
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/ConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/ConditioningField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Positive Conditioning",
- "ui_order": 0
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "negative_conditioning": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ConditioningField"
+ "type": "string"
},
{
- "items": {
- "$ref": "#/components/schemas/ConditioningField"
- },
- "type": "array"
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Negative conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Negative Conditioning",
- "ui_order": 1
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "noise": {
+ "config_path": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Noise tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "ui_order": 3
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
- "steps": {
- "default": 10,
- "description": "Number of steps to run",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 10,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
},
- "cfg_scale": {
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ },
+ "default_settings": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
- ],
- "default": 7.5,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 7.5,
- "orig_required": false,
- "title": "CFG Scale"
+ ]
},
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
+ "base": {
+ "type": "string",
+ "const": "sd-2",
+ "title": "Base",
+ "default": "sd-2"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "type",
+ "format",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Checkpoint_SD2_Config"
+ },
+ "ControlNet_Checkpoint_SDXL_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "scheduler": {
- "default": "euler",
- "description": "Scheduler to use during inference",
- "enum": [
- "ddim",
- "ddpm",
- "deis",
- "deis_k",
- "lms",
- "lms_k",
- "pndm",
- "heun",
- "heun_k",
- "euler",
- "euler_k",
- "euler_a",
- "kdpm_2",
- "kdpm_2_k",
- "kdpm_2_a",
- "kdpm_2_a_k",
- "dpmpp_2s",
- "dpmpp_2s_k",
- "dpmpp_2m",
- "dpmpp_2m_k",
- "dpmpp_2m_sde",
- "dpmpp_2m_sde_k",
- "dpmpp_3m",
- "dpmpp_3m_k",
- "dpmpp_sde",
- "dpmpp_sde_k",
- "er_sde",
- "unipc",
- "unipc_k",
- "lcm",
- "tcd"
- ],
- "field_kind": "input",
- "input": "any",
- "orig_default": "euler",
- "orig_required": false,
- "title": "Scheduler",
+ "path": {
"type": "string",
- "ui_type": "SchedulerField"
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "unet": {
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/UNetField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "UNet",
- "ui_order": 2
+ "title": "Description",
+ "description": "Model description"
},
- "control": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/ControlField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Control",
- "ui_order": 5
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "ip_adapter": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/IPAdapterField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/IPAdapterField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "IP-Adapter to apply",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "IP-Adapter",
- "ui_order": 6
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "t2i_adapter": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/T2IAdapterField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/T2IAdapterField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "T2I-Adapter(s) to apply",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "T2I-Adapter",
- "ui_order": 7
- },
- "cfg_rescale_multiplier": {
- "default": 0,
- "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
- "exclusiveMaximum": 1,
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "CFG Rescale Multiplier",
- "type": "number"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "latents": {
+ "config_path": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "ui_order": 4
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
- "denoise_mask": {
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ },
+ "default_settings": {
"anyOf": [
{
- "$ref": "#/components/schemas/DenoiseMaskField"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "ui_order": 8
+ ]
},
- "type": {
- "const": "denoise_latents",
- "default": "denoise_latents",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
}
},
- "required": ["type", "id"],
- "tags": ["latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
- "title": "Denoise - SD1.5, SDXL",
"type": "object",
- "version": "1.5.4",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "type",
+ "format",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Checkpoint_SDXL_Config"
},
- "DenoiseLatentsMetaInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "stable",
- "node_pack": "invokeai",
+ "ControlNet_Checkpoint_ZImage_Config": {
"properties": {
- "metadata": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "title": "Description",
+ "description": "Model description"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "positive_conditioning": {
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/ConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/ConditioningField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Positive Conditioning",
- "ui_order": 0
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "negative_conditioning": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/ConditioningField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Negative conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Negative Conditioning",
- "ui_order": 1
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "noise": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Noise tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "ui_order": 3
- },
- "steps": {
- "default": 10,
- "description": "Number of steps to run",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 10,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "cfg_scale": {
+ "config_path": {
"anyOf": [
{
- "type": "number"
+ "type": "string"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
],
- "default": 7.5,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 7.5,
- "orig_required": false,
- "title": "CFG Scale"
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
},
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
},
- "scheduler": {
- "default": "euler",
- "description": "Scheduler to use during inference",
- "enum": [
- "ddim",
- "ddpm",
- "deis",
- "deis_k",
- "lms",
- "lms_k",
- "pndm",
- "heun",
- "heun_k",
- "euler",
- "euler_k",
- "euler_a",
- "kdpm_2",
- "kdpm_2_k",
- "kdpm_2_a",
- "kdpm_2_a_k",
- "dpmpp_2s",
- "dpmpp_2s_k",
- "dpmpp_2m",
- "dpmpp_2m_k",
- "dpmpp_2m_sde",
- "dpmpp_2m_sde_k",
- "dpmpp_3m",
- "dpmpp_3m_k",
- "dpmpp_sde",
- "dpmpp_sde_k",
- "er_sde",
- "unipc",
- "unipc_k",
- "lcm",
- "tcd"
- ],
- "field_kind": "input",
- "input": "any",
- "orig_default": "euler",
- "orig_required": false,
- "title": "Scheduler",
+ "base": {
"type": "string",
- "ui_type": "SchedulerField"
+ "const": "z-image",
+ "title": "Base",
+ "default": "z-image"
},
- "unet": {
+ "default_settings": {
"anyOf": [
{
- "$ref": "#/components/schemas/UNetField"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "UNet",
- "ui_order": 2
+ ]
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "type",
+ "format",
+ "base",
+ "default_settings"
+ ],
+ "title": "ControlNet_Checkpoint_ZImage_Config",
+ "description": "Model config for Z-Image Control adapter models (Safetensors checkpoint).\n\nZ-Image Control models are standalone adapters containing only the control layers\n(control_layers, control_all_x_embedder, control_noise_refiner) that extend\nthe base Z-Image transformer with spatial conditioning capabilities.\n\nSupports: Canny, HED, Depth, Pose, MLSD.\nRecommended control_context_scale: 0.65-0.80."
+ },
+ "ControlNet_Diffusers_FLUX_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "control": {
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/ControlField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Control",
- "ui_order": 5
+ "title": "Description",
+ "description": "Model description"
},
- "ip_adapter": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/IPAdapterField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/IPAdapterField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "IP-Adapter to apply",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "IP-Adapter",
- "ui_order": 6
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "t2i_adapter": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/T2IAdapterField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/T2IAdapterField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "T2I-Adapter(s) to apply",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "T2I-Adapter",
- "ui_order": 7
- },
- "cfg_rescale_multiplier": {
- "default": 0,
- "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
- "exclusiveMaximum": 1,
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "CFG Rescale Multiplier",
- "type": "number"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "latents": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "ui_order": 4
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "denoise_mask": {
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
+ },
+ "default_settings": {
"anyOf": [
{
- "$ref": "#/components/schemas/DenoiseMaskField"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "ui_order": 8
+ ]
},
- "type": {
- "const": "denoise_latents_meta",
- "default": "denoise_latents_meta",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
}
},
- "required": ["type", "id"],
- "tags": ["latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
- "title": "Denoise - SD1.5, SDXL + Metadata",
"type": "object",
- "version": "1.1.1",
- "output": {
- "$ref": "#/components/schemas/LatentsMetaOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Diffusers_FLUX_Config"
},
- "DenoiseMaskField": {
- "description": "An inpaint mask field",
+ "ControlNet_Diffusers_SD1_Config": {
"properties": {
- "mask_name": {
- "description": "The name of the mask image",
- "title": "Mask Name",
- "type": "string"
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "masked_latents_name": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The name of the masked image latents",
- "title": "Masked Latents Name"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "gradient": {
- "default": false,
- "description": "Used for gradient inpainting",
- "title": "Gradient",
- "type": "boolean"
- }
- },
- "required": ["mask_name"],
- "title": "DenoiseMaskField",
- "type": "object"
- },
- "DenoiseMaskOutput": {
- "class": "output",
- "description": "Base class for nodes that output a single image",
- "properties": {
- "denoise_mask": {
- "$ref": "#/components/schemas/DenoiseMaskField",
- "description": "Mask for denoise model run",
- "field_kind": "output",
- "ui_hidden": false
- },
- "type": {
- "const": "denoise_mask_output",
- "default": "denoise_mask_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "denoise_mask", "type", "type"],
- "title": "DenoiseMaskOutput",
- "type": "object"
- },
- "DepthAnythingDepthEstimationInvocation": {
- "category": "controlnet_preprocessors",
- "class": "invocation",
- "classification": "stable",
- "description": "Generates a depth map using a Depth Anything model.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
},
- "image": {
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to process",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "model_size": {
- "default": "small_v2",
- "description": "The size of the depth model to use",
- "enum": ["large", "base", "small", "small_v2"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "small_v2",
- "orig_required": false,
- "title": "Model Size",
- "type": "string"
- },
- "type": {
- "const": "depth_anything_depth_estimation",
- "default": "depth_anything_depth_estimation",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["controlnet", "depth", "depth anything"],
- "title": "Depth Anything Depth Estimation",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "DivideInvocation": {
- "category": "math",
- "class": "invocation",
- "classification": "stable",
- "description": "Divides two numbers",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "a": {
- "default": 0,
- "description": "The first number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "A",
- "type": "integer"
- },
- "b": {
- "default": 0,
- "description": "The second number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "B",
- "type": "integer"
- },
- "type": {
- "const": "div",
- "default": "div",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["math", "divide"],
- "title": "Divide Integers",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/IntegerOutput"
- }
- },
- "DownloadCancelledEvent": {
- "description": "Event model for download_cancelled",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "source": {
- "description": "The source of the download",
- "title": "Source",
- "type": "string"
- }
- },
- "required": ["timestamp", "source"],
- "title": "DownloadCancelledEvent",
- "type": "object"
- },
- "DownloadCompleteEvent": {
- "description": "Event model for download_complete",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "source": {
- "description": "The source of the download",
- "title": "Source",
- "type": "string"
- },
- "download_path": {
- "description": "The local path where the download is saved",
- "title": "Download Path",
- "type": "string"
- },
- "total_bytes": {
- "description": "The total number of bytes downloaded",
- "title": "Total Bytes",
- "type": "integer"
- }
- },
- "required": ["timestamp", "source", "download_path", "total_bytes"],
- "title": "DownloadCompleteEvent",
- "type": "object"
- },
- "DownloadErrorEvent": {
- "description": "Event model for download_error",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ "title": "Description",
+ "description": "Model description"
},
"source": {
- "description": "The source of the download",
+ "type": "string",
"title": "Source",
- "type": "string"
- },
- "error_type": {
- "description": "The type of error",
- "title": "Error Type",
- "type": "string"
- },
- "error": {
- "description": "The error message",
- "title": "Error",
- "type": "string"
- }
- },
- "required": ["timestamp", "source", "error_type", "error"],
- "title": "DownloadErrorEvent",
- "type": "object"
- },
- "DownloadJob": {
- "properties": {
- "id": {
- "type": "integer",
- "title": "Id",
- "description": "Numeric ID of this job",
- "default": -1
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "dest": {
- "type": "string",
- "format": "path",
- "title": "Dest",
- "description": "Initial destination of downloaded model on local disk; a directory or file path"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "download_path": {
+ "source_api_response": {
"anyOf": [
{
- "type": "string",
- "format": "path"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Download Path",
- "description": "Final location of downloaded file or directory"
- },
- "status": {
- "$ref": "#/components/schemas/DownloadJobStatus",
- "description": "Status of the download",
- "default": "waiting"
- },
- "bytes": {
- "type": "integer",
- "title": "Bytes",
- "description": "Bytes downloaded so far",
- "default": 0
- },
- "total_bytes": {
- "type": "integer",
- "title": "Total Bytes",
- "description": "Total file size (bytes)",
- "default": 0
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "error_type": {
+ "source_url": {
"anyOf": [
{
"type": "string"
@@ -22087,10 +20739,10 @@
"type": "null"
}
],
- "title": "Error Type",
- "description": "Name of exception that caused an error"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "error": {
+ "cover_image": {
"anyOf": [
{
"type": "string"
@@ -22099,47 +20751,91 @@
"type": "null"
}
],
- "title": "Error",
- "description": "Traceback of the exception that caused an error"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "source": {
+ "format": {
"type": "string",
- "minLength": 1,
- "format": "uri",
- "title": "Source",
- "description": "Where to download from. Specific types specified in child classes."
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
},
- "access_token": {
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
+ },
+ "default_settings": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "title": "Access Token",
- "description": "authorization token for protected resources"
+ ]
},
- "priority": {
+ "base": {
+ "type": "string",
+ "const": "sd-1",
+ "title": "Base",
+ "default": "sd-1"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Diffusers_SD1_Config"
+ },
+ "ControlNet_Diffusers_SD2_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
"type": "integer",
- "title": "Priority",
- "description": "Queue priority; lower values are higher priority",
- "default": 10
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "job_started": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Job Started",
- "description": "Timestamp for when the download job started"
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
},
- "job_ended": {
+ "description": {
"anyOf": [
{
"type": "string"
@@ -22148,34 +20844,19 @@
"type": "null"
}
],
- "title": "Job Ended",
- "description": "Timestamp for when the download job ende1d (completed or errored)"
+ "title": "Description",
+ "description": "Model description"
},
- "content_type": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Content Type",
- "description": "Content type of downloaded file"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "canonical_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Canonical Url",
- "description": "Canonical URL to request on resume"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "etag": {
+ "source_api_response": {
"anyOf": [
{
"type": "string"
@@ -22184,10 +20865,10 @@
"type": "null"
}
],
- "title": "Etag",
- "description": "ETag from the remote server, if available"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "last_modified": {
+ "source_url": {
"anyOf": [
{
"type": "string"
@@ -22196,10 +20877,10 @@
"type": "null"
}
],
- "title": "Last Modified",
- "description": "Last-Modified from the remote server, if available"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "final_url": {
+ "cover_image": {
"anyOf": [
{
"type": "string"
@@ -22208,28 +20889,91 @@
"type": "null"
}
],
- "title": "Final Url",
- "description": "Final resolved URL after redirects, if available"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "expected_total_bytes": {
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
+ },
+ "default_settings": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "title": "Expected Total Bytes",
- "description": "Expected total size of the download"
+ ]
},
- "resume_required": {
- "type": "boolean",
- "title": "Resume Required",
- "description": "True if server refused resume; restart required",
- "default": false
+ "base": {
+ "type": "string",
+ "const": "sd-2",
+ "title": "Base",
+ "default": "sd-2"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Diffusers_SD2_Config"
+ },
+ "ControlNet_Diffusers_SDXL_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "resume_message": {
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
"type": "string"
@@ -22238,106 +20982,136 @@
"type": "null"
}
],
- "title": "Resume Message",
- "description": "Message explaining why resume is required"
- },
- "resume_from_scratch": {
- "type": "boolean",
- "title": "Resume From Scratch",
- "description": "True if resume metadata existed but the partial file was missing and the download restarted from the beginning",
- "default": false
- }
- },
- "type": "object",
- "required": ["dest", "source"],
- "title": "DownloadJob",
- "description": "Class to monitor and control a model download request."
- },
- "DownloadJobStatus": {
- "type": "string",
- "enum": ["waiting", "running", "paused", "completed", "cancelled", "error"],
- "title": "DownloadJobStatus",
- "description": "State of a download job."
- },
- "DownloadPausedEvent": {
- "description": "Event model for download_paused",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ "title": "Description",
+ "description": "Model description"
},
"source": {
- "description": "The source of the download",
+ "type": "string",
"title": "Source",
- "type": "string"
- }
- },
- "required": ["timestamp", "source"],
- "title": "DownloadPausedEvent",
- "type": "object"
- },
- "DownloadProgressEvent": {
- "description": "Event model for download_progress",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "source": {
- "description": "The source of the download",
- "title": "Source",
- "type": "string"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "download_path": {
- "description": "The local path where the download is saved",
- "title": "Download Path",
- "type": "string"
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "current_bytes": {
- "description": "The number of bytes downloaded so far",
- "title": "Current Bytes",
- "type": "integer"
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "total_bytes": {
- "description": "The total number of bytes to be downloaded",
- "title": "Total Bytes",
- "type": "integer"
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "const": "controlnet",
+ "title": "Type",
+ "default": "controlnet"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
}
},
- "required": ["timestamp", "source", "download_path", "current_bytes", "total_bytes"],
- "title": "DownloadProgressEvent",
- "type": "object"
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "default_settings",
+ "base"
+ ],
+ "title": "ControlNet_Diffusers_SDXL_Config"
},
- "DownloadStartedEvent": {
- "description": "Event model for download_started",
+ "ControlOutput": {
+ "class": "output",
+ "description": "node output for ControlNet info",
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "source": {
- "description": "The source of the download",
- "title": "Source",
- "type": "string"
+ "control": {
+ "$ref": "#/components/schemas/ControlField",
+ "description": "ControlNet(s) to apply",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "download_path": {
- "description": "The local path where the download is saved",
- "title": "Download Path",
+ "type": {
+ "const": "control_output",
+ "default": "control_output",
+ "field_kind": "node_attribute",
+ "title": "type",
"type": "string"
}
},
- "required": ["timestamp", "source", "download_path"],
- "title": "DownloadStartedEvent",
+ "required": ["output_meta", "control", "type", "type"],
+ "title": "ControlOutput",
"type": "object"
},
- "DynamicPromptInvocation": {
- "category": "prompt",
+ "CoreMetadataInvocation": {
+ "additionalProperties": true,
+ "category": "metadata",
"class": "invocation",
- "classification": "stable",
- "description": "Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator",
+ "classification": "internal",
+ "description": "Used internally by Invoke to collect metadata for generations.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -22358,15 +21132,58 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": false,
+ "default": true,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
"type": "boolean"
},
- "prompt": {
+ "generation_mode": {
"anyOf": [
{
+ "enum": [
+ "txt2img",
+ "img2img",
+ "inpaint",
+ "outpaint",
+ "sdxl_txt2img",
+ "sdxl_img2img",
+ "sdxl_inpaint",
+ "sdxl_outpaint",
+ "flux_txt2img",
+ "flux_img2img",
+ "flux_inpaint",
+ "flux_outpaint",
+ "flux2_txt2img",
+ "flux2_img2img",
+ "flux2_inpaint",
+ "flux2_outpaint",
+ "sd3_txt2img",
+ "sd3_img2img",
+ "sd3_inpaint",
+ "sd3_outpaint",
+ "cogview4_txt2img",
+ "cogview4_img2img",
+ "cogview4_inpaint",
+ "cogview4_outpaint",
+ "z_image_txt2img",
+ "z_image_img2img",
+ "z_image_inpaint",
+ "z_image_outpaint",
+ "qwen_image_txt2img",
+ "qwen_image_img2img",
+ "qwen_image_inpaint",
+ "qwen_image_outpaint",
+ "anima_txt2img",
+ "anima_img2img",
+ "anima_inpaint",
+ "anima_outpaint",
+ "wan_txt2img",
+ "wan_img2img",
+ "wan_inpaint",
+ "wan_outpaint",
+ "wan_i2v"
+ ],
"type": "string"
},
{
@@ -22374,848 +21191,498 @@
}
],
"default": null,
- "description": "The prompt to parse with dynamicprompts",
+ "description": "The generation mode that output this image",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Prompt",
- "ui_component": "textarea"
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Generation Mode"
},
- "max_prompts": {
- "default": 1,
- "description": "The number of prompts to generate",
+ "positive_prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The positive prompt parameter",
"field_kind": "input",
"input": "any",
- "orig_default": 1,
+ "orig_default": null,
"orig_required": false,
- "title": "Max Prompts",
- "type": "integer"
+ "title": "Positive Prompt"
},
- "combinatorial": {
- "default": false,
- "description": "Whether to use the combinatorial generator",
+ "negative_prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The negative prompt parameter",
"field_kind": "input",
"input": "any",
- "orig_default": false,
+ "orig_default": null,
"orig_required": false,
- "title": "Combinatorial",
- "type": "boolean"
- },
- "type": {
- "const": "dynamic_prompt",
- "default": "dynamic_prompt",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["prompt", "collection"],
- "title": "Dynamic Prompt",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/StringCollectionOutput"
- }
- },
- "DynamicPromptsResponse": {
- "properties": {
- "prompts": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Prompts"
+ "title": "Negative Prompt"
},
- "error": {
+ "width": {
"anyOf": [
{
- "type": "string"
+ "type": "integer"
},
{
"type": "null"
}
],
- "title": "Error"
- }
- },
- "type": "object",
- "required": ["prompts"],
- "title": "DynamicPromptsResponse"
- },
- "ESRGANInvocation": {
- "category": "upscale",
- "class": "invocation",
- "classification": "stable",
- "description": "Upscales an image using RealESRGAN.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "default": null,
+ "description": "The width parameter",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Width"
+ },
+ "height": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
+ "description": "The height parameter",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
"orig_required": false,
- "ui_hidden": false
+ "title": "Height"
},
- "metadata": {
+ "seed": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
+ "description": "The seed used for noise generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
"orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "title": "Seed"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "rand_device": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The device used for random number generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Rand Device"
},
- "image": {
+ "cfg_scale": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "number"
},
{
"type": "null"
}
],
"default": null,
- "description": "The input image",
+ "description": "The classifier-free guidance scale parameter",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Cfg Scale"
},
- "model_name": {
- "default": "RealESRGAN_x4plus.pth",
- "description": "The Real-ESRGAN model to use",
- "enum": [
- "RealESRGAN_x4plus.pth",
- "RealESRGAN_x4plus_anime_6B.pth",
- "ESRGAN_SRx4_DF2KOST_official-ff704c30.pth",
- "RealESRGAN_x2plus.pth"
+ "cfg_rescale_multiplier": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
],
+ "default": null,
+ "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
"field_kind": "input",
"input": "any",
- "orig_default": "RealESRGAN_x4plus.pth",
+ "orig_default": null,
"orig_required": false,
- "title": "Model Name",
- "type": "string"
+ "title": "Cfg Rescale Multiplier"
},
- "tile_size": {
- "default": 400,
- "description": "Tile size for tiled ESRGAN upscaling (0=tiling disabled)",
+ "steps": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The number of steps used for inference",
"field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 400,
+ "orig_default": null,
"orig_required": false,
- "title": "Tile Size",
- "type": "integer"
+ "title": "Steps"
},
- "type": {
- "const": "esrgan",
- "default": "esrgan",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["esrgan", "upscale"],
- "title": "Upscale (RealESRGAN)",
- "type": "object",
- "version": "1.3.2",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "Edge": {
- "properties": {
- "source": {
- "$ref": "#/components/schemas/EdgeConnection",
- "description": "The connection for the edge's from node and field"
- },
- "destination": {
- "$ref": "#/components/schemas/EdgeConnection",
- "description": "The connection for the edge's to node and field"
- }
- },
- "type": "object",
- "required": ["source", "destination"],
- "title": "Edge"
- },
- "EdgeConnection": {
- "properties": {
- "node_id": {
- "type": "string",
- "title": "Node Id",
- "description": "The id of the node for this edge connection"
- },
- "field": {
- "type": "string",
- "title": "Field",
- "description": "The field for this connection"
- }
- },
- "type": "object",
- "required": ["node_id", "field"],
- "title": "EdgeConnection"
- },
- "EnqueueBatchResult": {
- "properties": {
- "queue_id": {
- "type": "string",
- "title": "Queue Id",
- "description": "The ID of the queue"
- },
- "enqueued": {
- "type": "integer",
- "title": "Enqueued",
- "description": "The total number of queue items enqueued"
- },
- "requested": {
- "type": "integer",
- "title": "Requested",
- "description": "The total number of queue items requested to be enqueued"
- },
- "batch": {
- "$ref": "#/components/schemas/Batch",
- "description": "The batch that was enqueued"
- },
- "priority": {
- "type": "integer",
- "title": "Priority",
- "description": "The priority of the enqueued batch"
- },
- "item_ids": {
- "items": {
- "type": "integer"
- },
- "type": "array",
- "title": "Item Ids",
- "description": "The IDs of the queue items that were enqueued"
- }
- },
- "type": "object",
- "required": ["queue_id", "enqueued", "requested", "batch", "priority", "item_ids"],
- "title": "EnqueueBatchResult"
- },
- "ExpandMaskWithFadeInvocation": {
- "category": "mask",
- "class": "invocation",
- "classification": "stable",
- "description": "Expands a mask with a fade effect. The mask uses black to indicate areas to keep from the generated image and white for areas to discard.\nThe mask is thresholded to create a binary mask, and then a distance transform is applied to create a fade effect.\nThe fade size is specified in pixels, and the mask is expanded by that amount. The result is a mask with a smooth transition from black to white.\nIf the fade size is 0, the mask is returned as-is.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "scheduler": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
+ "description": "The scheduler used for inference",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
"orig_required": false,
- "ui_hidden": false
+ "title": "Scheduler"
},
- "metadata": {
+ "seamless_x": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "boolean"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
+ "description": "Whether seamless tiling was used on the X axis",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
"orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Seamless X"
},
- "mask": {
+ "seamless_y": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "boolean"
},
{
"type": "null"
}
],
"default": null,
- "description": "The mask to expand",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "threshold": {
- "default": 0,
- "description": "The threshold for the binary mask (0-255)",
+ "description": "Whether seamless tiling was used on the Y axis",
"field_kind": "input",
"input": "any",
- "maximum": 255,
- "minimum": 0,
- "orig_default": 0,
+ "orig_default": null,
"orig_required": false,
- "title": "Threshold",
- "type": "integer"
+ "title": "Seamless Y"
},
- "fade_size_px": {
- "default": 32,
- "description": "The size of the fade in pixels",
+ "clip_skip": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The number of skipped CLIP layers",
"field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 32,
+ "orig_default": null,
"orig_required": false,
- "title": "Fade Size Px",
- "type": "integer"
- },
- "type": {
- "const": "expand_mask_with_fade",
- "default": "expand_mask_with_fade",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "mask"],
- "title": "Expand Mask with Fade",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "ExpandPromptRequest": {
- "properties": {
- "prompt": {
- "type": "string",
- "title": "Prompt"
- },
- "model_key": {
- "type": "string",
- "title": "Model Key"
- },
- "max_tokens": {
- "type": "integer",
- "maximum": 2048.0,
- "minimum": 1.0,
- "title": "Max Tokens",
- "default": 300
+ "title": "Clip Skip"
},
- "system_prompt": {
+ "model": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "System Prompt"
- }
- },
- "type": "object",
- "required": ["prompt", "model_key"],
- "title": "ExpandPromptRequest"
- },
- "ExpandPromptResponse": {
- "properties": {
- "expanded_prompt": {
- "type": "string",
- "title": "Expanded Prompt"
+ "default": null,
+ "description": "The main model used for inference",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "error": {
+ "controlnets": {
"anyOf": [
{
- "type": "string"
+ "items": {
+ "$ref": "#/components/schemas/ControlNetMetadataField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "title": "Error"
- }
- },
- "type": "object",
- "required": ["expanded_prompt"],
- "title": "ExpandPromptResponse"
- },
- "ExposedField": {
- "properties": {
- "nodeId": {
- "type": "string",
- "title": "Nodeid"
- },
- "fieldName": {
- "type": "string",
- "title": "Fieldname"
- }
- },
- "type": "object",
- "required": ["nodeId", "fieldName"],
- "title": "ExposedField"
- },
- "ExternalApiModelConfig": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "default": ""
- },
- "path": {
- "type": "string",
- "title": "Path",
- "default": ""
- },
- "file_size": {
- "type": "integer",
- "minimum": 0.0,
- "title": "File Size",
- "default": 0
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "default": null,
+ "description": "The ControlNets used for inference",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Controlnets"
},
- "description": {
+ "ipAdapters": {
"anyOf": [
{
- "type": "string"
+ "items": {
+ "$ref": "#/components/schemas/IPAdapterMetadataField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "default": ""
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "default": "external"
+ "default": null,
+ "description": "The IP Adapters used for inference",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Ipadapters"
},
- "source_api_response": {
+ "t2iAdapters": {
"anyOf": [
{
- "type": "string"
+ "items": {
+ "$ref": "#/components/schemas/T2IAdapterMetadataField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "The IP Adapters used for inference",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "T2Iadapters"
},
- "source_url": {
+ "loras": {
"anyOf": [
{
- "type": "string"
+ "items": {
+ "$ref": "#/components/schemas/LoRAMetadataField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The LoRAs used for inference",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Loras"
},
- "cover_image": {
+ "strength": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "base": {
- "type": "string",
- "const": "external",
- "title": "Base",
- "default": "external"
- },
- "type": {
- "type": "string",
- "const": "external_image_generator",
- "title": "Type",
- "default": "external_image_generator"
- },
- "format": {
- "type": "string",
- "const": "external_api",
- "title": "Format",
- "default": "external_api"
- },
- "provider_id": {
- "type": "string",
- "minLength": 1,
- "title": "Provider Id",
- "description": "External provider ID"
- },
- "provider_model_id": {
- "type": "string",
- "minLength": 1,
- "title": "Provider Model Id",
- "description": "Provider-specific model ID"
- },
- "capabilities": {
- "$ref": "#/components/schemas/ExternalModelCapabilities",
- "description": "Provider capability matrix"
+ "default": null,
+ "description": "The strength used for latents-to-latents",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Strength"
},
- "default_settings": {
+ "init_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "default": null,
+ "description": "The name of the initial image",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Init Image"
},
- "panel_schema": {
+ "vae": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalModelPanelSchema"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
- ]
+ ],
+ "default": null,
+ "description": "The VAE used for decoding, if the main model's default was not used",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "tags": {
+ "qwen3_encoder": {
"anyOf": [
{
- "items": {
- "type": "string"
- },
- "type": "array"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Tags"
+ "default": null,
+ "description": "The Qwen3 text encoder model used for Z-Image inference",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "is_default": {
- "type": "boolean",
- "title": "Is Default",
- "default": false
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "base",
- "type",
- "format",
- "provider_id",
- "provider_model_id",
- "capabilities",
- "default_settings",
- "panel_schema",
- "tags",
- "is_default"
- ],
- "title": "ExternalApiModelConfig"
- },
- "ExternalApiModelDefaultSettings": {
- "properties": {
- "width": {
+ "hrf_enabled": {
"anyOf": [
{
- "type": "integer",
- "exclusiveMinimum": 0.0
+ "type": "boolean"
},
{
"type": "null"
}
],
- "title": "Width"
+ "default": null,
+ "description": "Whether or not high resolution fix was enabled.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Hrf Enabled"
},
- "height": {
+ "hrf_method": {
"anyOf": [
{
- "type": "integer",
- "exclusiveMinimum": 0.0
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Height"
+ "default": null,
+ "description": "The high resolution fix upscale method.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Hrf Method"
},
- "num_images": {
+ "hrf_strength": {
"anyOf": [
{
- "type": "integer",
- "exclusiveMinimum": 0.0
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Num Images"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "ExternalApiModelDefaultSettings"
- },
- "ExternalImageSize": {
- "properties": {
- "width": {
- "type": "integer",
- "exclusiveMinimum": 0.0,
- "title": "Width"
- },
- "height": {
- "type": "integer",
- "exclusiveMinimum": 0.0,
- "title": "Height"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": ["width", "height"],
- "title": "ExternalImageSize"
- },
- "ExternalModelCapabilities": {
- "properties": {
- "modes": {
- "items": {
- "type": "string",
- "enum": ["txt2img", "img2img", "inpaint"]
- },
- "type": "array",
- "title": "Modes"
- },
- "supports_reference_images": {
- "type": "boolean",
- "title": "Supports Reference Images",
- "default": false
- },
- "supports_negative_prompt": {
- "type": "boolean",
- "title": "Supports Negative Prompt",
- "default": true
- },
- "supports_seed": {
- "type": "boolean",
- "title": "Supports Seed",
- "default": false
- },
- "supports_guidance": {
- "type": "boolean",
- "title": "Supports Guidance",
- "default": false
- },
- "supports_steps": {
- "type": "boolean",
- "title": "Supports Steps",
- "default": false
- },
- "max_images_per_request": {
- "anyOf": [
- {
- "type": "integer",
- "exclusiveMinimum": 0.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Max Images Per Request"
- },
- "max_image_size": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ExternalImageSize"
- },
- {
- "type": "null"
- }
- ]
- },
- "allowed_aspect_ratios": {
- "anyOf": [
- {
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "title": "Allowed Aspect Ratios"
- },
- "aspect_ratio_sizes": {
- "anyOf": [
- {
- "additionalProperties": {
- "$ref": "#/components/schemas/ExternalImageSize"
- },
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "title": "Aspect Ratio Sizes"
+ "default": null,
+ "description": "The high resolution fix img2img strength used in the upscale pass.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Hrf Strength"
},
- "resolution_presets": {
+ "positive_style_prompt": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/ExternalResolutionPreset"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Resolution Presets"
+ "default": null,
+ "description": "The positive style prompt parameter",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Positive Style Prompt"
},
- "max_reference_images": {
+ "negative_style_prompt": {
"anyOf": [
{
- "type": "integer",
- "exclusiveMinimum": 0.0
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Max Reference Images"
- },
- "mask_format": {
- "type": "string",
- "enum": ["alpha", "binary", "none"],
- "title": "Mask Format",
- "default": "none"
+ "default": null,
+ "description": "The negative style prompt parameter",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Negative Style Prompt"
},
- "input_image_required_for": {
+ "refiner_model": {
"anyOf": [
{
- "items": {
- "type": "string",
- "enum": ["txt2img", "img2img", "inpaint"]
- },
- "type": "array"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Input Image Required For"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "ExternalModelCapabilities"
- },
- "ExternalModelPanelControl": {
- "properties": {
- "name": {
- "type": "string",
- "enum": ["reference_images", "dimensions", "seed"],
- "title": "Name"
+ "default": null,
+ "description": "The SDXL Refiner model used",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "slider_min": {
+ "refiner_cfg_scale": {
"anyOf": [
{
"type": "number"
@@ -23224,31 +21691,49 @@
"type": "null"
}
],
- "title": "Slider Min"
+ "default": null,
+ "description": "The classifier-free guidance scale parameter used for the refiner",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Refiner Cfg Scale"
},
- "slider_max": {
+ "refiner_steps": {
"anyOf": [
{
- "type": "number"
+ "type": "integer"
},
{
"type": "null"
}
],
- "title": "Slider Max"
+ "default": null,
+ "description": "The number of steps used for the refiner",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Refiner Steps"
},
- "number_input_min": {
+ "refiner_scheduler": {
"anyOf": [
{
- "type": "number"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Number Input Min"
+ "default": null,
+ "description": "The scheduler used for the refiner",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Refiner Scheduler"
},
- "number_input_max": {
+ "refiner_positive_aesthetic_score": {
"anyOf": [
{
"type": "number"
@@ -23257,9 +21742,15 @@
"type": "null"
}
],
- "title": "Number Input Max"
+ "default": null,
+ "description": "The aesthetic score used for the refiner",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Refiner Positive Aesthetic Score"
},
- "fine_step": {
+ "refiner_negative_aesthetic_score": {
"anyOf": [
{
"type": "number"
@@ -23268,9 +21759,15 @@
"type": "null"
}
],
- "title": "Fine Step"
+ "default": null,
+ "description": "The aesthetic score used for the refiner",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Refiner Negative Aesthetic Score"
},
- "coarse_step": {
+ "refiner_start": {
"anyOf": [
{
"type": "number"
@@ -23279,207 +21776,155 @@
"type": "null"
}
],
- "title": "Coarse Step"
- },
- "marks": {
- "anyOf": [
- {
- "items": {
- "type": "number"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "title": "Marks"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": ["name"],
- "title": "ExternalModelPanelControl"
- },
- "ExternalModelPanelSchema": {
- "properties": {
- "prompts": {
- "items": {
- "$ref": "#/components/schemas/ExternalModelPanelControl"
- },
- "type": "array",
- "title": "Prompts"
- },
- "image": {
- "items": {
- "$ref": "#/components/schemas/ExternalModelPanelControl"
- },
- "type": "array",
- "title": "Image"
- },
- "generation": {
- "items": {
- "$ref": "#/components/schemas/ExternalModelPanelControl"
- },
- "type": "array",
- "title": "Generation"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "title": "ExternalModelPanelSchema"
- },
- "ExternalModelSource": {
- "properties": {
- "provider_id": {
- "type": "string",
- "title": "Provider Id"
- },
- "provider_model_id": {
- "type": "string",
- "title": "Provider Model Id"
+ "default": null,
+ "description": "The start value used for refiner denoising",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Refiner Start"
},
"type": {
- "type": "string",
- "const": "external",
- "title": "Type",
- "default": "external"
+ "const": "core_metadata",
+ "default": "core_metadata",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["metadata"],
+ "title": "Core Metadata",
"type": "object",
- "required": ["provider_id", "provider_model_id"],
- "title": "ExternalModelSource",
- "description": "An external provider model identifier."
+ "version": "2.1.0",
+ "output": {
+ "$ref": "#/components/schemas/MetadataOutput"
+ }
},
- "ExternalProviderConfigModel": {
+ "CreateDenoiseMaskInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Creates mask for denoising model run.",
+ "node_pack": "invokeai",
"properties": {
- "provider_id": {
- "type": "string",
- "title": "Provider Id",
- "description": "The external provider identifier"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "api_key_configured": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
"type": "boolean",
- "title": "Api Key Configured",
- "description": "Whether an API key is configured"
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "base_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Base Url",
- "description": "Optional base URL override"
- }
- },
- "type": "object",
- "required": ["provider_id", "api_key_configured"],
- "title": "ExternalProviderConfigModel"
- },
- "ExternalProviderConfigUpdate": {
- "properties": {
- "api_key": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Api Key",
- "description": "API key for the external provider"
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "ui_order": 0
},
- "base_url": {
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Base Url",
- "description": "Optional base URL override for the provider"
- }
- },
- "type": "object",
- "title": "ExternalProviderConfigUpdate"
- },
- "ExternalProviderStatusModel": {
- "properties": {
- "provider_id": {
- "type": "string",
- "title": "Provider Id",
- "description": "The external provider identifier"
- },
- "configured": {
- "type": "boolean",
- "title": "Configured",
- "description": "Whether credentials are configured for the provider"
+ "default": null,
+ "description": "Image which will be masked",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_order": 1
},
- "message": {
+ "mask": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Message",
- "description": "Optional provider status detail"
- }
- },
- "type": "object",
- "required": ["provider_id", "configured"],
- "title": "ExternalProviderStatusModel"
- },
- "ExternalResolutionPreset": {
- "properties": {
- "label": {
- "type": "string",
- "minLength": 1,
- "title": "Label",
- "description": "Display label, e.g. '1:1 (1K)'"
- },
- "aspect_ratio": {
- "type": "string",
- "minLength": 1,
- "title": "Aspect Ratio",
- "description": "Aspect ratio string, e.g. '1:1'"
+ "default": null,
+ "description": "The mask to use when pasting",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "ui_order": 2
},
- "image_size": {
- "type": "string",
- "minLength": 1,
- "title": "Image Size",
- "description": "Image size preset, e.g. '1K'"
+ "tiled": {
+ "default": false,
+ "description": "Processing using overlapping tiles (reduce memory consumption)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Tiled",
+ "type": "boolean",
+ "ui_order": 3
},
- "width": {
- "type": "integer",
- "exclusiveMinimum": 0.0,
- "title": "Width"
+ "fp32": {
+ "default": false,
+ "description": "Whether or not to use full float32 precision",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Fp32",
+ "type": "boolean",
+ "ui_order": 4
},
- "height": {
- "type": "integer",
- "exclusiveMinimum": 0.0,
- "title": "Height"
+ "type": {
+ "const": "create_denoise_mask",
+ "default": "create_denoise_mask",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "additionalProperties": false,
+ "required": ["type", "id"],
+ "tags": ["mask", "denoise"],
+ "title": "Create Denoise Mask",
"type": "object",
- "required": ["label", "aspect_ratio", "image_size", "width", "height"],
- "title": "ExternalResolutionPreset"
+ "version": "1.0.2",
+ "output": {
+ "$ref": "#/components/schemas/DenoiseMaskOutput"
+ }
},
- "FLUXLoRACollectionLoader": {
- "category": "model",
+ "CreateGradientMaskInvocation": {
+ "category": "mask",
"class": "invocation",
"classification": "stable",
- "description": "Applies a collection of LoRAs to a FLUX transformer.",
+ "description": "Creates mask for denoising.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -23506,227 +21951,402 @@
"title": "Use Cache",
"type": "boolean"
},
- "loras": {
+ "mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/LoRAField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "LoRA models and weights. May be a single LoRA or collection.",
+ "description": "Image which will be masked",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "LoRAs",
- "ui_model_base": ["flux"],
- "ui_model_type": ["lora"]
+ "orig_required": true,
+ "ui_order": 1
},
- "transformer": {
+ "edge_radius": {
+ "default": 16,
+ "description": "How far to expand the edges of the mask",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 16,
+ "orig_required": false,
+ "title": "Edge Radius",
+ "type": "integer",
+ "ui_order": 2
+ },
+ "coherence_mode": {
+ "default": "Gaussian Blur",
+ "enum": ["Gaussian Blur", "Box Blur", "Staged"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "Gaussian Blur",
+ "orig_required": false,
+ "title": "Coherence Mode",
+ "type": "string",
+ "ui_order": 3
+ },
+ "minimum_denoise": {
+ "default": 0.0,
+ "description": "Minimum denoise level for the coherence region",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Minimum Denoise",
+ "type": "number",
+ "ui_order": 4
+ },
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Transformer",
+ "description": "OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE",
"field_kind": "input",
- "input": "connection",
+ "input": "any",
"orig_default": null,
"orig_required": false,
- "title": "Transformer"
+ "title": "[OPTIONAL] Image",
+ "ui_order": 6
},
- "clip": {
+ "unet": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "$ref": "#/components/schemas/UNetField"
},
{
"type": "null"
}
],
"default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "description": "OPTIONAL: If the Unet is a specialized Inpainting model, masked_latents will be generated from the image with the VAE",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "CLIP"
+ "title": "[OPTIONAL] UNet",
+ "ui_order": 5
},
- "t5_encoder": {
+ "vae": {
"anyOf": [
{
- "$ref": "#/components/schemas/T5EncoderField"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
"default": null,
- "description": "T5 tokenizer and text encoder",
+ "description": "OPTIONAL: Only connect for specialized Inpainting models, masked_latents will be generated from the image with the VAE",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "T5 Encoder"
+ "title": "[OPTIONAL] VAE",
+ "ui_order": 7
+ },
+ "tiled": {
+ "default": false,
+ "description": "Processing using overlapping tiles (reduce memory consumption)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Tiled",
+ "type": "boolean",
+ "ui_order": 8
+ },
+ "fp32": {
+ "default": false,
+ "description": "Whether or not to use full float32 precision",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Fp32",
+ "type": "boolean",
+ "ui_order": 9
},
"type": {
- "const": "flux_lora_collection_loader",
- "default": "flux_lora_collection_loader",
+ "const": "create_gradient_mask",
+ "default": "create_gradient_mask",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["lora", "model", "flux"],
- "title": "Apply LoRA Collection - FLUX",
+ "tags": ["mask", "denoise"],
+ "title": "Create Gradient Mask",
"type": "object",
- "version": "1.3.2",
+ "version": "1.3.0",
"output": {
- "$ref": "#/components/schemas/FluxLoRALoaderOutput"
+ "$ref": "#/components/schemas/GradientMaskOutput"
}
},
- "FLUXRedux_Checkpoint_Config": {
+ "CropImageToBoundingBoxInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Crop an image to the given bounding box. If the bounding box is omitted, the image is cropped to the non-transparent pixels.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "description": {
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
+ "default": null,
+ "description": "The image to crop",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "bounding_box": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoundingBoxField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The bounding box to crop the image to",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "type": {
+ "const": "crop_image_to_bounding_box",
+ "default": "crop_image_to_bounding_box",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "crop"],
+ "title": "Crop Image to Bounding Box",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "CropLatentsCoreInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Crops a latent-space tensor to a box specified in image-space. The box dimensions and coordinates must be\ndivisible by the latent scale factor of 8.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source_api_response": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "source_url": {
+ "x": {
"anyOf": [
{
- "type": "string"
+ "minimum": 0,
+ "multipleOf": 8,
+ "type": "integer"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The left x coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "X"
},
- "cover_image": {
+ "y": {
"anyOf": [
{
- "type": "string"
+ "minimum": 0,
+ "multipleOf": 8,
+ "type": "integer"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "The top y coordinate (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Y"
},
- "type": {
- "type": "string",
- "const": "flux_redux",
- "title": "Type",
- "default": "flux_redux"
+ "width": {
+ "anyOf": [
+ {
+ "minimum": 1,
+ "multipleOf": 8,
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The width (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Width"
},
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "height": {
+ "anyOf": [
+ {
+ "minimum": 1,
+ "multipleOf": 8,
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The height (in px) of the crop rectangle in image space. This value will be converted to a dimension in latent space.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Height"
},
- "base": {
- "type": "string",
- "const": "flux",
- "title": "Base",
- "default": "flux"
+ "type": {
+ "const": "crop_latents",
+ "default": "crop_latents",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["latents", "crop"],
+ "title": "Crop Latents",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "type",
- "format",
- "base"
- ],
- "title": "FLUXRedux_Checkpoint_Config",
- "description": "Model config for FLUX Tools Redux model."
+ "version": "1.0.2",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
- "FaceIdentifierInvocation": {
- "category": "segmentation",
+ "CvInpaintInvocation": {
+ "category": "inpaint",
"class": "invocation",
"classification": "stable",
- "description": "Outputs an image with detected face IDs printed on each face. For use with other FaceTools.",
+ "description": "Simple inpaint using opencv.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -23795,55 +22415,66 @@
}
],
"default": null,
- "description": "Image to face detect",
+ "description": "The image to inpaint",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "minimum_confidence": {
- "default": 0.5,
- "description": "Minimum confidence for face detection (lower if detection is failing)",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.5,
- "orig_required": false,
- "title": "Minimum Confidence",
- "type": "number"
- },
- "chunk": {
- "default": false,
- "description": "Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image.",
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The mask to use when inpainting",
"field_kind": "input",
"input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Chunk",
- "type": "boolean"
+ "orig_required": true
},
"type": {
- "const": "face_identifier",
- "default": "face_identifier",
+ "const": "cv_inpaint",
+ "default": "cv_inpaint",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "face", "identifier"],
- "title": "FaceIdentifier",
+ "tags": ["opencv", "inpaint"],
+ "title": "OpenCV Inpaint",
"type": "object",
- "version": "1.2.2",
+ "version": "1.3.1",
"output": {
"$ref": "#/components/schemas/ImageOutput"
}
},
- "FaceMaskInvocation": {
- "category": "segmentation",
+ "DWOpenposeDetectionInvocation": {
+ "category": "controlnet_preprocessors",
"class": "invocation",
"classification": "stable",
- "description": "Face mask creation using mediapipe face detection",
+ "description": "Generates an openpose pose from an image using DWPose",
"node_pack": "invokeai",
"properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"metadata": {
"anyOf": [
{
@@ -23894,153 +22525,62 @@
}
],
"default": null,
- "description": "Image to face detect",
+ "description": "The image to process",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "face_ids": {
- "default": "",
- "description": "Comma-separated list of face ids to mask eg '0,2,7'. Numbered from 0. Leave empty to mask all. Find face IDs with FaceIdentifier node.",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Face Ids",
- "type": "string"
- },
- "minimum_confidence": {
- "default": 0.5,
- "description": "Minimum confidence for face detection (lower if detection is failing)",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.5,
- "orig_required": false,
- "title": "Minimum Confidence",
- "type": "number"
- },
- "x_offset": {
- "default": 0.0,
- "description": "Offset for the X-axis of the face mask",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.0,
- "orig_required": false,
- "title": "X Offset",
- "type": "number"
- },
- "y_offset": {
- "default": 0.0,
- "description": "Offset for the Y-axis of the face mask",
+ "draw_body": {
+ "default": true,
"field_kind": "input",
"input": "any",
- "orig_default": 0.0,
+ "orig_default": true,
"orig_required": false,
- "title": "Y Offset",
- "type": "number"
+ "title": "Draw Body",
+ "type": "boolean"
},
- "chunk": {
+ "draw_face": {
"default": false,
- "description": "Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image.",
"field_kind": "input",
"input": "any",
"orig_default": false,
"orig_required": false,
- "title": "Chunk",
+ "title": "Draw Face",
"type": "boolean"
},
- "invert_mask": {
+ "draw_hands": {
"default": false,
- "description": "Toggle to invert the mask",
"field_kind": "input",
"input": "any",
"orig_default": false,
"orig_required": false,
- "title": "Invert Mask",
+ "title": "Draw Hands",
"type": "boolean"
},
"type": {
- "const": "face_mask_detection",
- "default": "face_mask_detection",
+ "const": "dw_openpose_detection",
+ "default": "dw_openpose_detection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "face", "mask"],
- "title": "FaceMask",
+ "tags": ["controlnet", "dwpose", "openpose"],
+ "title": "DW Openpose Detection",
"type": "object",
- "version": "1.2.2",
+ "version": "1.1.1",
"output": {
- "$ref": "#/components/schemas/FaceMaskOutput"
+ "$ref": "#/components/schemas/ImageOutput"
}
},
- "FaceMaskOutput": {
- "class": "output",
- "description": "Base class for FaceMask output",
- "properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The output image",
- "field_kind": "output",
- "ui_hidden": false
- },
- "width": {
- "description": "The width of the image in pixels",
- "field_kind": "output",
- "title": "Width",
- "type": "integer",
- "ui_hidden": false
- },
- "height": {
- "description": "The height of the image in pixels",
- "field_kind": "output",
- "title": "Height",
- "type": "integer",
- "ui_hidden": false
- },
- "type": {
- "const": "face_mask_output",
- "default": "face_mask_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- },
- "mask": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The output mask",
- "field_kind": "output",
- "ui_hidden": false
- }
- },
- "required": ["output_meta", "image", "width", "height", "type", "mask", "type"],
- "title": "FaceMaskOutput",
- "type": "object"
- },
- "FaceOffInvocation": {
- "category": "segmentation",
+ "DecodeInvisibleWatermarkInvocation": {
+ "category": "image",
"class": "invocation",
"classification": "stable",
- "description": "Bound, extract, and mask a face from an image using MediaPipe detection",
+ "description": "Decode an invisible watermark from an image.",
"node_pack": "invokeai",
"properties": {
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -24075,322 +22615,199 @@
}
],
"default": null,
- "description": "Image for face detection",
+ "description": "The image to decode the watermark from",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "face_id": {
- "default": 0,
- "description": "The face ID to process, numbered from 0. Multiple faces not supported. Find a face's ID with FaceIdentifier node.",
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "Face Id",
- "type": "integer"
- },
- "minimum_confidence": {
- "default": 0.5,
- "description": "Minimum confidence for face detection (lower if detection is failing)",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.5,
- "orig_required": false,
- "title": "Minimum Confidence",
- "type": "number"
- },
- "x_offset": {
- "default": 0.0,
- "description": "X-axis offset of the mask",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.0,
- "orig_required": false,
- "title": "X Offset",
- "type": "number"
- },
- "y_offset": {
- "default": 0.0,
- "description": "Y-axis offset of the mask",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Y Offset",
- "type": "number"
- },
- "padding": {
- "default": 0,
- "description": "All-axis padding around the mask in pixels",
+ "length": {
+ "default": 8,
+ "description": "The expected watermark length in bytes",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
+ "orig_default": 8,
"orig_required": false,
- "title": "Padding",
+ "title": "Length",
"type": "integer"
},
- "chunk": {
- "default": false,
- "description": "Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image.",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Chunk",
- "type": "boolean"
- },
"type": {
- "const": "face_off",
- "default": "face_off",
+ "const": "decode_watermark",
+ "default": "decode_watermark",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "faceoff", "face", "mask"],
- "title": "FaceOff",
+ "tags": ["image", "watermark"],
+ "title": "Decode Invisible Watermark",
"type": "object",
- "version": "1.2.2",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/FaceOffOutput"
+ "$ref": "#/components/schemas/StringOutput"
}
},
- "FaceOffOutput": {
- "class": "output",
- "description": "Base class for FaceOff Output",
+ "DeleteAllExceptCurrentResult": {
"properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The output image",
- "field_kind": "output",
- "ui_hidden": false
- },
- "width": {
- "description": "The width of the image in pixels",
- "field_kind": "output",
- "title": "Width",
- "type": "integer",
- "ui_hidden": false
- },
- "height": {
- "description": "The height of the image in pixels",
- "field_kind": "output",
- "title": "Height",
+ "deleted": {
"type": "integer",
- "ui_hidden": false
+ "title": "Deleted",
+ "description": "Number of queue items deleted"
+ }
+ },
+ "type": "object",
+ "required": ["deleted"],
+ "title": "DeleteAllExceptCurrentResult",
+ "description": "Result of deleting all except current"
+ },
+ "DeleteBoardResult": {
+ "properties": {
+ "board_id": {
+ "type": "string",
+ "title": "Board Id",
+ "description": "The id of the board that was deleted."
},
- "type": {
- "const": "face_off_output",
- "default": "face_off_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "deleted_board_images": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted Board Images",
+ "description": "The image names of the board-images relationships that were deleted."
},
- "mask": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The output mask",
- "field_kind": "output",
- "ui_hidden": false
+ "deleted_images": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted Images",
+ "description": "The names of the images that were deleted."
},
- "x": {
- "description": "The x coordinate of the bounding box's left side",
- "field_kind": "output",
- "title": "X",
- "type": "integer",
- "ui_hidden": false
+ "deleted_board_videos": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted Board Videos",
+ "description": "The video names of the board-videos relationships that were deleted."
},
- "y": {
- "description": "The y coordinate of the bounding box's top side",
- "field_kind": "output",
- "title": "Y",
- "type": "integer",
- "ui_hidden": false
+ "deleted_videos": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted Videos",
+ "description": "The names of the videos that were deleted."
}
},
- "required": ["output_meta", "image", "width", "height", "type", "mask", "x", "y", "type"],
- "title": "FaceOffOutput",
- "type": "object"
+ "type": "object",
+ "required": ["board_id", "deleted_board_images", "deleted_images"],
+ "title": "DeleteBoardResult"
},
- "FieldKind": {
- "description": "The kind of field.\n- `Input`: An input field on a node.\n- `Output`: An output field on a node.\n- `Internal`: A field which is treated as an input, but cannot be used in node definitions. Metadata is\none example. It is provided to nodes via the WithMetadata class, and we want to reserve the field name\n\"metadata\" for this on all nodes. `FieldKind` is used to short-circuit the field name validation logic,\nallowing \"metadata\" for that field.\n- `NodeAttribute`: The field is a node attribute. These are fields which are not inputs or outputs,\nbut which are used to store information about the node. For example, the `id` and `type` fields are node\nattributes.\n\nThe presence of this in `json_schema_extra[\"field_kind\"]` is used when initializing node schemas on app\nstartup, and when generating the OpenAPI schema for the workflow editor.",
- "enum": ["input", "output", "internal", "node_attribute"],
- "title": "FieldKind",
- "type": "string"
+ "DeleteByDestinationResult": {
+ "properties": {
+ "deleted": {
+ "type": "integer",
+ "title": "Deleted",
+ "description": "Number of queue items deleted"
+ }
+ },
+ "type": "object",
+ "required": ["deleted"],
+ "title": "DeleteByDestinationResult",
+ "description": "Result of deleting by a destination"
},
- "FloatBatchInvocation": {
- "category": "batch",
- "class": "invocation",
- "classification": "special",
- "description": "Create a batched generation, where the workflow is executed once for each float in the batch.",
- "node_pack": "invokeai",
+ "DeleteImagesResult": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "batch_group_id": {
- "default": "None",
- "description": "The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size.",
- "enum": ["None", "Group 1", "Group 2", "Group 3", "Group 4", "Group 5"],
- "field_kind": "input",
- "input": "direct",
- "orig_default": "None",
- "orig_required": false,
- "title": "Batch Group",
- "type": "string"
- },
- "floats": {
- "anyOf": [
- {
- "items": {
- "type": "number"
- },
- "minItems": 1,
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The floats to batch over",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Floats"
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the delete operation"
},
- "type": {
- "const": "float_batch",
- "default": "float_batch",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "deleted_images": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted Images",
+ "description": "The names of the images that were deleted"
}
},
- "required": ["type", "id"],
- "tags": ["primitives", "float", "number", "batch", "special"],
- "title": "Float Batch",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/FloatOutput"
- }
+ "required": ["affected_boards", "deleted_images"],
+ "title": "DeleteImagesResult"
},
- "FloatCollectionInvocation": {
- "category": "primitives",
- "class": "invocation",
- "classification": "stable",
- "description": "A collection of float primitive values",
- "node_pack": "invokeai",
+ "DeleteOrphanedModelsRequest": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "collection": {
- "default": [],
- "description": "The collection of float values",
- "field_kind": "input",
- "input": "any",
+ "paths": {
"items": {
- "type": "number"
+ "type": "string"
},
- "orig_default": [],
- "orig_required": false,
- "title": "Collection",
- "type": "array"
+ "type": "array",
+ "title": "Paths",
+ "description": "List of relative paths to delete"
+ }
+ },
+ "type": "object",
+ "required": ["paths"],
+ "title": "DeleteOrphanedModelsRequest",
+ "description": "Request to delete specific orphaned model directories."
+ },
+ "DeleteOrphanedModelsResponse": {
+ "properties": {
+ "deleted": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted",
+ "description": "Paths that were successfully deleted"
},
- "type": {
- "const": "float_collection",
- "default": "float_collection",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "errors": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "type": "object",
+ "title": "Errors",
+ "description": "Paths that had errors, with error messages"
}
},
- "required": ["type", "id"],
- "tags": ["primitives", "float", "collection"],
- "title": "Float Collection Primitive",
"type": "object",
- "version": "1.0.2",
- "output": {
- "$ref": "#/components/schemas/FloatCollectionOutput"
- }
+ "required": ["deleted", "errors"],
+ "title": "DeleteOrphanedModelsResponse",
+ "description": "Response from deleting orphaned models."
},
- "FloatCollectionOutput": {
- "class": "output",
- "description": "Base class for nodes that output a collection of floats",
+ "DeleteVideosResult": {
"properties": {
- "collection": {
- "description": "The float collection",
- "field_kind": "output",
+ "affected_boards": {
"items": {
- "type": "number"
+ "type": "string"
},
- "title": "Collection",
"type": "array",
- "ui_hidden": false
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the operation"
},
- "type": {
- "const": "float_collection_output",
- "default": "float_collection_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "deleted_videos": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Deleted Videos",
+ "description": "The names of the videos that were deleted"
}
},
- "required": ["output_meta", "collection", "type", "type"],
- "title": "FloatCollectionOutput",
- "type": "object"
+ "type": "object",
+ "required": ["affected_boards", "deleted_videos"],
+ "title": "DeleteVideosResult"
},
- "FloatGenerator": {
- "category": "batch",
+ "DenoiseLatentsInvocation": {
+ "category": "latents",
"class": "invocation",
- "classification": "special",
- "description": "Generated a range of floats for use in a batched generation",
+ "classification": "stable",
+ "description": "Denoises noisy latents to decodable images",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -24417,449 +22834,51 @@
"title": "Use Cache",
"type": "boolean"
},
- "generator": {
- "$ref": "#/components/schemas/FloatGeneratorField",
- "description": "The float generator.",
+ "positive_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Positive conditioning tensor",
"field_kind": "input",
- "input": "direct",
+ "input": "connection",
"orig_required": true,
- "title": "Generator Type"
+ "title": "Positive Conditioning",
+ "ui_order": 0
},
- "type": {
- "const": "float_generator",
- "default": "float_generator",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["generator", "type", "id"],
- "tags": ["primitives", "float", "number", "batch", "special"],
- "title": "Float Generator",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/FloatGeneratorOutput"
- }
- },
- "FloatGeneratorField": {
- "properties": {},
- "title": "FloatGeneratorField",
- "type": "object"
- },
- "FloatGeneratorOutput": {
- "class": "output",
- "description": "Base class for nodes that output a collection of floats",
- "properties": {
- "floats": {
- "description": "The generated floats",
- "field_kind": "output",
- "items": {
- "type": "number"
- },
- "title": "Floats",
- "type": "array",
- "ui_hidden": false
- },
- "type": {
- "const": "float_generator_output",
- "default": "float_generator_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "floats", "type", "type"],
- "title": "FloatGeneratorOutput",
- "type": "object"
- },
- "FloatInvocation": {
- "category": "primitives",
- "class": "invocation",
- "classification": "stable",
- "description": "A float primitive value",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "value": {
- "default": 0.0,
- "description": "The float value",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Value",
- "type": "number"
- },
- "type": {
- "const": "float",
- "default": "float",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["primitives", "float"],
- "title": "Float Primitive",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/FloatOutput"
- }
- },
- "FloatLinearRangeInvocation": {
- "category": "math",
- "class": "invocation",
- "classification": "stable",
- "description": "Creates a range",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "start": {
- "default": 5,
- "description": "The first value of the range",
- "field_kind": "input",
- "input": "any",
- "orig_default": 5,
- "orig_required": false,
- "title": "Start",
- "type": "number"
- },
- "stop": {
- "default": 10,
- "description": "The last value of the range",
- "field_kind": "input",
- "input": "any",
- "orig_default": 10,
- "orig_required": false,
- "title": "Stop",
- "type": "number"
- },
- "steps": {
- "default": 30,
- "description": "number of values to interpolate over (including start and stop)",
- "field_kind": "input",
- "input": "any",
- "orig_default": 30,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
- },
- "type": {
- "const": "float_range",
- "default": "float_range",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["math", "range"],
- "title": "Float Range",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/FloatCollectionOutput"
- }
- },
- "FloatMathInvocation": {
- "category": "math",
- "class": "invocation",
- "classification": "stable",
- "description": "Performs floating point math.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "operation": {
- "default": "ADD",
- "description": "The operation to perform",
- "enum": ["ADD", "SUB", "MUL", "DIV", "EXP", "ABS", "SQRT", "MIN", "MAX"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "ADD",
- "orig_required": false,
- "title": "Operation",
- "type": "string",
- "ui_choice_labels": {
- "ABS": "Absolute Value of A",
- "ADD": "Add A+B",
- "DIV": "Divide A/B",
- "EXP": "Exponentiate A^B",
- "MAX": "Maximum(A,B)",
- "MIN": "Minimum(A,B)",
- "MUL": "Multiply A*B",
- "SQRT": "Square Root of A",
- "SUB": "Subtract A-B"
- }
- },
- "a": {
- "default": 1,
- "description": "The first number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "A",
- "type": "number"
- },
- "b": {
- "default": 1,
- "description": "The second number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "B",
- "type": "number"
- },
- "type": {
- "const": "float_math",
- "default": "float_math",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": [
- "math",
- "float",
- "add",
- "subtract",
- "multiply",
- "divide",
- "power",
- "root",
- "absolute value",
- "min",
- "max"
- ],
- "title": "Float Math",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/FloatOutput"
- }
- },
- "FloatOutput": {
- "class": "output",
- "description": "Base class for nodes that output a single float",
- "properties": {
- "value": {
- "description": "The output float",
- "field_kind": "output",
- "title": "Value",
- "type": "number",
- "ui_hidden": false
- },
- "type": {
- "const": "float_output",
- "default": "float_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "value", "type", "type"],
- "title": "FloatOutput",
- "type": "object"
- },
- "FloatToIntegerInvocation": {
- "category": "math",
- "class": "invocation",
- "classification": "stable",
- "description": "Rounds a float number to (a multiple of) an integer.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "value": {
- "default": 0,
- "description": "The value to round",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Value",
- "type": "number"
- },
- "multiple": {
- "default": 1,
- "description": "The multiple to round to",
- "field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1,
- "orig_required": false,
- "title": "Multiple of",
- "type": "integer"
- },
- "method": {
- "default": "Nearest",
- "description": "The method to use for rounding",
- "enum": ["Nearest", "Floor", "Ceiling", "Truncate"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "Nearest",
- "orig_required": false,
- "title": "Method",
- "type": "string"
- },
- "type": {
- "const": "float_to_int",
- "default": "float_to_int",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["math", "round", "integer", "float", "convert"],
- "title": "Float To Integer",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/IntegerOutput"
- }
- },
- "Flux2DenoiseInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "prototype",
- "description": "Run denoising process with a FLUX.2 Klein transformer model.\n\nThis node is designed for FLUX.2 Klein models which use Qwen3 as the text encoder.\nIt does not support ControlNet, IP-Adapters, or regional prompting.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "latents": {
+ "negative_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Latents tensor",
+ "description": "Negative conditioning tensor",
"field_kind": "input",
"input": "connection",
- "orig_default": null,
- "orig_required": false
+ "orig_required": true,
+ "title": "Negative Conditioning",
+ "ui_order": 1
},
"noise": {
"anyOf": [
@@ -24875,23 +22894,39 @@
"field_kind": "input",
"input": "connection",
"orig_default": null,
- "orig_required": false
+ "orig_required": false,
+ "ui_order": 3
},
- "denoise_mask": {
+ "steps": {
+ "default": 10,
+ "description": "Number of steps to run",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 10,
+ "orig_required": false,
+ "title": "Steps",
+ "type": "integer"
+ },
+ "cfg_scale": {
"anyOf": [
{
- "$ref": "#/components/schemas/DenoiseMaskField"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "default": 7.5,
+ "description": "Classifier-Free Guidance scale",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "input": "any",
+ "orig_default": 7.5,
+ "orig_required": false,
+ "title": "CFG Scale"
},
"denoising_start": {
"default": 0.0,
@@ -24917,205 +22952,223 @@
"title": "Denoising End",
"type": "number"
},
- "add_noise": {
- "default": true,
- "description": "Add noise based on denoising start.",
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler to use during inference",
+ "enum": [
+ "ddim",
+ "ddpm",
+ "deis",
+ "deis_k",
+ "lms",
+ "lms_k",
+ "pndm",
+ "heun",
+ "heun_k",
+ "euler",
+ "euler_k",
+ "euler_a",
+ "kdpm_2",
+ "kdpm_2_k",
+ "kdpm_2_a",
+ "kdpm_2_a_k",
+ "dpmpp_2s",
+ "dpmpp_2s_k",
+ "dpmpp_2m",
+ "dpmpp_2m_k",
+ "dpmpp_2m_sde",
+ "dpmpp_2m_sde_k",
+ "dpmpp_3m",
+ "dpmpp_3m_k",
+ "dpmpp_sde",
+ "dpmpp_sde_k",
+ "er_sde",
+ "unipc",
+ "unipc_k",
+ "lcm",
+ "tcd"
+ ],
"field_kind": "input",
"input": "any",
- "orig_default": true,
+ "orig_default": "euler",
"orig_required": false,
- "title": "Add Noise",
- "type": "boolean"
+ "title": "Scheduler",
+ "type": "string",
+ "ui_type": "SchedulerField"
},
- "transformer": {
+ "unet": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "$ref": "#/components/schemas/UNetField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Flux model (Transformer) to load",
+ "description": "UNet (scheduler, LoRAs)",
"field_kind": "input",
"input": "connection",
"orig_required": true,
- "title": "Transformer"
+ "title": "UNet",
+ "ui_order": 2
},
- "positive_text_conditioning": {
+ "control": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxConditioningField"
+ "$ref": "#/components/schemas/ControlField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/ControlField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Positive conditioning tensor",
"field_kind": "input",
"input": "connection",
- "orig_required": true
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Control",
+ "ui_order": 5
},
- "negative_text_conditioning": {
+ "ip_adapter": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxConditioningField"
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Negative conditioning tensor. Can be None if cfg_scale is 1.0.",
+ "description": "IP-Adapter to apply",
"field_kind": "input",
"input": "connection",
"orig_default": null,
- "orig_required": false
- },
- "guidance": {
- "default": 4.0,
- "description": "Guidance strength for distilled guidance-embedding models. Inert for all current FLUX.2 Klein variants (their guidance_embeds weights are absent/zero); kept for node-graph compatibility and future guidance-embedded models.",
- "field_kind": "input",
- "input": "any",
- "maximum": 20,
- "minimum": 0,
- "orig_default": 4.0,
- "orig_required": false,
- "title": "Guidance",
- "type": "number"
- },
- "cfg_scale": {
- "default": 1.0,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1.0,
- "orig_required": false,
- "title": "CFG Scale",
- "type": "number"
- },
- "width": {
- "default": 1024,
- "description": "Width of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
- },
- "num_steps": {
- "default": 4,
- "description": "Number of diffusion steps. Use 4 for distilled models, 28+ for base models.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 4,
"orig_required": false,
- "title": "Num Steps",
- "type": "integer"
+ "title": "IP-Adapter",
+ "ui_order": 6
},
- "scheduler": {
- "default": "euler",
- "description": "Scheduler (sampler) for the denoising process. 'euler' is fast and standard. 'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
- "enum": ["euler", "heun", "lcm"],
+ "t2i_adapter": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/T2IAdapterField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/T2IAdapterField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "T2I-Adapter(s) to apply",
"field_kind": "input",
- "input": "any",
- "orig_default": "euler",
+ "input": "connection",
+ "orig_default": null,
"orig_required": false,
- "title": "Scheduler",
- "type": "string",
- "ui_choice_labels": {
- "euler": "Euler",
- "heun": "Heun (2nd order)",
- "lcm": "LCM"
- }
+ "title": "T2I-Adapter",
+ "ui_order": 7
},
- "seed": {
+ "cfg_rescale_multiplier": {
"default": 0,
- "description": "Randomness seed for reproducibility.",
+ "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
+ "exclusiveMaximum": 1,
"field_kind": "input",
"input": "any",
+ "minimum": 0,
"orig_default": 0,
"orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "title": "CFG Rescale Multiplier",
+ "type": "number"
},
- "vae": {
+ "latents": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "FLUX.2 VAE model (required for BN statistics).",
+ "description": "Latents tensor",
"field_kind": "input",
"input": "connection",
- "orig_required": true
+ "orig_default": null,
+ "orig_required": false,
+ "ui_order": 4
},
- "kontext_conditioning": {
+ "denoise_mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxKontextConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxKontextConditioningField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/DenoiseMaskField"
},
{
"type": "null"
}
],
"default": null,
- "description": "FLUX Kontext conditioning (reference images for multi-reference image editing).",
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Reference Images"
+ "ui_order": 8
},
"type": {
- "const": "flux2_denoise",
- "default": "flux2_denoise",
+ "const": "denoise_latents",
+ "default": "denoise_latents",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "flux", "flux2", "klein", "denoise"],
- "title": "FLUX2 Denoise",
+ "tags": ["latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
+ "title": "Denoise - SD1.5, SDXL",
"type": "object",
- "version": "1.5.0",
+ "version": "1.5.4",
"output": {
"$ref": "#/components/schemas/LatentsOutput"
}
},
- "Flux2KleinLoRACollectionLoader": {
- "category": "model",
+ "DenoiseLatentsMetaInvocation": {
+ "category": "metadata",
"class": "invocation",
- "classification": "prototype",
- "description": "Applies a collection of LoRAs to a FLUX.2 Klein transformer and/or Qwen3 text encoder.",
+ "classification": "stable",
"node_pack": "invokeai",
"properties": {
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -25140,14 +23193,14 @@
"title": "Use Cache",
"type": "boolean"
},
- "loras": {
+ "positive_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/LoRAField"
+ "$ref": "#/components/schemas/ConditioningField"
},
{
"items": {
- "$ref": "#/components/schemas/LoRAField"
+ "$ref": "#/components/schemas/ConditioningField"
},
"type": "array"
},
@@ -25156,506 +23209,362 @@
}
],
"default": null,
- "description": "LoRA models and weights. May be a single LoRA or collection.",
+ "description": "Positive conditioning tensor",
"field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "LoRAs",
- "ui_model_base": ["flux2"],
- "ui_model_type": ["lora"]
+ "input": "connection",
+ "orig_required": true,
+ "title": "Positive Conditioning",
+ "ui_order": 0
},
- "transformer": {
+ "negative_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/ConditioningField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Transformer",
+ "description": "Negative conditioning tensor",
"field_kind": "input",
"input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Transformer"
+ "orig_required": true,
+ "title": "Negative Conditioning",
+ "ui_order": 1
},
- "qwen3_encoder": {
+ "noise": {
"anyOf": [
{
- "$ref": "#/components/schemas/Qwen3EncoderField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Qwen3 tokenizer and text encoder",
+ "description": "Noise tensor",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Qwen3 Encoder"
+ "ui_order": 3
},
- "type": {
- "const": "flux2_klein_lora_collection_loader",
- "default": "flux2_klein_lora_collection_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["lora", "model", "flux", "klein", "flux2"],
- "title": "Apply LoRA Collection - Flux2 Klein",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/Flux2KleinLoRALoaderOutput"
- }
- },
- "Flux2KleinLoRALoaderInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "prototype",
- "description": "Apply a LoRA model to a FLUX.2 Klein transformer and/or Qwen3 text encoder.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "steps": {
+ "default": 10,
+ "description": "Number of steps to run",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 10,
+ "orig_required": false,
+ "title": "Steps",
+ "type": "integer"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "cfg_scale": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 7.5,
+ "description": "Classifier-Free Guidance scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 7.5,
+ "orig_required": false,
+ "title": "CFG Scale"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Denoising Start",
+ "type": "number"
},
- "lora": {
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
+ },
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler to use during inference",
+ "enum": [
+ "ddim",
+ "ddpm",
+ "deis",
+ "deis_k",
+ "lms",
+ "lms_k",
+ "pndm",
+ "heun",
+ "heun_k",
+ "euler",
+ "euler_k",
+ "euler_a",
+ "kdpm_2",
+ "kdpm_2_k",
+ "kdpm_2_a",
+ "kdpm_2_a_k",
+ "dpmpp_2s",
+ "dpmpp_2s_k",
+ "dpmpp_2m",
+ "dpmpp_2m_k",
+ "dpmpp_2m_sde",
+ "dpmpp_2m_sde_k",
+ "dpmpp_3m",
+ "dpmpp_3m_k",
+ "dpmpp_sde",
+ "dpmpp_sde_k",
+ "er_sde",
+ "unipc",
+ "unipc_k",
+ "lcm",
+ "tcd"
+ ],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Scheduler",
+ "type": "string",
+ "ui_type": "SchedulerField"
+ },
+ "unet": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/UNetField"
},
{
"type": "null"
}
],
"default": null,
- "description": "LoRA model to load",
+ "description": "UNet (scheduler, LoRAs)",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_required": true,
- "title": "LoRA",
- "ui_model_base": ["flux2"],
- "ui_model_type": ["lora"]
+ "title": "UNet",
+ "ui_order": 2
},
- "weight": {
- "default": 0.75,
- "description": "The weight at which the LoRA is applied to each model",
+ "control": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ControlField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/ControlField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
"field_kind": "input",
- "input": "any",
- "orig_default": 0.75,
+ "input": "connection",
+ "orig_default": null,
"orig_required": false,
- "title": "Weight",
- "type": "number"
+ "title": "Control",
+ "ui_order": 5
},
- "transformer": {
+ "ip_adapter": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Transformer",
+ "description": "IP-Adapter to apply",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Transformer"
+ "title": "IP-Adapter",
+ "ui_order": 6
},
- "qwen3_encoder": {
+ "t2i_adapter": {
"anyOf": [
{
- "$ref": "#/components/schemas/Qwen3EncoderField"
+ "$ref": "#/components/schemas/T2IAdapterField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/T2IAdapterField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Qwen3 tokenizer and text encoder",
+ "description": "T2I-Adapter(s) to apply",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Qwen3 Encoder"
+ "title": "T2I-Adapter",
+ "ui_order": 7
},
- "type": {
- "const": "flux2_klein_lora_loader",
- "default": "flux2_klein_lora_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["lora", "model", "flux", "klein", "flux2"],
- "title": "Apply LoRA - Flux2 Klein",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/Flux2KleinLoRALoaderOutput"
- }
- },
- "Flux2KleinLoRALoaderOutput": {
- "class": "output",
- "description": "FLUX.2 Klein LoRA Loader Output",
- "properties": {
- "transformer": {
+ "cfg_rescale_multiplier": {
+ "default": 0,
+ "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
+ "exclusiveMaximum": 1,
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "CFG Rescale Multiplier",
+ "type": "number"
+ },
+ "latents": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Transformer",
- "field_kind": "output",
- "title": "Transformer",
- "ui_hidden": false
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_order": 4
},
- "qwen3_encoder": {
+ "denoise_mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/Qwen3EncoderField"
+ "$ref": "#/components/schemas/DenoiseMaskField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "output",
- "title": "Qwen3 Encoder",
- "ui_hidden": false
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_order": 8
},
"type": {
- "const": "flux2_klein_lora_loader_output",
- "default": "flux2_klein_lora_loader_output",
+ "const": "denoise_latents_meta",
+ "default": "denoise_latents_meta",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "transformer", "qwen3_encoder", "type", "type"],
- "title": "Flux2KleinLoRALoaderOutput",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
+ "title": "Denoise - SD1.5, SDXL + Metadata",
+ "type": "object",
+ "version": "1.1.1",
+ "output": {
+ "$ref": "#/components/schemas/LatentsMetaOutput"
+ }
},
- "Flux2KleinModelLoaderInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "prototype",
- "description": "Loads a Flux2 Klein model, outputting its submodels.\n\nFlux2 Klein uses Qwen3 as the text encoder instead of CLIP+T5.\nIt uses a 32-channel VAE (AutoencoderKLFlux2) instead of the 16-channel FLUX.1 VAE.\n\nWhen using a Diffusers format model, both VAE and Qwen3 encoder are extracted\nautomatically from the main model. You can override with standalone models:\n- Transformer: Always from Flux2 Klein main model\n- VAE: From main model (Diffusers) or standalone VAE\n- Qwen3 Encoder: From main model (Diffusers) or standalone Qwen3 model",
- "node_pack": "invokeai",
+ "DenoiseMaskField": {
+ "description": "An inpaint mask field",
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Flux model (Transformer) to load",
- "field_kind": "input",
- "input": "direct",
- "orig_required": true,
- "title": "Transformer",
- "ui_model_base": ["flux2"],
- "ui_model_type": ["main"]
- },
- "vae_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Standalone VAE model. Flux2 Klein uses the same VAE as FLUX (16-channel). If not provided, VAE will be loaded from the Qwen3 Source model.",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "VAE",
- "ui_model_base": ["flux", "flux2"],
- "ui_model_type": ["vae"]
- },
- "qwen3_encoder_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Standalone Qwen3 Encoder model. If not provided, encoder will be loaded from the Qwen3 Source model.",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Qwen3 Encoder",
- "ui_model_type": ["qwen3_encoder"]
+ "mask_name": {
+ "description": "The name of the mask image",
+ "title": "Mask Name",
+ "type": "string"
},
- "qwen3_source_model": {
+ "masked_latents_name": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "Diffusers Flux2 Klein model to extract VAE and/or Qwen3 encoder from. Use this if you don't have separate VAE/Qwen3 models. Ignored if both VAE and Qwen3 Encoder are provided separately.",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Qwen3 Source (Diffusers)",
- "ui_model_base": ["flux2"],
- "ui_model_format": ["diffusers"],
- "ui_model_type": ["main"]
- },
- "max_seq_len": {
- "default": 512,
- "description": "Max sequence length for the Qwen3 encoder.",
- "enum": [256, 512],
- "field_kind": "input",
- "input": "any",
- "orig_default": 512,
- "orig_required": false,
- "title": "Max Seq Length",
- "type": "integer"
+ "description": "The name of the masked image latents",
+ "title": "Masked Latents Name"
},
- "type": {
- "const": "flux2_klein_model_loader",
- "default": "flux2_klein_model_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "gradient": {
+ "default": false,
+ "description": "Used for gradient inpainting",
+ "title": "Gradient",
+ "type": "boolean"
}
},
- "required": ["model", "type", "id"],
- "tags": ["model", "flux", "klein", "qwen3"],
- "title": "Main Model - Flux2 Klein",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/Flux2KleinModelLoaderOutput"
- }
+ "required": ["mask_name"],
+ "title": "DenoiseMaskField",
+ "type": "object"
},
- "Flux2KleinModelLoaderOutput": {
+ "DenoiseMaskOutput": {
"class": "output",
- "description": "Flux2 Klein model loader output.",
+ "description": "Base class for nodes that output a single image",
"properties": {
- "transformer": {
- "$ref": "#/components/schemas/TransformerField",
- "description": "Transformer",
- "field_kind": "output",
- "title": "Transformer",
- "ui_hidden": false
- },
- "qwen3_encoder": {
- "$ref": "#/components/schemas/Qwen3EncoderField",
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "output",
- "title": "Qwen3 Encoder",
- "ui_hidden": false
- },
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
- },
- "max_seq_len": {
- "description": "The max sequence length for the Qwen3 encoder.",
- "enum": [256, 512],
+ "denoise_mask": {
+ "$ref": "#/components/schemas/DenoiseMaskField",
+ "description": "Mask for denoise model run",
"field_kind": "output",
- "title": "Max Seq Length",
- "type": "integer",
"ui_hidden": false
},
"type": {
- "const": "flux2_klein_model_loader_output",
- "default": "flux2_klein_model_loader_output",
+ "const": "denoise_mask_output",
+ "default": "denoise_mask_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "transformer", "qwen3_encoder", "vae", "max_seq_len", "type", "type"],
- "title": "Flux2KleinModelLoaderOutput",
+ "required": ["output_meta", "denoise_mask", "type", "type"],
+ "title": "DenoiseMaskOutput",
"type": "object"
},
- "Flux2KleinTextEncoderInvocation": {
- "category": "prompt",
- "class": "invocation",
- "classification": "prototype",
- "description": "Encodes and preps a prompt for Flux2 Klein image generation.\n\nFlux2 Klein uses Qwen3 as the text encoder, extracting hidden states from\nlayers (9, 18, 27) and stacking them for richer text representations.\nThis matches the diffusers Flux2KleinPipeline implementation exactly.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Text prompt to encode.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Prompt",
- "ui_component": "textarea"
- },
- "qwen3_encoder": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Qwen3EncoderField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Qwen3 tokenizer and text encoder",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Qwen3 Encoder"
- },
- "max_seq_len": {
- "default": 512,
- "description": "Max sequence length for the Qwen3 encoder.",
- "enum": [256, 512],
- "field_kind": "input",
- "input": "any",
- "orig_default": 512,
- "orig_required": false,
- "title": "Max Seq Len",
- "type": "integer"
- },
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TensorField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "A mask defining the region that this conditioning prompt applies to.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
- },
- "type": {
- "const": "flux2_klein_text_encoder",
- "default": "flux2_klein_text_encoder",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["prompt", "conditioning", "flux", "klein", "qwen3"],
- "title": "Prompt - Flux2 Klein",
- "type": "object",
- "version": "1.1.1",
- "output": {
- "$ref": "#/components/schemas/FluxConditioningOutput"
- }
- },
- "Flux2VaeDecodeInvocation": {
- "category": "latents",
+ "DepthAnythingDepthEstimationInvocation": {
+ "category": "controlnet_preprocessors",
"class": "invocation",
- "classification": "prototype",
- "description": "Generates an image from latents using FLUX.2 Klein's 32-channel VAE.",
+ "classification": "stable",
+ "description": "Generates a depth map using a Depth Anything model.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -25714,58 +23623,54 @@
"title": "Use Cache",
"type": "boolean"
},
- "latents": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Latents tensor",
+ "description": "The image to process",
"field_kind": "input",
- "input": "connection",
+ "input": "any",
"orig_required": true
},
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE",
+ "model_size": {
+ "default": "small_v2",
+ "description": "The size of the depth model to use",
+ "enum": ["large", "base", "small", "small_v2"],
"field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "input": "any",
+ "orig_default": "small_v2",
+ "orig_required": false,
+ "title": "Model Size",
+ "type": "string"
},
"type": {
- "const": "flux2_vae_decode",
- "default": "flux2_vae_decode",
+ "const": "depth_anything_depth_estimation",
+ "default": "depth_anything_depth_estimation",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["latents", "image", "vae", "l2i", "flux2", "klein"],
- "title": "Latents to Image - FLUX2",
+ "tags": ["controlnet", "depth", "depth anything"],
+ "title": "Depth Anything Depth Estimation",
"type": "object",
"version": "1.0.0",
"output": {
"$ref": "#/components/schemas/ImageOutput"
}
},
- "Flux2VaeEncodeInvocation": {
- "category": "latents",
+ "DivideInvocation": {
+ "category": "math",
"class": "invocation",
- "classification": "prototype",
- "description": "Encodes an image into latents using FLUX.2 Klein's 32-channel VAE.",
+ "classification": "stable",
+ "description": "Divides two numbers",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -25792,495 +23697,421 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to encode.",
+ "a": {
+ "default": 0,
+ "description": "The first number",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "A",
+ "type": "integer"
},
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE",
+ "b": {
+ "default": 0,
+ "description": "The second number",
"field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "B",
+ "type": "integer"
},
"type": {
- "const": "flux2_vae_encode",
- "default": "flux2_vae_encode",
+ "const": "div",
+ "default": "div",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["latents", "image", "vae", "i2l", "flux2", "klein"],
- "title": "Image to Latents - FLUX2",
+ "tags": ["math", "divide"],
+ "title": "Divide Integers",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/LatentsOutput"
+ "$ref": "#/components/schemas/IntegerOutput"
}
},
- "Flux2VariantType": {
- "type": "string",
- "enum": ["klein_4b", "klein_4b_base", "klein_9b", "klein_9b_base"],
- "title": "Flux2VariantType",
- "description": "FLUX.2 model variants."
- },
- "FluxConditioningCollectionOutput": {
- "class": "output",
- "description": "Base class for nodes that output a collection of conditioning tensors",
+ "DownloadCancelledEvent": {
+ "description": "Event model for download_cancelled",
"properties": {
- "collection": {
- "description": "The output conditioning tensors",
- "field_kind": "output",
- "items": {
- "$ref": "#/components/schemas/FluxConditioningField"
- },
- "title": "Collection",
- "type": "array",
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
- "type": {
- "const": "flux_conditioning_collection_output",
- "default": "flux_conditioning_collection_output",
- "field_kind": "node_attribute",
- "title": "type",
+ "source": {
+ "description": "The source of the download",
+ "title": "Source",
"type": "string"
}
},
- "required": ["output_meta", "collection", "type", "type"],
- "title": "FluxConditioningCollectionOutput",
+ "required": ["timestamp", "source"],
+ "title": "DownloadCancelledEvent",
"type": "object"
},
- "FluxConditioningField": {
- "description": "A conditioning tensor primitive value",
+ "DownloadCompleteEvent": {
+ "description": "Event model for download_complete",
"properties": {
- "conditioning_name": {
- "description": "The name of conditioning tensor",
- "title": "Conditioning Name",
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "source": {
+ "description": "The source of the download",
+ "title": "Source",
"type": "string"
},
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TensorField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True."
+ "download_path": {
+ "description": "The local path where the download is saved",
+ "title": "Download Path",
+ "type": "string"
+ },
+ "total_bytes": {
+ "description": "The total number of bytes downloaded",
+ "title": "Total Bytes",
+ "type": "integer"
}
},
- "required": ["conditioning_name"],
- "title": "FluxConditioningField",
+ "required": ["timestamp", "source", "download_path", "total_bytes"],
+ "title": "DownloadCompleteEvent",
"type": "object"
},
- "FluxConditioningOutput": {
- "class": "output",
- "description": "Base class for nodes that output a single conditioning tensor",
+ "DownloadErrorEvent": {
+ "description": "Event model for download_error",
"properties": {
- "conditioning": {
- "$ref": "#/components/schemas/FluxConditioningField",
- "description": "Conditioning tensor",
- "field_kind": "output",
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
- "type": {
- "const": "flux_conditioning_output",
- "default": "flux_conditioning_output",
- "field_kind": "node_attribute",
- "title": "type",
+ "source": {
+ "description": "The source of the download",
+ "title": "Source",
+ "type": "string"
+ },
+ "error_type": {
+ "description": "The type of error",
+ "title": "Error Type",
+ "type": "string"
+ },
+ "error": {
+ "description": "The error message",
+ "title": "Error",
"type": "string"
}
},
- "required": ["output_meta", "conditioning", "type", "type"],
- "title": "FluxConditioningOutput",
+ "required": ["timestamp", "source", "error_type", "error"],
+ "title": "DownloadErrorEvent",
"type": "object"
},
- "FluxControlLoRALoaderInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "stable",
- "description": "LoRA model and Image to use with FLUX transformer generation.",
- "node_pack": "invokeai",
+ "DownloadJob": {
"properties": {
"id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
+ "type": "integer",
"title": "Id",
- "type": "string"
+ "description": "Numeric ID of this job",
+ "default": -1
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "dest": {
+ "type": "string",
+ "format": "path",
+ "title": "Dest",
+ "description": "Initial destination of downloaded model on local disk; a directory or file path"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "lora": {
+ "download_path": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "type": "string",
+ "format": "path"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Control LoRA model to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Control LoRA",
- "ui_model_base": ["flux"],
- "ui_model_type": ["control_lora"]
+ "title": "Download Path",
+ "description": "Final location of downloaded file or directory"
},
- "image": {
+ "status": {
+ "$ref": "#/components/schemas/DownloadJobStatus",
+ "description": "Status of the download",
+ "default": "waiting"
+ },
+ "bytes": {
+ "type": "integer",
+ "title": "Bytes",
+ "description": "Bytes downloaded so far",
+ "default": 0
+ },
+ "total_bytes": {
+ "type": "integer",
+ "title": "Total Bytes",
+ "description": "Total file size (bytes)",
+ "default": 0
+ },
+ "error_type": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to encode.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "weight": {
- "default": 1.0,
- "description": "The weight of the LoRA.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Weight",
- "type": "number"
- },
- "type": {
- "const": "flux_control_lora_loader",
- "default": "flux_control_lora_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["lora", "model", "flux"],
- "title": "Control LoRA - FLUX",
- "type": "object",
- "version": "1.1.1",
- "output": {
- "$ref": "#/components/schemas/FluxControlLoRALoaderOutput"
- }
- },
- "FluxControlLoRALoaderOutput": {
- "class": "output",
- "description": "Flux Control LoRA Loader Output",
- "properties": {
- "control_lora": {
- "$ref": "#/components/schemas/ControlLoRAField",
- "default": null,
- "description": "Control LoRAs to apply on model loading",
- "field_kind": "output",
- "title": "Flux Control LoRA",
- "ui_hidden": false
- },
- "type": {
- "const": "flux_control_lora_loader_output",
- "default": "flux_control_lora_loader_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "control_lora", "type", "type"],
- "title": "FluxControlLoRALoaderOutput",
- "type": "object"
- },
- "FluxControlNetField": {
- "properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The control image"
- },
- "control_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "The ControlNet model to use"
+ "title": "Error Type",
+ "description": "Name of exception that caused an error"
},
- "control_weight": {
+ "error": {
"anyOf": [
{
- "type": "number"
+ "type": "string"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
],
- "default": 1,
- "description": "The weight given to the ControlNet",
- "title": "Control Weight"
+ "title": "Error",
+ "description": "Traceback of the exception that caused an error"
},
- "begin_step_percent": {
- "default": 0,
- "description": "When the ControlNet is first applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "Begin Step Percent",
- "type": "number"
+ "source": {
+ "type": "string",
+ "minLength": 1,
+ "format": "uri",
+ "title": "Source",
+ "description": "Where to download from. Specific types specified in child classes."
},
- "end_step_percent": {
- "default": 1,
- "description": "When the ControlNet is last applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "End Step Percent",
- "type": "number"
+ "access_token": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Access Token",
+ "description": "authorization token for protected resources"
},
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode to use",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
- "title": "Resize Mode",
- "type": "string"
+ "priority": {
+ "type": "integer",
+ "title": "Priority",
+ "description": "Queue priority; lower values are higher priority",
+ "default": 10
},
- "instantx_control_mode": {
+ "job_started": {
"anyOf": [
{
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": -1,
- "description": "The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'.",
- "title": "Instantx Control Mode"
- }
- },
- "required": ["image", "control_model"],
- "title": "FluxControlNetField",
- "type": "object"
- },
- "FluxControlNetInvocation": {
- "category": "conditioning",
- "class": "invocation",
- "classification": "stable",
- "description": "Collect FLUX ControlNet info to pass to other nodes.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "title": "Job Started",
+ "description": "Timestamp for when the download job started"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "job_ended": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Job Ended",
+ "description": "Timestamp for when the download job ende1d (completed or errored)"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "content_type": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Content Type",
+ "description": "Content type of downloaded file"
},
- "image": {
+ "canonical_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The control image",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Canonical Url",
+ "description": "Canonical URL to request on resume"
},
- "control_model": {
+ "etag": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "ControlNet model to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_model_base": ["flux"],
- "ui_model_type": ["controlnet"]
+ "title": "Etag",
+ "description": "ETag from the remote server, if available"
},
- "control_weight": {
+ "last_modified": {
"anyOf": [
{
- "type": "number"
+ "type": "string"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
],
- "default": 1.0,
- "description": "The weight given to the ControlNet",
- "field_kind": "input",
- "ge": -1,
- "input": "any",
- "le": 2,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Control Weight"
+ "title": "Last Modified",
+ "description": "Last-Modified from the remote server, if available"
},
- "begin_step_percent": {
- "default": 0,
- "description": "When the ControlNet is first applied (% of total steps)",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "Begin Step Percent",
- "type": "number"
+ "final_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Final Url",
+ "description": "Final resolved URL after redirects, if available"
},
- "end_step_percent": {
- "default": 1,
- "description": "When the ControlNet is last applied (% of total steps)",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1,
- "orig_required": false,
- "title": "End Step Percent",
- "type": "number"
+ "expected_total_bytes": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Expected Total Bytes",
+ "description": "Expected total size of the download"
},
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode used",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "just_resize",
- "orig_required": false,
- "title": "Resize Mode",
- "type": "string"
+ "resume_required": {
+ "type": "boolean",
+ "title": "Resume Required",
+ "description": "True if server refused resume; restart required",
+ "default": false
},
- "instantx_control_mode": {
+ "resume_message": {
"anyOf": [
{
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": -1,
- "description": "The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'.",
- "field_kind": "input",
- "input": "any",
- "orig_default": -1,
- "orig_required": false,
- "title": "Instantx Control Mode"
+ "title": "Resume Message",
+ "description": "Message explaining why resume is required"
},
- "type": {
- "const": "flux_controlnet",
- "default": "flux_controlnet",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "resume_from_scratch": {
+ "type": "boolean",
+ "title": "Resume From Scratch",
+ "description": "True if resume metadata existed but the partial file was missing and the download restarted from the beginning",
+ "default": false
}
},
- "required": ["type", "id"],
- "tags": ["controlnet", "flux"],
- "title": "FLUX ControlNet",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/FluxControlNetOutput"
- }
+ "required": ["dest", "source"],
+ "title": "DownloadJob",
+ "description": "Class to monitor and control a model download request."
},
- "FluxControlNetOutput": {
- "class": "output",
- "description": "FLUX ControlNet info",
+ "DownloadJobStatus": {
+ "type": "string",
+ "enum": ["waiting", "running", "paused", "completed", "cancelled", "error"],
+ "title": "DownloadJobStatus",
+ "description": "State of a download job."
+ },
+ "DownloadPausedEvent": {
+ "description": "Event model for download_paused",
"properties": {
- "control": {
- "$ref": "#/components/schemas/FluxControlNetField",
- "description": "ControlNet(s) to apply",
- "field_kind": "output",
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
- "type": {
- "const": "flux_controlnet_output",
- "default": "flux_controlnet_output",
- "field_kind": "node_attribute",
- "title": "type",
+ "source": {
+ "description": "The source of the download",
+ "title": "Source",
"type": "string"
}
},
- "required": ["output_meta", "control", "type", "type"],
- "title": "FluxControlNetOutput",
+ "required": ["timestamp", "source"],
+ "title": "DownloadPausedEvent",
"type": "object"
},
- "FluxDenoiseInvocation": {
- "category": "latents",
+ "DownloadProgressEvent": {
+ "description": "Event model for download_progress",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "source": {
+ "description": "The source of the download",
+ "title": "Source",
+ "type": "string"
+ },
+ "download_path": {
+ "description": "The local path where the download is saved",
+ "title": "Download Path",
+ "type": "string"
+ },
+ "current_bytes": {
+ "description": "The number of bytes downloaded so far",
+ "title": "Current Bytes",
+ "type": "integer"
+ },
+ "total_bytes": {
+ "description": "The total number of bytes to be downloaded",
+ "title": "Total Bytes",
+ "type": "integer"
+ }
+ },
+ "required": ["timestamp", "source", "download_path", "current_bytes", "total_bytes"],
+ "title": "DownloadProgressEvent",
+ "type": "object"
+ },
+ "DownloadStartedEvent": {
+ "description": "Event model for download_started",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "source": {
+ "description": "The source of the download",
+ "title": "Source",
+ "type": "string"
+ },
+ "download_path": {
+ "description": "The local path where the download is saved",
+ "title": "Download Path",
+ "type": "string"
+ }
+ },
+ "required": ["timestamp", "source", "download_path"],
+ "title": "DownloadStartedEvent",
+ "type": "object"
+ },
+ "DynamicPromptInvocation": {
+ "category": "prompt",
"class": "invocation",
"classification": "stable",
- "description": "Run denoising process with a FLUX transformer model.",
+ "description": "Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -26301,650 +24132,781 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": true,
+ "default": false,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
"type": "boolean"
},
- "latents": {
+ "prompt": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "Latents tensor",
+ "description": "The prompt to parse with dynamicprompts",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt",
+ "ui_component": "textarea"
},
- "noise": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LatentsField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Noise tensor",
+ "max_prompts": {
+ "default": 1,
+ "description": "The number of prompts to generate",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Max Prompts",
+ "type": "integer"
},
- "denoise_mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/DenoiseMaskField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
- },
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
- },
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
- },
- "add_noise": {
- "default": true,
- "description": "Add noise based on denoising start.",
+ "combinatorial": {
+ "default": false,
+ "description": "Whether to use the combinatorial generator",
"field_kind": "input",
"input": "any",
- "orig_default": true,
+ "orig_default": false,
"orig_required": false,
- "title": "Add Noise",
+ "title": "Combinatorial",
"type": "boolean"
},
- "transformer": {
+ "type": {
+ "const": "dynamic_prompt",
+ "default": "dynamic_prompt",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "collection"],
+ "title": "Dynamic Prompt",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/StringCollectionOutput"
+ }
+ },
+ "DynamicPromptsResponse": {
+ "properties": {
+ "prompts": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Prompts"
+ },
+ "error": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Flux model (Transformer) to load",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Transformer"
- },
- "control_lora": {
+ "title": "Error"
+ }
+ },
+ "type": "object",
+ "required": ["prompts"],
+ "title": "DynamicPromptsResponse"
+ },
+ "ESRGANInvocation": {
+ "category": "upscale",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Upscales an image using RealESRGAN.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlLoRAField"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Control LoRA model to load",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
"orig_required": false,
- "title": "Control LoRA"
+ "ui_hidden": false
},
- "positive_text_conditioning": {
+ "metadata": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxConditioningField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
"input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
"orig_required": true,
- "title": "Positive Text Conditioning"
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "negative_text_conditioning": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxConditioningField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Negative conditioning tensor. Can be None if cfg_scale is 1.0.",
+ "description": "The input image",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
+ "input": "any",
+ "orig_required": true
+ },
+ "model_name": {
+ "default": "RealESRGAN_x4plus.pth",
+ "description": "The Real-ESRGAN model to use",
+ "enum": [
+ "RealESRGAN_x4plus.pth",
+ "RealESRGAN_x4plus_anime_6B.pth",
+ "ESRGAN_SRx4_DF2KOST_official-ff704c30.pth",
+ "RealESRGAN_x2plus.pth"
+ ],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "RealESRGAN_x4plus.pth",
"orig_required": false,
- "title": "Negative Text Conditioning"
+ "title": "Model Name",
+ "type": "string"
},
- "redux_conditioning": {
+ "tile_size": {
+ "default": 400,
+ "description": "Tile size for tiled ESRGAN upscaling (0=tiling disabled)",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 400,
+ "orig_required": false,
+ "title": "Tile Size",
+ "type": "integer"
+ },
+ "type": {
+ "const": "esrgan",
+ "default": "esrgan",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["esrgan", "upscale"],
+ "title": "Upscale (RealESRGAN)",
+ "type": "object",
+ "version": "1.3.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "Edge": {
+ "properties": {
+ "source": {
+ "$ref": "#/components/schemas/EdgeConnection",
+ "description": "The connection for the edge's from node and field"
+ },
+ "destination": {
+ "$ref": "#/components/schemas/EdgeConnection",
+ "description": "The connection for the edge's to node and field"
+ }
+ },
+ "type": "object",
+ "required": ["source", "destination"],
+ "title": "Edge"
+ },
+ "EdgeConnection": {
+ "properties": {
+ "node_id": {
+ "type": "string",
+ "title": "Node Id",
+ "description": "The id of the node for this edge connection"
+ },
+ "field": {
+ "type": "string",
+ "title": "Field",
+ "description": "The field for this connection"
+ }
+ },
+ "type": "object",
+ "required": ["node_id", "field"],
+ "title": "EdgeConnection"
+ },
+ "EnqueueBatchResult": {
+ "properties": {
+ "queue_id": {
+ "type": "string",
+ "title": "Queue Id",
+ "description": "The ID of the queue"
+ },
+ "enqueued": {
+ "type": "integer",
+ "title": "Enqueued",
+ "description": "The total number of queue items enqueued"
+ },
+ "requested": {
+ "type": "integer",
+ "title": "Requested",
+ "description": "The total number of queue items requested to be enqueued"
+ },
+ "batch": {
+ "$ref": "#/components/schemas/Batch",
+ "description": "The batch that was enqueued"
+ },
+ "priority": {
+ "type": "integer",
+ "title": "Priority",
+ "description": "The priority of the enqueued batch"
+ },
+ "item_ids": {
+ "items": {
+ "type": "integer"
+ },
+ "type": "array",
+ "title": "Item Ids",
+ "description": "The IDs of the queue items that were enqueued"
+ }
+ },
+ "type": "object",
+ "required": ["queue_id", "enqueued", "requested", "batch", "priority", "item_ids"],
+ "title": "EnqueueBatchResult"
+ },
+ "ExpandMaskWithFadeInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Expands a mask with a fade effect. The mask uses black to indicate areas to keep from the generated image and white for areas to discard.\nThe mask is thresholded to create a binary mask, and then a distance transform is applied to create a fade effect.\nThe fade size is specified in pixels, and the mask is expanded by that amount. The result is a mask with a smooth transition from black to white.\nIf the fade size is 0, the mask is returned as-is.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxReduxConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxReduxConditioningField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
"default": null,
- "description": "FLUX Redux conditioning tensor.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
"orig_required": false,
- "title": "Redux Conditioning"
+ "ui_hidden": false
},
- "fill_conditioning": {
+ "metadata": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxFillConditioningField"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
"default": null,
- "description": "FLUX Fill conditioning.",
- "field_kind": "input",
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
"input": "connection",
- "orig_default": null,
- "orig_required": false
+ "orig_required": false,
+ "ui_hidden": false
},
- "cfg_scale": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "mask": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
],
- "default": 1.0,
- "description": "Classifier-Free Guidance scale",
+ "default": null,
+ "description": "The mask to expand",
"field_kind": "input",
"input": "any",
- "orig_default": 1.0,
- "orig_required": false,
- "title": "CFG Scale"
+ "orig_required": true
},
- "cfg_scale_start_step": {
+ "threshold": {
"default": 0,
- "description": "Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step).",
+ "description": "The threshold for the binary mask (0-255)",
"field_kind": "input",
"input": "any",
+ "maximum": 255,
+ "minimum": 0,
"orig_default": 0,
"orig_required": false,
- "title": "CFG Scale Start Step",
- "type": "integer"
- },
- "cfg_scale_end_step": {
- "default": -1,
- "description": "Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step).",
- "field_kind": "input",
- "input": "any",
- "orig_default": -1,
- "orig_required": false,
- "title": "CFG Scale End Step",
- "type": "integer"
- },
- "width": {
- "default": 1024,
- "description": "Width of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
+ "title": "Threshold",
"type": "integer"
},
- "num_steps": {
- "default": 4,
- "description": "Number of diffusion steps. Recommended values are schnell: 4, dev: 50.",
+ "fade_size_px": {
+ "default": 32,
+ "description": "The size of the fade in pixels",
"field_kind": "input",
"input": "any",
- "orig_default": 4,
+ "minimum": 0,
+ "orig_default": 32,
"orig_required": false,
- "title": "Num Steps",
+ "title": "Fade Size Px",
"type": "integer"
},
- "scheduler": {
- "default": "euler",
- "description": "Scheduler (sampler) for the denoising process. 'euler' is fast and standard. 'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
- "enum": ["euler", "heun", "lcm"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "euler",
- "orig_required": false,
- "title": "Scheduler",
+ "type": {
+ "const": "expand_mask_with_fade",
+ "default": "expand_mask_with_fade",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "mask"],
+ "title": "Expand Mask with Fade",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "ExpandPromptRequest": {
+ "properties": {
+ "prompt": {
"type": "string",
- "ui_choice_labels": {
- "euler": "Euler",
- "heun": "Heun (2nd order)",
- "lcm": "LCM"
- }
+ "title": "Prompt"
},
- "guidance": {
- "default": 4.0,
- "description": "The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 4.0,
- "orig_required": false,
- "title": "Guidance",
- "type": "number"
+ "model_key": {
+ "type": "string",
+ "title": "Model Key"
},
- "seed": {
- "default": 0,
- "description": "Randomness seed for reproducibility.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "max_tokens": {
+ "type": "integer",
+ "maximum": 2048.0,
+ "minimum": 1.0,
+ "title": "Max Tokens",
+ "default": 300
},
- "control": {
+ "system_prompt": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxControlNetField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxControlNetField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "ControlNet models.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Control"
+ "title": "System Prompt"
+ }
+ },
+ "type": "object",
+ "required": ["prompt", "model_key"],
+ "title": "ExpandPromptRequest"
+ },
+ "ExpandPromptResponse": {
+ "properties": {
+ "expanded_prompt": {
+ "type": "string",
+ "title": "Expanded Prompt"
},
- "controlnet_vae": {
+ "error": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
- },
- "ip_adapter": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/IPAdapterField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/IPAdapterField"
- },
- "type": "array"
+ "title": "Error"
+ }
+ },
+ "type": "object",
+ "required": ["expanded_prompt"],
+ "title": "ExpandPromptResponse"
+ },
+ "ExposedField": {
+ "properties": {
+ "nodeId": {
+ "type": "string",
+ "title": "Nodeid"
+ },
+ "fieldName": {
+ "type": "string",
+ "title": "Fieldname"
+ }
+ },
+ "type": "object",
+ "required": ["nodeId", "fieldName"],
+ "title": "ExposedField"
+ },
+ "ExternalApiModelConfig": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "default": ""
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "default": ""
+ },
+ "file_size": {
+ "type": "integer",
+ "minimum": 0.0,
+ "title": "File Size",
+ "default": 0
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "IP-Adapter to apply",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "IP-Adapter"
+ "title": "Description",
+ "description": "Model description"
},
- "kontext_conditioning": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "default": ""
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "default": "external"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxKontextConditioningField"
+ "type": "string"
},
{
- "items": {
- "$ref": "#/components/schemas/FluxKontextConditioningField"
- },
- "type": "array"
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "FLUX Kontext conditioning (reference image).",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Kontext Conditioning"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "dype_preset": {
- "default": "off",
- "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. 'area' enables automatically based on image area. '4k' uses optimized settings for 4K output.",
- "enum": ["off", "manual", "auto", "area", "4k"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "off",
- "orig_required": false,
- "title": "Dype Preset",
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "base": {
"type": "string",
- "ui_choice_labels": {
- "4k": "4K Optimized",
- "area": "Area (auto)",
- "auto": "Auto (>1536px)",
- "manual": "Manual",
- "off": "Off"
- },
- "ui_order": 100
+ "const": "external",
+ "title": "Base",
+ "default": "external"
},
- "dype_scale": {
+ "type": {
+ "type": "string",
+ "const": "external_image_generator",
+ "title": "Type",
+ "default": "external_image_generator"
+ },
+ "format": {
+ "type": "string",
+ "const": "external_api",
+ "title": "Format",
+ "default": "external_api"
+ },
+ "provider_id": {
+ "type": "string",
+ "minLength": 1,
+ "title": "Provider Id",
+ "description": "External provider ID"
+ },
+ "provider_model_id": {
+ "type": "string",
+ "minLength": 1,
+ "title": "Provider Model Id",
+ "description": "Provider-specific model ID"
+ },
+ "capabilities": {
+ "$ref": "#/components/schemas/ExternalModelCapabilities",
+ "description": "Provider capability matrix"
+ },
+ "default_settings": {
"anyOf": [
{
- "maximum": 8.0,
- "minimum": 0.0,
- "type": "number"
+ "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "DyPE magnitude (\u03bbs). Higher values = stronger extrapolation. Only used when dype_preset is not 'off'.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Dype Scale",
- "ui_order": 101
+ ]
},
- "dype_exponent": {
+ "panel_schema": {
"anyOf": [
{
- "maximum": 1000.0,
- "minimum": 0.0,
- "type": "number"
+ "$ref": "#/components/schemas/ExternalModelPanelSchema"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "DyPE decay speed (\u03bbt). Controls transition from low to high frequency detail. Only used when dype_preset is not 'off'.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Dype Exponent",
- "ui_order": 102
+ ]
},
- "type": {
- "const": "flux_denoise",
- "default": "flux_denoise",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "flux"],
- "title": "FLUX Denoise",
- "type": "object",
- "version": "4.6.0",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
- },
- "FluxDenoiseLatentsMetaInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "stable",
- "description": "Run denoising process with a FLUX transformer model + metadata.",
- "node_pack": "invokeai",
- "properties": {
- "metadata": {
+ "tags": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "title": "Tags"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
+ "is_default": {
"type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "latents": {
+ "title": "Is Default",
+ "default": false
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "base",
+ "type",
+ "format",
+ "provider_id",
+ "provider_model_id",
+ "capabilities",
+ "default_settings",
+ "panel_schema",
+ "tags",
+ "is_default"
+ ],
+ "title": "ExternalApiModelConfig"
+ },
+ "ExternalApiModelDefaultSettings": {
+ "properties": {
+ "width": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "integer",
+ "exclusiveMinimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Width"
},
- "noise": {
+ "height": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "integer",
+ "exclusiveMinimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "Noise tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Height"
},
- "denoise_mask": {
+ "num_images": {
"anyOf": [
{
- "$ref": "#/components/schemas/DenoiseMaskField"
+ "type": "integer",
+ "exclusiveMinimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Num Images"
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "title": "ExternalApiModelDefaultSettings"
+ },
+ "ExternalImageSize": {
+ "properties": {
+ "width": {
+ "type": "integer",
+ "exclusiveMinimum": 0.0,
+ "title": "Width"
},
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
+ "height": {
+ "type": "integer",
+ "exclusiveMinimum": 0.0,
+ "title": "Height"
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "required": ["width", "height"],
+ "title": "ExternalImageSize"
+ },
+ "ExternalModelCapabilities": {
+ "properties": {
+ "modes": {
+ "items": {
+ "type": "string",
+ "enum": ["txt2img", "img2img", "inpaint"]
+ },
+ "type": "array",
+ "title": "Modes"
},
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
+ "supports_reference_images": {
+ "type": "boolean",
+ "title": "Supports Reference Images",
+ "default": false
},
- "add_noise": {
- "default": true,
- "description": "Add noise based on denoising start.",
- "field_kind": "input",
- "input": "any",
- "orig_default": true,
- "orig_required": false,
- "title": "Add Noise",
- "type": "boolean"
+ "supports_negative_prompt": {
+ "type": "boolean",
+ "title": "Supports Negative Prompt",
+ "default": true
},
- "transformer": {
+ "supports_seed": {
+ "type": "boolean",
+ "title": "Supports Seed",
+ "default": false
+ },
+ "supports_guidance": {
+ "type": "boolean",
+ "title": "Supports Guidance",
+ "default": false
+ },
+ "supports_steps": {
+ "type": "boolean",
+ "title": "Supports Steps",
+ "default": false
+ },
+ "max_images_per_request": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "type": "integer",
+ "exclusiveMinimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "Flux model (Transformer) to load",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Transformer"
+ "title": "Max Images Per Request"
},
- "control_lora": {
+ "max_image_size": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlLoRAField"
+ "$ref": "#/components/schemas/ExternalImageSize"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "Control LoRA model to load",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Control LoRA"
+ ]
},
- "positive_text_conditioning": {
+ "allowed_aspect_ratios": {
"anyOf": [
- {
- "$ref": "#/components/schemas/FluxConditioningField"
- },
{
"items": {
- "$ref": "#/components/schemas/FluxConditioningField"
+ "type": "string"
},
"type": "array"
},
@@ -26952,44 +24914,27 @@
"type": "null"
}
],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Positive Text Conditioning"
+ "title": "Allowed Aspect Ratios"
},
- "negative_text_conditioning": {
+ "aspect_ratio_sizes": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxConditioningField"
+ "additionalProperties": {
+ "$ref": "#/components/schemas/ExternalImageSize"
},
- "type": "array"
+ "type": "object"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Negative conditioning tensor. Can be None if cfg_scale is 1.0.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Negative Text Conditioning"
+ "title": "Aspect Ratio Sizes"
},
- "redux_conditioning": {
+ "resolution_presets": {
"anyOf": [
- {
- "$ref": "#/components/schemas/FluxReduxConditioningField"
- },
{
"items": {
- "$ref": "#/components/schemas/FluxReduxConditioningField"
+ "$ref": "#/components/schemas/ExternalResolutionPreset"
},
"type": "array"
},
@@ -26997,423 +24942,352 @@
"type": "null"
}
],
- "default": null,
- "description": "FLUX Redux conditioning tensor.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Redux Conditioning"
+ "title": "Resolution Presets"
},
- "fill_conditioning": {
+ "max_reference_images": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxFillConditioningField"
+ "type": "integer",
+ "exclusiveMinimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "FLUX Fill conditioning.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Max Reference Images"
},
- "cfg_scale": {
+ "mask_format": {
+ "type": "string",
+ "enum": ["alpha", "binary", "none"],
+ "title": "Mask Format",
+ "default": "none"
+ },
+ "input_image_required_for": {
"anyOf": [
- {
- "type": "number"
- },
{
"items": {
- "type": "number"
+ "type": "string",
+ "enum": ["txt2img", "img2img", "inpaint"]
},
"type": "array"
- }
- ],
- "default": 1.0,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1.0,
- "orig_required": false,
- "title": "CFG Scale"
- },
- "cfg_scale_start_step": {
- "default": 0,
- "description": "Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step).",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "CFG Scale Start Step",
- "type": "integer"
- },
- "cfg_scale_end_step": {
- "default": -1,
- "description": "Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step).",
- "field_kind": "input",
- "input": "any",
- "orig_default": -1,
- "orig_required": false,
- "title": "CFG Scale End Step",
- "type": "integer"
- },
- "width": {
- "default": 1024,
- "description": "Width of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
- },
- "num_steps": {
- "default": 4,
- "description": "Number of diffusion steps. Recommended values are schnell: 4, dev: 50.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 4,
- "orig_required": false,
- "title": "Num Steps",
- "type": "integer"
- },
- "scheduler": {
- "default": "euler",
- "description": "Scheduler (sampler) for the denoising process. 'euler' is fast and standard. 'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
- "enum": ["euler", "heun", "lcm"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "euler",
- "orig_required": false,
- "title": "Scheduler",
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Input Image Required For"
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "title": "ExternalModelCapabilities"
+ },
+ "ExternalModelPanelControl": {
+ "properties": {
+ "name": {
"type": "string",
- "ui_choice_labels": {
- "euler": "Euler",
- "heun": "Heun (2nd order)",
- "lcm": "LCM"
- }
- },
- "guidance": {
- "default": 4.0,
- "description": "The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 4.0,
- "orig_required": false,
- "title": "Guidance",
- "type": "number"
- },
- "seed": {
- "default": 0,
- "description": "Randomness seed for reproducibility.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "enum": ["reference_images", "dimensions", "seed"],
+ "title": "Name"
},
- "control": {
+ "slider_min": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxControlNetField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxControlNetField"
- },
- "type": "array"
+ "type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "ControlNet models.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Control"
+ "title": "Slider Min"
},
- "controlnet_vae": {
+ "slider_max": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Slider Max"
},
- "ip_adapter": {
+ "number_input_min": {
"anyOf": [
{
- "$ref": "#/components/schemas/IPAdapterField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/IPAdapterField"
- },
- "type": "array"
+ "type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "IP-Adapter to apply",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "IP-Adapter"
+ "title": "Number Input Min"
},
- "kontext_conditioning": {
+ "number_input_max": {
"anyOf": [
{
- "$ref": "#/components/schemas/FluxKontextConditioningField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/FluxKontextConditioningField"
- },
- "type": "array"
+ "type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "FLUX Kontext conditioning (reference image).",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Kontext Conditioning"
- },
- "dype_preset": {
- "default": "off",
- "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. 'area' enables automatically based on image area. '4k' uses optimized settings for 4K output.",
- "enum": ["off", "manual", "auto", "area", "4k"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "off",
- "orig_required": false,
- "title": "Dype Preset",
- "type": "string",
- "ui_choice_labels": {
- "4k": "4K Optimized",
- "area": "Area (auto)",
- "auto": "Auto (>1536px)",
- "manual": "Manual",
- "off": "Off"
- },
- "ui_order": 100
+ "title": "Number Input Max"
},
- "dype_scale": {
+ "fine_step": {
"anyOf": [
{
- "maximum": 8.0,
- "minimum": 0.0,
"type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "DyPE magnitude (\u03bbs). Higher values = stronger extrapolation. Only used when dype_preset is not 'off'.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Dype Scale",
- "ui_order": 101
+ "title": "Fine Step"
},
- "dype_exponent": {
+ "coarse_step": {
"anyOf": [
{
- "maximum": 1000.0,
- "minimum": 0.0,
"type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "DyPE decay speed (\u03bbt). Controls transition from low to high frequency detail. Only used when dype_preset is not 'off'.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Dype Exponent",
- "ui_order": 102
+ "title": "Coarse Step"
},
- "type": {
- "const": "flux_denoise_meta",
- "default": "flux_denoise_meta",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "marks": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Marks"
}
},
- "required": ["type", "id"],
- "tags": ["flux", "latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
- "title": "FLUX Denoise + Metadata",
+ "additionalProperties": false,
"type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/LatentsMetaOutput"
- }
+ "required": ["name"],
+ "title": "ExternalModelPanelControl"
},
- "FluxFillConditioningField": {
- "description": "A FLUX Fill conditioning field.",
+ "ExternalModelPanelSchema": {
"properties": {
+ "prompts": {
+ "items": {
+ "$ref": "#/components/schemas/ExternalModelPanelControl"
+ },
+ "type": "array",
+ "title": "Prompts"
+ },
"image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The FLUX Fill reference image."
+ "items": {
+ "$ref": "#/components/schemas/ExternalModelPanelControl"
+ },
+ "type": "array",
+ "title": "Image"
},
- "mask": {
- "$ref": "#/components/schemas/TensorField",
- "description": "The FLUX Fill inpaint mask."
+ "generation": {
+ "items": {
+ "$ref": "#/components/schemas/ExternalModelPanelControl"
+ },
+ "type": "array",
+ "title": "Generation"
}
},
- "required": ["image", "mask"],
- "title": "FluxFillConditioningField",
- "type": "object"
+ "additionalProperties": false,
+ "type": "object",
+ "title": "ExternalModelPanelSchema"
},
- "FluxFillInvocation": {
- "category": "conditioning",
- "class": "invocation",
- "classification": "beta",
- "description": "Prepare the FLUX Fill conditioning data.",
- "node_pack": "invokeai",
+ "ExternalModelSource": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "provider_id": {
+ "type": "string",
+ "title": "Provider Id"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "provider_model_id": {
+ "type": "string",
+ "title": "Provider Model Id"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "type": {
+ "type": "string",
+ "const": "external",
+ "title": "Type",
+ "default": "external"
+ }
+ },
+ "type": "object",
+ "required": ["provider_id", "provider_model_id"],
+ "title": "ExternalModelSource",
+ "description": "An external provider model identifier."
+ },
+ "ExternalProviderConfigModel": {
+ "properties": {
+ "provider_id": {
+ "type": "string",
+ "title": "Provider Id",
+ "description": "The external provider identifier"
},
- "image": {
+ "api_key_configured": {
+ "type": "boolean",
+ "title": "Api Key Configured",
+ "description": "Whether an API key is configured"
+ },
+ "base_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The FLUX Fill reference image.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Base Url",
+ "description": "Optional base URL override"
+ }
+ },
+ "type": "object",
+ "required": ["provider_id", "api_key_configured"],
+ "title": "ExternalProviderConfigModel"
+ },
+ "ExternalProviderConfigUpdate": {
+ "properties": {
+ "api_key": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Api Key",
+ "description": "API key for the external provider"
},
- "mask": {
+ "base_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/TensorField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The bool inpainting mask. Excluded regions should be set to False, included regions should be set to True.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Base Url",
+ "description": "Optional base URL override for the provider"
+ }
+ },
+ "type": "object",
+ "title": "ExternalProviderConfigUpdate"
+ },
+ "ExternalProviderStatusModel": {
+ "properties": {
+ "provider_id": {
+ "type": "string",
+ "title": "Provider Id",
+ "description": "The external provider identifier"
},
- "type": {
- "const": "flux_fill",
- "default": "flux_fill",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "configured": {
+ "type": "boolean",
+ "title": "Configured",
+ "description": "Whether credentials are configured for the provider"
+ },
+ "message": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Message",
+ "description": "Optional provider status detail"
}
},
- "required": ["type", "id"],
- "tags": ["inpaint"],
- "title": "FLUX Fill Conditioning",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/FluxFillOutput"
- }
+ "required": ["provider_id", "configured"],
+ "title": "ExternalProviderStatusModel"
},
- "FluxFillOutput": {
- "class": "output",
- "description": "The conditioning output of a FLUX Fill invocation.",
+ "ExternalResolutionPreset": {
"properties": {
- "fill_cond": {
- "$ref": "#/components/schemas/FluxFillConditioningField",
- "description": "FLUX Redux conditioning tensor",
- "field_kind": "output",
- "title": "Conditioning",
- "ui_hidden": false
+ "label": {
+ "type": "string",
+ "minLength": 1,
+ "title": "Label",
+ "description": "Display label, e.g. '1:1 (1K)'"
},
- "type": {
- "const": "flux_fill_output",
- "default": "flux_fill_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "aspect_ratio": {
+ "type": "string",
+ "minLength": 1,
+ "title": "Aspect Ratio",
+ "description": "Aspect ratio string, e.g. '1:1'"
+ },
+ "image_size": {
+ "type": "string",
+ "minLength": 1,
+ "title": "Image Size",
+ "description": "Image size preset, e.g. '1K'"
+ },
+ "width": {
+ "type": "integer",
+ "exclusiveMinimum": 0.0,
+ "title": "Width"
+ },
+ "height": {
+ "type": "integer",
+ "exclusiveMinimum": 0.0,
+ "title": "Height"
}
},
- "required": ["output_meta", "fill_cond", "type", "type"],
- "title": "FluxFillOutput",
- "type": "object"
+ "additionalProperties": false,
+ "type": "object",
+ "required": ["label", "aspect_ratio", "image_size", "width", "height"],
+ "title": "ExternalResolutionPreset"
},
- "FluxIPAdapterInvocation": {
- "category": "conditioning",
+ "ExtractVideoRangeInvocation": {
+ "category": "video",
"class": "invocation",
- "classification": "stable",
- "description": "Collects FLUX IP-Adapter info to pass to other nodes.",
+ "classification": "prototype",
+ "description": "Trim a video to a contiguous frame range and re-encode as MP4.\n\nBoth bounds are inclusive and 0-based \u2014 ``start_frame=10, end_frame=50``\nemits 41 frames. Negative indices count from the end (``end_frame=-1``\nis the final frame), matching ``video_frame_extract``. The output frame\nrate defaults to the source video's frame rate; set ``fps=0`` to inherit\nit (or 16 fps if the source rate can't be probed).\n\nThe resolved (positive) ``start_frame`` and ``end_frame`` are also emitted as\noutputs, so chained workflows can re-use the boundary indices \u2014 e.g. feeding\nthem into a downstream Frame from Video to extract the same boundary frame.",
"node_pack": "invokeai",
"properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -27438,325 +25312,160 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "video": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/VideoField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The IP-Adapter image prompt(s).",
+ "description": "The video to extract a frame range from.",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "ip_adapter_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The IP-Adapter model.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "IP-Adapter Model",
- "ui_model_base": ["flux"],
- "ui_model_type": ["ip_adapter"]
- },
- "clip_vision_model": {
- "const": "ViT-L",
- "default": "ViT-L",
- "description": "CLIP Vision model to use.",
+ "start_frame": {
+ "default": 0,
+ "description": "First frame to keep, inclusive. 0 = first frame. Negative indices count from the end.",
"field_kind": "input",
"input": "any",
- "orig_default": "ViT-L",
+ "orig_default": 0,
"orig_required": false,
- "title": "Clip Vision Model",
- "type": "string"
+ "title": "Start Frame",
+ "type": "integer",
+ "ui_component": "video-frame-index"
},
- "weight": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "items": {
- "type": "number"
- },
- "type": "array"
- }
- ],
- "default": 1,
- "description": "The weight given to the IP-Adapter",
+ "end_frame": {
+ "default": -1,
+ "description": "Last frame to keep, inclusive. -1 = last frame. Negative indices count from the end.",
"field_kind": "input",
"input": "any",
- "orig_default": 1,
+ "orig_default": -1,
"orig_required": false,
- "title": "Weight"
+ "title": "End Frame",
+ "type": "integer",
+ "ui_component": "video-frame-index"
},
- "begin_step_percent": {
+ "fps": {
"default": 0,
- "description": "When the IP-Adapter is first applied (% of total steps)",
+ "description": "Output frame rate. 0 = match the source video's frame rate (falls back to 16 fps if the source rate can't be probed).",
"field_kind": "input",
"input": "any",
- "maximum": 1,
+ "maximum": 120,
"minimum": 0,
"orig_default": 0,
"orig_required": false,
- "title": "Begin Step Percent",
- "type": "number"
- },
- "end_step_percent": {
- "default": 1,
- "description": "When the IP-Adapter is last applied (% of total steps)",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1,
- "orig_required": false,
- "title": "End Step Percent",
- "type": "number"
+ "title": "Fps",
+ "type": "integer"
},
"type": {
- "const": "flux_ip_adapter",
- "default": "flux_ip_adapter",
+ "const": "extract_video_range",
+ "default": "extract_video_range",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["ip_adapter", "control"],
- "title": "FLUX IP-Adapter",
+ "tags": ["video", "trim", "range", "frames"],
+ "title": "Frame Range from Video",
"type": "object",
- "version": "1.0.0",
+ "version": "1.1.0",
"output": {
- "$ref": "#/components/schemas/IPAdapterOutput"
+ "$ref": "#/components/schemas/ExtractVideoRangeOutput"
}
},
- "FluxKontextConcatenateImagesInvocation": {
- "category": "conditioning",
- "class": "invocation",
- "classification": "stable",
- "description": "Prepares an image or images for use with FLUX Kontext. The first/single image is resized to the nearest\npreferred Kontext resolution. All other images are concatenated horizontally, maintaining their aspect ratio.",
- "node_pack": "invokeai",
+ "ExtractVideoRangeOutput": {
+ "class": "output",
+ "description": "Output of ``extract_video_range``: a trimmed video plus the resolved frame indices.\n\nMirrors ``VideoOutput`` so the video can be piped directly into Concatenate Videos or\nany other ``VideoField``-consuming node, and additionally exposes the resolved\n(positive, clamped) start and end indices so chained workflows can feed them back in\n\u2014 e.g. drive a downstream Frame from Video to pull the same boundary frame.",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
+ "video": {
+ "$ref": "#/components/schemas/VideoField",
+ "description": "The trimmed video",
+ "field_kind": "output",
"ui_hidden": false
},
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
+ "width": {
+ "description": "The width of the video in pixels",
+ "field_kind": "output",
+ "title": "Width",
+ "type": "integer",
"ui_hidden": false
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "images": {
- "anyOf": [
- {
- "items": {
- "$ref": "#/components/schemas/ImageField"
- },
- "maxItems": 10,
- "minItems": 1,
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The images to concatenate",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Images"
- },
- "use_preferred_resolution": {
- "default": true,
- "description": "Use FLUX preferred resolutions for the first image",
- "field_kind": "input",
- "input": "any",
- "orig_default": true,
- "orig_required": false,
- "title": "Use Preferred Resolution",
- "type": "boolean"
+ "height": {
+ "description": "The height of the video in pixels",
+ "field_kind": "output",
+ "title": "Height",
+ "type": "integer",
+ "ui_hidden": false
},
- "type": {
- "const": "flux_kontext_image_prep",
- "default": "flux_kontext_image_prep",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "concatenate", "flux", "kontext"],
- "title": "FLUX Kontext Image Prep",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "FluxKontextConditioningField": {
- "description": "A conditioning field for FLUX Kontext (reference image).",
- "properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The Kontext reference image."
- }
- },
- "required": ["image"],
- "title": "FluxKontextConditioningField",
- "type": "object"
- },
- "FluxKontextInvocation": {
- "category": "conditioning",
- "class": "invocation",
- "classification": "stable",
- "description": "Prepares a reference image for FLUX Kontext conditioning.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "num_frames": {
+ "description": "The number of frames in the trimmed video",
+ "field_kind": "output",
+ "title": "Num Frames",
+ "type": "integer",
+ "ui_hidden": false
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "fps": {
+ "description": "The frames-per-second of the trimmed video",
+ "field_kind": "output",
+ "title": "Fps",
+ "type": "number",
+ "ui_hidden": false
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "duration": {
+ "description": "The duration of the trimmed video in seconds",
+ "field_kind": "output",
+ "title": "Duration",
+ "type": "number",
+ "ui_hidden": false
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The Kontext reference image.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "start_frame": {
+ "description": "The resolved (positive, 0-based) start frame index in the source video",
+ "field_kind": "output",
+ "title": "Start Frame",
+ "type": "integer",
+ "ui_hidden": false
},
- "type": {
- "const": "flux_kontext",
- "default": "flux_kontext",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["conditioning", "kontext", "flux"],
- "title": "Kontext Conditioning - FLUX",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/FluxKontextOutput"
- }
- },
- "FluxKontextOutput": {
- "class": "output",
- "description": "The conditioning output of a FLUX Kontext invocation.",
- "properties": {
- "kontext_cond": {
- "$ref": "#/components/schemas/FluxKontextConditioningField",
- "description": "FLUX Kontext conditioning (reference image)",
+ "end_frame": {
+ "description": "The resolved (positive, 0-based) end frame index in the source video",
"field_kind": "output",
- "title": "Kontext Conditioning",
+ "title": "End Frame",
+ "type": "integer",
"ui_hidden": false
},
"type": {
- "const": "flux_kontext_output",
- "default": "flux_kontext_output",
+ "const": "extract_video_range_output",
+ "default": "extract_video_range_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "kontext_cond", "type", "type"],
- "title": "FluxKontextOutput",
+ "required": [
+ "output_meta",
+ "video",
+ "width",
+ "height",
+ "num_frames",
+ "fps",
+ "duration",
+ "start_frame",
+ "end_frame",
+ "type",
+ "type"
+ ],
+ "title": "ExtractVideoRangeOutput",
"type": "object"
},
- "FluxLoRALoaderInvocation": {
+ "FLUXLoRACollectionLoader": {
"category": "model",
"class": "invocation",
"classification": "stable",
- "description": "Apply a LoRA model to a FLUX transformer and/or text encoder.",
+ "description": "Applies a collection of LoRAs to a FLUX transformer.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -27783,34 +25492,31 @@
"title": "Use Cache",
"type": "boolean"
},
- "lora": {
+ "loras": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "LoRA model to load",
+ "description": "LoRA models and weights. May be a single LoRA or collection.",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "LoRA",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "LoRAs",
"ui_model_base": ["flux"],
"ui_model_type": ["lora"]
},
- "weight": {
- "default": 0.75,
- "description": "The weight at which the LoRA is applied to each model",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.75,
- "orig_required": false,
- "title": "Weight",
- "type": "number"
- },
"transformer": {
"anyOf": [
{
@@ -27826,7 +25532,7 @@
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "FLUX Transformer"
+ "title": "Transformer"
},
"clip": {
"anyOf": [
@@ -27863,8 +25569,8 @@
"title": "T5 Encoder"
},
"type": {
- "const": "flux_lora_loader",
- "default": "flux_lora_loader",
+ "const": "flux_lora_collection_loader",
+ "default": "flux_lora_collection_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -27872,81 +25578,175 @@
},
"required": ["type", "id"],
"tags": ["lora", "model", "flux"],
- "title": "Apply LoRA - FLUX",
+ "title": "Apply LoRA Collection - FLUX",
"type": "object",
- "version": "1.2.1",
+ "version": "1.3.2",
"output": {
"$ref": "#/components/schemas/FluxLoRALoaderOutput"
}
},
- "FluxLoRALoaderOutput": {
- "class": "output",
- "description": "FLUX LoRA Loader Output",
+ "FLUXRedux_Checkpoint_Config": {
"properties": {
- "transformer": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Transformer",
- "field_kind": "output",
- "title": "FLUX Transformer",
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
},
- "clip": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP",
- "ui_hidden": false
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "t5_encoder": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/T5EncoderField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "T5 tokenizer and text encoder",
- "field_kind": "output",
- "title": "T5 Encoder",
- "ui_hidden": false
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
"type": {
- "const": "flux_lora_loader_output",
- "default": "flux_lora_loader_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "flux_redux",
+ "title": "Type",
+ "default": "flux_redux"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ },
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
}
},
- "required": ["output_meta", "transformer", "clip", "t5_encoder", "type", "type"],
- "title": "FluxLoRALoaderOutput",
- "type": "object"
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "format",
+ "base"
+ ],
+ "title": "FLUXRedux_Checkpoint_Config",
+ "description": "Model config for FLUX Tools Redux model."
},
- "FluxModelLoaderInvocation": {
- "category": "model",
+ "FaceIdentifierInvocation": {
+ "category": "segmentation",
"class": "invocation",
"classification": "stable",
- "description": "Loads a flux base model, outputting its submodels.",
+ "description": "Outputs an image with detected face IDs printed on each face. For use with other FaceTools.",
"node_pack": "invokeai",
"properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -27971,175 +25771,262 @@
"title": "Use Cache",
"type": "boolean"
},
- "model": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Flux model (Transformer) to load",
+ "description": "Image to face detect",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "ui_model_base": ["flux"],
- "ui_model_type": ["main"]
+ "orig_required": true
},
- "t5_encoder_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "T5 tokenizer and text encoder",
+ "minimum_confidence": {
+ "default": 0.5,
+ "description": "Minimum confidence for face detection (lower if detection is failing)",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "T5 Encoder",
- "ui_model_type": ["t5_encoder"]
+ "orig_default": 0.5,
+ "orig_required": false,
+ "title": "Minimum Confidence",
+ "type": "number"
},
- "clip_embed_model": {
+ "chunk": {
+ "default": false,
+ "description": "Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Chunk",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "face_identifier",
+ "default": "face_identifier",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "face", "identifier"],
+ "title": "FaceIdentifier",
+ "type": "object",
+ "version": "1.2.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "FaceMaskInvocation": {
+ "category": "segmentation",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Face mask creation using mediapipe face detection",
+ "node_pack": "invokeai",
+ "properties": {
+ "metadata": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
"default": null,
- "description": "CLIP Embed loader",
- "field_kind": "input",
- "input": "any",
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
"orig_required": true,
- "title": "CLIP Embed",
- "ui_model_type": ["clip_embed"]
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "vae_model": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "VAE model to load",
+ "description": "Image to face detect",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "VAE",
- "ui_model_base": ["flux"],
- "ui_model_type": ["vae"]
+ "orig_required": true
+ },
+ "face_ids": {
+ "default": "",
+ "description": "Comma-separated list of face ids to mask eg '0,2,7'. Numbered from 0. Leave empty to mask all. Find face IDs with FaceIdentifier node.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Face Ids",
+ "type": "string"
+ },
+ "minimum_confidence": {
+ "default": 0.5,
+ "description": "Minimum confidence for face detection (lower if detection is failing)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.5,
+ "orig_required": false,
+ "title": "Minimum Confidence",
+ "type": "number"
+ },
+ "x_offset": {
+ "default": 0.0,
+ "description": "Offset for the X-axis of the face mask",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "X Offset",
+ "type": "number"
+ },
+ "y_offset": {
+ "default": 0.0,
+ "description": "Offset for the Y-axis of the face mask",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Y Offset",
+ "type": "number"
+ },
+ "chunk": {
+ "default": false,
+ "description": "Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Chunk",
+ "type": "boolean"
+ },
+ "invert_mask": {
+ "default": false,
+ "description": "Toggle to invert the mask",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Invert Mask",
+ "type": "boolean"
},
"type": {
- "const": "flux_model_loader",
- "default": "flux_model_loader",
+ "const": "face_mask_detection",
+ "default": "face_mask_detection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["model", "flux"],
- "title": "Main Model - FLUX",
+ "tags": ["image", "face", "mask"],
+ "title": "FaceMask",
"type": "object",
- "version": "1.0.7",
+ "version": "1.2.2",
"output": {
- "$ref": "#/components/schemas/FluxModelLoaderOutput"
+ "$ref": "#/components/schemas/FaceMaskOutput"
}
},
- "FluxModelLoaderOutput": {
+ "FaceMaskOutput": {
"class": "output",
- "description": "Flux base model loader output",
+ "description": "Base class for FaceMask output",
"properties": {
- "transformer": {
- "$ref": "#/components/schemas/TransformerField",
- "description": "Transformer",
- "field_kind": "output",
- "title": "Transformer",
- "ui_hidden": false
- },
- "clip": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP",
- "ui_hidden": false
- },
- "t5_encoder": {
- "$ref": "#/components/schemas/T5EncoderField",
- "description": "T5 tokenizer and text encoder",
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The output image",
"field_kind": "output",
- "title": "T5 Encoder",
"ui_hidden": false
},
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
+ "width": {
+ "description": "The width of the image in pixels",
"field_kind": "output",
- "title": "VAE",
+ "title": "Width",
+ "type": "integer",
"ui_hidden": false
},
- "max_seq_len": {
- "description": "The max sequence length to used for the T5 encoder. (256 for schnell transformer, 512 for dev transformer)",
- "enum": [256, 512],
+ "height": {
+ "description": "The height of the image in pixels",
"field_kind": "output",
- "title": "Max Seq Length",
+ "title": "Height",
"type": "integer",
"ui_hidden": false
},
"type": {
- "const": "flux_model_loader_output",
- "default": "flux_model_loader_output",
+ "const": "face_mask_output",
+ "default": "face_mask_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
+ },
+ "mask": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The output mask",
+ "field_kind": "output",
+ "ui_hidden": false
}
},
- "required": ["output_meta", "transformer", "clip", "t5_encoder", "vae", "max_seq_len", "type", "type"],
- "title": "FluxModelLoaderOutput",
+ "required": ["output_meta", "image", "width", "height", "type", "mask", "type"],
+ "title": "FaceMaskOutput",
"type": "object"
},
- "FluxReduxConditioningField": {
- "description": "A FLUX Redux conditioning tensor primitive value",
+ "FaceOffInvocation": {
+ "category": "segmentation",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Bound, extract, and mask a face from an image using MediaPipe detection",
+ "node_pack": "invokeai",
"properties": {
- "conditioning": {
- "$ref": "#/components/schemas/TensorField",
- "description": "The Redux image conditioning tensor."
- },
- "mask": {
+ "metadata": {
"anyOf": [
{
- "$ref": "#/components/schemas/TensorField"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True."
- }
- },
- "required": ["conditioning"],
- "title": "FluxReduxConditioningField",
- "type": "object"
- },
- "FluxReduxInvocation": {
- "category": "conditioning",
- "class": "invocation",
- "classification": "beta",
- "description": "Runs a FLUX Redux model to generate a conditioning tensor.",
- "node_pack": "invokeai",
- "properties": {
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -28174,125 +26061,156 @@
}
],
"default": null,
- "description": "The FLUX Redux image prompt.",
+ "description": "Image for face detection",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TensorField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The bool mask associated with this FLUX Redux image prompt. Excluded regions should be set to False, included regions should be set to True.",
+ "face_id": {
+ "default": 0,
+ "description": "The face ID to process, numbered from 0. Multiple faces not supported. Find a face's ID with FaceIdentifier node.",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Face Id",
+ "type": "integer"
},
- "redux_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The FLUX Redux model to use.",
+ "minimum_confidence": {
+ "default": 0.5,
+ "description": "Minimum confidence for face detection (lower if detection is failing)",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "FLUX Redux Model",
- "ui_model_base": ["flux"],
- "ui_model_type": ["flux_redux"]
+ "orig_default": 0.5,
+ "orig_required": false,
+ "title": "Minimum Confidence",
+ "type": "number"
},
- "downsampling_factor": {
- "default": 1,
- "description": "Redux Downsampling Factor (1-9)",
+ "x_offset": {
+ "default": 0.0,
+ "description": "X-axis offset of the mask",
"field_kind": "input",
"input": "any",
- "maximum": 9,
- "minimum": 1,
- "orig_default": 1,
+ "orig_default": 0.0,
"orig_required": false,
- "title": "Downsampling Factor",
- "type": "integer"
+ "title": "X Offset",
+ "type": "number"
},
- "downsampling_function": {
- "default": "area",
- "description": "Redux Downsampling Function",
- "enum": ["nearest", "bilinear", "bicubic", "area", "nearest-exact"],
+ "y_offset": {
+ "default": 0.0,
+ "description": "Y-axis offset of the mask",
"field_kind": "input",
"input": "any",
- "orig_default": "area",
+ "orig_default": 0.0,
"orig_required": false,
- "title": "Downsampling Function",
- "type": "string"
+ "title": "Y Offset",
+ "type": "number"
},
- "weight": {
- "default": 1.0,
- "description": "Redux weight (0.0-1.0)",
+ "padding": {
+ "default": 0,
+ "description": "All-axis padding around the mask in pixels",
"field_kind": "input",
"input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
+ "orig_default": 0,
"orig_required": false,
- "title": "Weight",
- "type": "number"
+ "title": "Padding",
+ "type": "integer"
+ },
+ "chunk": {
+ "default": false,
+ "description": "Whether to bypass full image face detection and default to image chunking. Chunking will occur if no faces are found in the full image.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Chunk",
+ "type": "boolean"
},
"type": {
- "const": "flux_redux",
- "default": "flux_redux",
+ "const": "face_off",
+ "default": "face_off",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["ip_adapter", "control"],
- "title": "FLUX Redux",
+ "tags": ["image", "faceoff", "face", "mask"],
+ "title": "FaceOff",
"type": "object",
- "version": "2.1.0",
+ "version": "1.2.2",
"output": {
- "$ref": "#/components/schemas/FluxReduxOutput"
+ "$ref": "#/components/schemas/FaceOffOutput"
}
},
- "FluxReduxOutput": {
+ "FaceOffOutput": {
"class": "output",
- "description": "The conditioning output of a FLUX Redux invocation.",
+ "description": "Base class for FaceOff Output",
"properties": {
- "redux_cond": {
- "$ref": "#/components/schemas/FluxReduxConditioningField",
- "description": "FLUX Redux conditioning tensor",
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The output image",
"field_kind": "output",
- "title": "Conditioning",
+ "ui_hidden": false
+ },
+ "width": {
+ "description": "The width of the image in pixels",
+ "field_kind": "output",
+ "title": "Width",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "height": {
+ "description": "The height of the image in pixels",
+ "field_kind": "output",
+ "title": "Height",
+ "type": "integer",
"ui_hidden": false
},
"type": {
- "const": "flux_redux_output",
- "default": "flux_redux_output",
+ "const": "face_off_output",
+ "default": "face_off_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
+ },
+ "mask": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The output mask",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "x": {
+ "description": "The x coordinate of the bounding box's left side",
+ "field_kind": "output",
+ "title": "X",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "y": {
+ "description": "The y coordinate of the bounding box's top side",
+ "field_kind": "output",
+ "title": "Y",
+ "type": "integer",
+ "ui_hidden": false
}
},
- "required": ["output_meta", "redux_cond", "type", "type"],
- "title": "FluxReduxOutput",
+ "required": ["output_meta", "image", "width", "height", "type", "mask", "x", "y", "type"],
+ "title": "FaceOffOutput",
"type": "object"
},
- "FluxTextEncoderInvocation": {
- "category": "prompt",
+ "FieldKind": {
+ "description": "The kind of field.\n- `Input`: An input field on a node.\n- `Output`: An output field on a node.\n- `Internal`: A field which is treated as an input, but cannot be used in node definitions. Metadata is\none example. It is provided to nodes via the WithMetadata class, and we want to reserve the field name\n\"metadata\" for this on all nodes. `FieldKind` is used to short-circuit the field name validation logic,\nallowing \"metadata\" for that field.\n- `NodeAttribute`: The field is a node attribute. These are fields which are not inputs or outputs,\nbut which are used to store information about the node. For example, the `id` and `type` fields are node\nattributes.\n\nThe presence of this in `json_schema_extra[\"field_kind\"]` is used when initializing node schemas on app\nstartup, and when generating the OpenAPI schema for the workflow editor.",
+ "enum": ["input", "output", "internal", "node_attribute"],
+ "title": "FieldKind",
+ "type": "string"
+ },
+ "FloatBatchInvocation": {
+ "category": "batch",
"class": "invocation",
- "classification": "stable",
- "description": "Encodes and preps a prompt for a flux image.",
+ "classification": "special",
+ "description": "Create a batched generation, where the workflow is executed once for each float in the batch.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -28319,144 +26237,61 @@
"title": "Use Cache",
"type": "boolean"
},
- "clip": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/CLIPField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "CLIP"
- },
- "t5_encoder": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/T5EncoderField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "T5 tokenizer and text encoder",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "T5Encoder"
- },
- "t5_max_seq_len": {
- "anyOf": [
- {
- "enum": [256, 512],
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Max sequence length for the T5 encoder. Expected to be 256 for FLUX schnell models and 512 for FLUX dev models.",
+ "batch_group_id": {
+ "default": "None",
+ "description": "The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size.",
+ "enum": ["None", "Group 1", "Group 2", "Group 3", "Group 4", "Group 5"],
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "T5 Max Seq Len"
+ "input": "direct",
+ "orig_default": "None",
+ "orig_required": false,
+ "title": "Batch Group",
+ "type": "string"
},
- "prompt": {
+ "floats": {
"anyOf": [
{
- "type": "string"
+ "items": {
+ "type": "number"
+ },
+ "minItems": 1,
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Text prompt to encode.",
+ "description": "The floats to batch over",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "title": "Prompt",
- "ui_component": "textarea"
- },
- "mask": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TensorField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "A mask defining the region that this conditioning prompt applies to.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
+ "title": "Floats"
},
"type": {
- "const": "flux_text_encoder",
- "default": "flux_text_encoder",
+ "const": "float_batch",
+ "default": "float_batch",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["prompt", "conditioning", "flux"],
- "title": "Prompt - FLUX",
+ "tags": ["primitives", "float", "number", "batch", "special"],
+ "title": "Float Batch",
"type": "object",
- "version": "1.1.2",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/FluxConditioningOutput"
+ "$ref": "#/components/schemas/FloatOutput"
}
},
- "FluxVaeDecodeInvocation": {
- "category": "latents",
+ "FloatCollectionInvocation": {
+ "category": "primitives",
"class": "invocation",
"classification": "stable",
- "description": "Generates an image from latents.",
+ "description": "A collection of float primitive values",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -28481,58 +26316,67 @@
"title": "Use Cache",
"type": "boolean"
},
- "latents": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LatentsField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE",
+ "collection": {
+ "default": [],
+ "description": "The collection of float values",
"field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "input": "any",
+ "items": {
+ "type": "number"
+ },
+ "orig_default": [],
+ "orig_required": false,
+ "title": "Collection",
+ "type": "array"
},
"type": {
- "const": "flux_vae_decode",
- "default": "flux_vae_decode",
+ "const": "float_collection",
+ "default": "float_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["latents", "image", "vae", "l2i", "flux"],
- "title": "Latents to Image - FLUX",
+ "tags": ["primitives", "float", "collection"],
+ "title": "Float Collection Primitive",
"type": "object",
"version": "1.0.2",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/FloatCollectionOutput"
}
},
- "FluxVaeEncodeInvocation": {
- "category": "latents",
+ "FloatCollectionOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a collection of floats",
+ "properties": {
+ "collection": {
+ "description": "The float collection",
+ "field_kind": "output",
+ "items": {
+ "type": "number"
+ },
+ "title": "Collection",
+ "type": "array",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "float_collection_output",
+ "default": "float_collection_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "collection", "type", "type"],
+ "title": "FloatCollectionOutput",
+ "type": "object"
+ },
+ "FloatGenerator": {
+ "category": "batch",
"class": "invocation",
- "classification": "stable",
- "description": "Encodes an image into latents.",
+ "classification": "special",
+ "description": "Generated a range of floats for use in a batched generation",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -28559,117 +26403,125 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to encode.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE",
+ "generator": {
+ "$ref": "#/components/schemas/FloatGeneratorField",
+ "description": "The float generator.",
"field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "input": "direct",
+ "orig_required": true,
+ "title": "Generator Type"
},
"type": {
- "const": "flux_vae_encode",
- "default": "flux_vae_encode",
+ "const": "float_generator",
+ "default": "float_generator",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["latents", "image", "vae", "i2l", "flux"],
- "title": "Image to Latents - FLUX",
+ "required": ["generator", "type", "id"],
+ "tags": ["primitives", "float", "number", "batch", "special"],
+ "title": "Float Generator",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/LatentsOutput"
+ "$ref": "#/components/schemas/FloatGeneratorOutput"
}
},
- "FluxVariantType": {
- "type": "string",
- "enum": ["schnell", "dev", "dev_fill"],
- "title": "FluxVariantType",
- "description": "FLUX.1 model variants."
+ "FloatGeneratorField": {
+ "properties": {},
+ "title": "FloatGeneratorField",
+ "type": "object"
},
- "FoundModel": {
+ "FloatGeneratorOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a collection of floats",
"properties": {
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model"
+ "floats": {
+ "description": "The generated floats",
+ "field_kind": "output",
+ "items": {
+ "type": "number"
+ },
+ "title": "Floats",
+ "type": "array",
+ "ui_hidden": false
},
- "is_installed": {
- "type": "boolean",
- "title": "Is Installed",
- "description": "Whether or not the model is already installed"
+ "type": {
+ "const": "float_generator_output",
+ "default": "float_generator_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "type": "object",
- "required": ["path", "is_installed"],
- "title": "FoundModel"
+ "required": ["output_meta", "floats", "type", "type"],
+ "title": "FloatGeneratorOutput",
+ "type": "object"
},
- "FreeUConfig": {
- "description": "Configuration for the FreeU hyperparameters.\n- https://huggingface.co/docs/diffusers/main/en/using-diffusers/freeu\n- https://github.com/ChenyangSi/FreeU",
+ "FloatInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "A float primitive value",
+ "node_pack": "invokeai",
"properties": {
- "s1": {
- "description": "Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
- "maximum": 3,
- "minimum": -1,
- "title": "S1",
- "type": "number"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "s2": {
- "description": "Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
- "maximum": 3,
- "minimum": -1,
- "title": "S2",
- "type": "number"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "b1": {
- "description": "Scaling factor for stage 1 to amplify the contributions of backbone features.",
- "maximum": 3,
- "minimum": -1,
- "title": "B1",
- "type": "number"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "b2": {
- "description": "Scaling factor for stage 2 to amplify the contributions of backbone features.",
- "maximum": 3,
- "minimum": -1,
- "title": "B2",
+ "value": {
+ "default": 0.0,
+ "description": "The float value",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Value",
"type": "number"
+ },
+ "type": {
+ "const": "float",
+ "default": "float",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "required": ["s1", "s2", "b1", "b2"],
- "title": "FreeUConfig",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["primitives", "float"],
+ "title": "Float Primitive",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/FloatOutput"
+ }
},
- "FreeUInvocation": {
- "category": "model",
+ "FloatLinearRangeInvocation": {
+ "category": "math",
"class": "invocation",
"classification": "stable",
- "description": "Applies FreeU to the UNet. Suggested values (b1/b2/s1/s2):\n\nSD1.5: 1.2/1.4/0.9/0.2,\nSD2: 1.1/1.2/0.9/0.2,\nSDXL: 1.1/1.2/0.6/0.4,",
+ "description": "Creates a range",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -28696,126 +26548,60 @@
"title": "Use Cache",
"type": "boolean"
},
- "unet": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/UNetField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "UNet"
- },
- "b1": {
- "default": 1.2,
- "description": "Scaling factor for stage 1 to amplify the contributions of backbone features.",
- "field_kind": "input",
- "input": "any",
- "maximum": 3,
- "minimum": -1,
- "orig_default": 1.2,
- "orig_required": false,
- "title": "B1",
- "type": "number"
- },
- "b2": {
- "default": 1.4,
- "description": "Scaling factor for stage 2 to amplify the contributions of backbone features.",
+ "start": {
+ "default": 5,
+ "description": "The first value of the range",
"field_kind": "input",
"input": "any",
- "maximum": 3,
- "minimum": -1,
- "orig_default": 1.4,
+ "orig_default": 5,
"orig_required": false,
- "title": "B2",
+ "title": "Start",
"type": "number"
},
- "s1": {
- "default": 0.9,
- "description": "Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
+ "stop": {
+ "default": 10,
+ "description": "The last value of the range",
"field_kind": "input",
"input": "any",
- "maximum": 3,
- "minimum": -1,
- "orig_default": 0.9,
+ "orig_default": 10,
"orig_required": false,
- "title": "S1",
+ "title": "Stop",
"type": "number"
},
- "s2": {
- "default": 0.2,
- "description": "Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
+ "steps": {
+ "default": 30,
+ "description": "number of values to interpolate over (including start and stop)",
"field_kind": "input",
"input": "any",
- "maximum": 3,
- "minimum": -1,
- "orig_default": 0.2,
+ "orig_default": 30,
"orig_required": false,
- "title": "S2",
- "type": "number"
+ "title": "Steps",
+ "type": "integer"
},
"type": {
- "const": "freeu",
- "default": "freeu",
+ "const": "float_range",
+ "default": "float_range",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["freeu"],
- "title": "Apply FreeU - SD1.5, SDXL",
+ "tags": ["math", "range"],
+ "title": "Float Range",
"type": "object",
- "version": "1.0.2",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/UNetOutput"
+ "$ref": "#/components/schemas/FloatCollectionOutput"
}
},
- "GeminiImageGenerationInvocation": {
- "category": "image",
+ "FloatMathInvocation": {
+ "category": "math",
"class": "invocation",
"classification": "stable",
- "description": "Generate images using a Gemini-hosted external model.",
+ "description": "Performs floating point math.",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -28840,239 +26626,480 @@
"title": "Use Cache",
"type": "boolean"
},
- "model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Main model (UNet, VAE, CLIP) to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_model_base": ["external"],
- "ui_model_format": ["external_api"],
- "ui_model_provider_id": ["gemini"],
- "ui_model_type": ["external_image_generator"]
- },
- "mode": {
- "default": "txt2img",
- "description": "Generation mode.",
- "enum": ["txt2img", "img2img", "inpaint"],
+ "operation": {
+ "default": "ADD",
+ "description": "The operation to perform",
+ "enum": ["ADD", "SUB", "MUL", "DIV", "EXP", "ABS", "SQRT", "MIN", "MAX"],
"field_kind": "input",
"input": "any",
- "orig_default": "txt2img",
+ "orig_default": "ADD",
"orig_required": false,
- "title": "Mode",
+ "title": "Operation",
"type": "string",
- "ui_hidden": true
- },
- "prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Prompt",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Prompt"
+ "ui_choice_labels": {
+ "ABS": "Absolute Value of A",
+ "ADD": "Add A+B",
+ "DIV": "Divide A/B",
+ "EXP": "Exponentiate A^B",
+ "MAX": "Maximum(A,B)",
+ "MIN": "Minimum(A,B)",
+ "MUL": "Multiply A*B",
+ "SQRT": "Square Root of A",
+ "SUB": "Subtract A-B"
+ }
},
- "seed": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Seed for random number generation",
+ "a": {
+ "default": 1,
+ "description": "The first number",
"field_kind": "input",
"input": "any",
- "orig_default": null,
+ "orig_default": 1,
"orig_required": false,
- "title": "Seed"
+ "title": "A",
+ "type": "number"
},
- "num_images": {
+ "b": {
"default": 1,
- "description": "Number of images to generate",
- "exclusiveMinimum": 0,
+ "description": "The second number",
"field_kind": "input",
"input": "any",
"orig_default": 1,
"orig_required": false,
- "title": "Num Images",
- "type": "integer"
+ "title": "B",
+ "type": "number"
},
- "width": {
- "default": 1024,
- "description": "Width of output (px)",
- "exclusiveMinimum": 0,
+ "type": {
+ "const": "float_math",
+ "default": "float_math",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": [
+ "math",
+ "float",
+ "add",
+ "subtract",
+ "multiply",
+ "divide",
+ "power",
+ "root",
+ "absolute value",
+ "min",
+ "max"
+ ],
+ "title": "Float Math",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/FloatOutput"
+ }
+ },
+ "FloatOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a single float",
+ "properties": {
+ "value": {
+ "description": "The output float",
+ "field_kind": "output",
+ "title": "Value",
+ "type": "number",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "float_output",
+ "default": "float_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "value", "type", "type"],
+ "title": "FloatOutput",
+ "type": "object"
+ },
+ "FloatToIntegerInvocation": {
+ "category": "math",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Rounds a float number to (a multiple of) an integer.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "value": {
+ "default": 0,
+ "description": "The value to round",
"field_kind": "input",
"input": "any",
- "orig_default": 1024,
+ "orig_default": 0,
"orig_required": false,
- "title": "Width",
- "type": "integer"
+ "title": "Value",
+ "type": "number"
},
- "height": {
- "default": 1024,
- "description": "Height of output (px)",
- "exclusiveMinimum": 0,
+ "multiple": {
+ "default": 1,
+ "description": "The multiple to round to",
"field_kind": "input",
"input": "any",
- "orig_default": 1024,
+ "minimum": 1,
+ "orig_default": 1,
"orig_required": false,
- "title": "Height",
+ "title": "Multiple of",
"type": "integer"
},
- "image_size": {
+ "method": {
+ "default": "Nearest",
+ "description": "The method to use for rounding",
+ "enum": ["Nearest", "Floor", "Ceiling", "Truncate"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "Nearest",
+ "orig_required": false,
+ "title": "Method",
+ "type": "string"
+ },
+ "type": {
+ "const": "float_to_int",
+ "default": "float_to_int",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["math", "round", "integer", "float", "convert"],
+ "title": "Float To Integer",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/IntegerOutput"
+ }
+ },
+ "Flux2DenoiseInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Run denoising process with a FLUX.2 Klein transformer model.\n\nThis node is designed for FLUX.2 Klein models which use Qwen3 as the text encoder.\nIt does not support ControlNet, IP-Adapters, or regional prompting.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Image size preset (e.g. 1K, 2K, 4K)",
+ "description": "Latents tensor",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_default": null,
- "orig_required": false,
- "title": "Image Size"
+ "orig_required": false
},
- "init_image": {
+ "noise": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Init image for img2img/inpaint",
+ "description": "Noise tensor",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_default": null,
- "orig_required": false,
- "ui_hidden": true
+ "orig_required": false
},
- "mask_image": {
+ "denoise_mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/DenoiseMaskField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Mask image for inpaint",
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_default": null,
+ "orig_required": false
+ },
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
"orig_required": false,
- "ui_hidden": true
+ "title": "Denoising Start",
+ "type": "number"
},
- "reference_images": {
- "default": [],
- "description": "Reference images",
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
"field_kind": "input",
"input": "any",
- "items": {
- "$ref": "#/components/schemas/ImageField"
- },
- "orig_default": [],
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
"orig_required": false,
- "title": "Reference Images",
- "type": "array"
+ "title": "Denoising End",
+ "type": "number"
},
- "temperature": {
+ "add_noise": {
+ "default": true,
+ "description": "Add noise based on denoising start.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": true,
+ "orig_required": false,
+ "title": "Add Noise",
+ "type": "boolean"
+ },
+ "transformer": {
"anyOf": [
{
- "maximum": 2.0,
- "minimum": 0.0,
- "type": "number"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Sampling temperature",
+ "description": "Flux model (Transformer) to load",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Transformer"
+ },
+ "positive_text_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "negative_text_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Negative conditioning tensor. Can be None if cfg_scale is 1.0.",
+ "field_kind": "input",
+ "input": "connection",
"orig_default": null,
+ "orig_required": false
+ },
+ "guidance": {
+ "default": 4.0,
+ "description": "Guidance strength for distilled guidance-embedding models. Inert for all current FLUX.2 Klein variants (their guidance_embeds weights are absent/zero); kept for node-graph compatibility and future guidance-embedded models.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 20,
+ "minimum": 0,
+ "orig_default": 4.0,
"orig_required": false,
- "title": "Temperature"
+ "title": "Guidance",
+ "type": "number"
},
- "thinking_level": {
+ "cfg_scale": {
+ "default": 1.0,
+ "description": "Classifier-Free Guidance scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "CFG Scale",
+ "type": "number"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "num_steps": {
+ "default": 4,
+ "description": "Number of diffusion steps. Use 4 for distilled models, 28+ for base models.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 4,
+ "orig_required": false,
+ "title": "Num Steps",
+ "type": "integer"
+ },
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler (sampler) for the denoising process. 'euler' is fast and standard. 'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
+ "enum": ["euler", "heun", "lcm"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Scheduler",
+ "type": "string",
+ "ui_choice_labels": {
+ "euler": "Euler",
+ "heun": "Heun (2nd order)",
+ "lcm": "LCM"
+ }
+ },
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
+ },
+ "vae": {
"anyOf": [
{
- "enum": ["minimal", "high"],
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Thinking level for image generation",
+ "description": "FLUX.2 VAE model (required for BN statistics).",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
+ "orig_required": true
+ },
+ "kontext_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxKontextConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxKontextConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "FLUX Kontext conditioning (reference images for multi-reference image editing).",
+ "field_kind": "input",
+ "input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Thinking Level"
+ "title": "Reference Images"
},
"type": {
- "const": "gemini_image_generation",
- "default": "gemini_image_generation",
+ "const": "flux2_denoise",
+ "default": "flux2_denoise",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["external", "generation", "gemini"],
- "title": "Gemini Image Generation",
+ "tags": ["image", "flux", "flux2", "klein", "denoise"],
+ "title": "FLUX2 Denoise",
"type": "object",
- "version": "1.0.0",
+ "version": "1.5.0",
"output": {
- "$ref": "#/components/schemas/ImageCollectionOutput"
+ "$ref": "#/components/schemas/LatentsOutput"
}
},
- "GeneratePasswordResponse": {
- "properties": {
- "password": {
- "type": "string",
- "title": "Password",
- "description": "Generated strong password"
- }
- },
- "type": "object",
- "required": ["password"],
- "title": "GeneratePasswordResponse",
- "description": "Response containing a generated password."
- },
- "GetMaskBoundingBoxInvocation": {
- "category": "mask",
+ "Flux2KleinLoRACollectionLoader": {
+ "category": "model",
"class": "invocation",
- "classification": "stable",
- "description": "Gets the bounding box of the given mask image.",
+ "classification": "prototype",
+ "description": "Applies a collection of LoRAs to a FLUX.2 Klein transformer and/or Qwen3 text encoder.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -29099,294 +27126,4416 @@
"title": "Use Cache",
"type": "boolean"
},
- "mask": {
+ "loras": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "The mask to crop.",
+ "description": "LoRA models and weights. May be a single LoRA or collection.",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": null,
+ "orig_required": false,
+ "title": "LoRAs",
+ "ui_model_base": ["flux2"],
+ "ui_model_type": ["lora"]
},
- "margin": {
- "default": 0,
- "description": "Margin to add to the bounding box.",
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Transformer",
"field_kind": "input",
- "input": "any",
- "orig_default": 0,
+ "input": "connection",
+ "orig_default": null,
"orig_required": false,
- "title": "Margin",
- "type": "integer"
+ "title": "Transformer"
},
- "mask_color": {
- "$ref": "#/components/schemas/ColorField",
- "default": {
- "r": 255,
- "g": 255,
- "b": 255,
- "a": 255
- },
- "description": "Color of the mask in the image.",
+ "qwen3_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Qwen3EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
"field_kind": "input",
- "input": "any",
- "orig_default": {
- "a": 255,
- "b": 255,
- "g": 255,
- "r": 255
- },
- "orig_required": false
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Qwen3 Encoder"
},
"type": {
- "const": "get_image_mask_bounding_box",
- "default": "get_image_mask_bounding_box",
+ "const": "flux2_klein_lora_collection_loader",
+ "default": "flux2_klein_lora_collection_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["mask"],
- "title": "Get Image Mask Bounding Box",
+ "tags": ["lora", "model", "flux", "klein", "flux2"],
+ "title": "Apply LoRA Collection - Flux2 Klein",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/BoundingBoxOutput"
+ "$ref": "#/components/schemas/Flux2KleinLoRALoaderOutput"
}
},
- "GlmEncoderField": {
+ "Flux2KleinLoRALoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Apply a LoRA model to a FLUX.2 Klein transformer and/or Qwen3 text encoder.",
+ "node_pack": "invokeai",
"properties": {
- "tokenizer": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load tokenizer submodel"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "text_encoder": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load text_encoder submodel"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "lora": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "LoRA model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "LoRA",
+ "ui_model_base": ["flux2"],
+ "ui_model_type": ["lora"]
+ },
+ "weight": {
+ "default": 0.75,
+ "description": "The weight at which the LoRA is applied to each model",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.75,
+ "orig_required": false,
+ "title": "Weight",
+ "type": "number"
+ },
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Transformer"
+ },
+ "qwen3_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Qwen3EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Qwen3 Encoder"
+ },
+ "type": {
+ "const": "flux2_klein_lora_loader",
+ "default": "flux2_klein_lora_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "required": ["tokenizer", "text_encoder"],
- "title": "GlmEncoderField",
+ "required": ["type", "id"],
+ "tags": ["lora", "model", "flux", "klein", "flux2"],
+ "title": "Apply LoRA - Flux2 Klein",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/Flux2KleinLoRALoaderOutput"
+ }
+ },
+ "Flux2KleinLoRALoaderOutput": {
+ "class": "output",
+ "description": "FLUX.2 Klein LoRA Loader Output",
+ "properties": {
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "output",
+ "title": "Transformer",
+ "ui_hidden": false
+ },
+ "qwen3_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Qwen3EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
+ "field_kind": "output",
+ "title": "Qwen3 Encoder",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux2_klein_lora_loader_output",
+ "default": "flux2_klein_lora_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "transformer", "qwen3_encoder", "type", "type"],
+ "title": "Flux2KleinLoRALoaderOutput",
"type": "object"
},
- "GradientMaskOutput": {
+ "Flux2KleinModelLoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Loads a Flux2 Klein model, outputting its submodels.\n\nFlux2 Klein uses Qwen3 as the text encoder instead of CLIP+T5.\nIt uses a 32-channel VAE (AutoencoderKLFlux2) instead of the 16-channel FLUX.1 VAE.\n\nWhen using a Diffusers format model, both VAE and Qwen3 encoder are extracted\nautomatically from the main model. You can override with standalone models:\n- Transformer: Always from Flux2 Klein main model\n- VAE: From main model (Diffusers) or standalone VAE\n- Qwen3 Encoder: From main model (Diffusers) or standalone Qwen3 model",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Flux model (Transformer) to load",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Transformer",
+ "ui_model_base": ["flux2"],
+ "ui_model_type": ["main"]
+ },
+ "vae_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Standalone VAE model. Flux2 Klein uses the same VAE as FLUX (16-channel). If not provided, VAE will be loaded from the Qwen3 Source model.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "VAE",
+ "ui_model_base": ["flux", "flux2"],
+ "ui_model_type": ["vae"]
+ },
+ "qwen3_encoder_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Standalone Qwen3 Encoder model. If not provided, encoder will be loaded from the Qwen3 Source model.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Qwen3 Encoder",
+ "ui_model_type": ["qwen3_encoder"]
+ },
+ "qwen3_source_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Diffusers Flux2 Klein model to extract VAE and/or Qwen3 encoder from. Use this if you don't have separate VAE/Qwen3 models. Ignored if both VAE and Qwen3 Encoder are provided separately.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Qwen3 Source (Diffusers)",
+ "ui_model_base": ["flux2"],
+ "ui_model_format": ["diffusers"],
+ "ui_model_type": ["main"]
+ },
+ "max_seq_len": {
+ "default": 512,
+ "description": "Max sequence length for the Qwen3 encoder.",
+ "enum": [256, 512],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Max Seq Length",
+ "type": "integer"
+ },
+ "type": {
+ "const": "flux2_klein_model_loader",
+ "default": "flux2_klein_model_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["model", "type", "id"],
+ "tags": ["model", "flux", "klein", "qwen3"],
+ "title": "Main Model - Flux2 Klein",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/Flux2KleinModelLoaderOutput"
+ }
+ },
+ "Flux2KleinModelLoaderOutput": {
"class": "output",
- "description": "Outputs a denoise mask and an image representing the total gradient of the mask.",
+ "description": "Flux2 Klein model loader output.",
"properties": {
- "denoise_mask": {
- "$ref": "#/components/schemas/DenoiseMaskField",
- "description": "Mask for denoise model run. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "transformer": {
+ "$ref": "#/components/schemas/TransformerField",
+ "description": "Transformer",
"field_kind": "output",
+ "title": "Transformer",
"ui_hidden": false
},
- "expanded_mask_area": {
- "$ref": "#/components/schemas/ImageField",
- "description": "Image representing the total gradient area of the mask. For paste-back purposes.",
+ "qwen3_encoder": {
+ "$ref": "#/components/schemas/Qwen3EncoderField",
+ "description": "Qwen3 tokenizer and text encoder",
+ "field_kind": "output",
+ "title": "Qwen3 Encoder",
+ "ui_hidden": false
+ },
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
+ },
+ "max_seq_len": {
+ "description": "The max sequence length for the Qwen3 encoder.",
+ "enum": [256, 512],
"field_kind": "output",
+ "title": "Max Seq Length",
+ "type": "integer",
"ui_hidden": false
},
"type": {
- "const": "gradient_mask_output",
- "default": "gradient_mask_output",
+ "const": "flux2_klein_model_loader_output",
+ "default": "flux2_klein_model_loader_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "denoise_mask", "expanded_mask_area", "type", "type"],
- "title": "GradientMaskOutput",
+ "required": ["output_meta", "transformer", "qwen3_encoder", "vae", "max_seq_len", "type", "type"],
+ "title": "Flux2KleinModelLoaderOutput",
"type": "object"
},
- "Graph": {
+ "Flux2KleinTextEncoderInvocation": {
+ "category": "prompt",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Encodes and preps a prompt for Flux2 Klein image generation.\n\nFlux2 Klein uses Qwen3 as the text encoder, extracting hidden states from\nlayers (9, 18, 27) and stacking them for richer text representations.\nThis matches the diffusers Flux2KleinPipeline implementation exactly.",
+ "node_pack": "invokeai",
"properties": {
"id": {
- "type": "string",
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
"title": "Id",
- "description": "The id of this graph"
+ "type": "string"
},
- "nodes": {
- "additionalProperties": {
- "oneOf": [
- {
- "$ref": "#/components/schemas/AddInvocation"
- },
- {
- "$ref": "#/components/schemas/AlibabaCloudImageGenerationInvocation"
- },
- {
- "$ref": "#/components/schemas/AlphaMaskToTensorInvocation"
- },
- {
- "$ref": "#/components/schemas/AnimaDenoiseInvocation"
- },
- {
- "$ref": "#/components/schemas/AnimaImageToLatentsInvocation"
- },
- {
- "$ref": "#/components/schemas/AnimaLLLiteInvocation"
- },
- {
- "$ref": "#/components/schemas/AnimaLatentsToImageInvocation"
- },
- {
- "$ref": "#/components/schemas/AnimaLoRACollectionLoader"
- },
- {
- "$ref": "#/components/schemas/AnimaLoRALoaderInvocation"
- },
- {
- "$ref": "#/components/schemas/AnimaModelLoaderInvocation"
- },
- {
- "$ref": "#/components/schemas/AnimaTextEncoderInvocation"
- },
- {
- "$ref": "#/components/schemas/ApplyMaskTensorToImageInvocation"
- },
- {
- "$ref": "#/components/schemas/ApplyMaskToImageInvocation"
- },
- {
- "$ref": "#/components/schemas/BlankImageInvocation"
- },
- {
- "$ref": "#/components/schemas/BlendLatentsInvocation"
- },
- {
- "$ref": "#/components/schemas/BooleanCollectionInvocation"
- },
- {
- "$ref": "#/components/schemas/BooleanInvocation"
- },
- {
- "$ref": "#/components/schemas/BoundingBoxInvocation"
- },
- {
- "$ref": "#/components/schemas/CLIPSkipInvocation"
- },
- {
- "$ref": "#/components/schemas/CV2InfillInvocation"
- },
- {
- "$ref": "#/components/schemas/CalculateImageTilesEvenSplitInvocation"
- },
- {
- "$ref": "#/components/schemas/CalculateImageTilesInvocation"
- },
- {
- "$ref": "#/components/schemas/CalculateImageTilesMinimumOverlapInvocation"
- },
- {
- "$ref": "#/components/schemas/CallSavedWorkflowInvocation"
- },
- {
- "$ref": "#/components/schemas/CannyEdgeDetectionInvocation"
- },
- {
- "$ref": "#/components/schemas/CanvasOutputInvocation"
- },
- {
- "$ref": "#/components/schemas/CanvasPasteBackInvocation"
- },
- {
- "$ref": "#/components/schemas/CanvasV2MaskAndCropInvocation"
- },
- {
- "$ref": "#/components/schemas/CenterPadCropInvocation"
- },
- {
- "$ref": "#/components/schemas/CogView4DenoiseInvocation"
- },
- {
- "$ref": "#/components/schemas/CogView4ImageToLatentsInvocation"
- },
- {
- "$ref": "#/components/schemas/CogView4LatentsToImageInvocation"
- },
- {
- "$ref": "#/components/schemas/CogView4ModelLoaderInvocation"
- },
- {
- "$ref": "#/components/schemas/CogView4TextEncoderInvocation"
- },
- {
- "$ref": "#/components/schemas/CollectInvocation"
- },
- {
- "$ref": "#/components/schemas/ColorCorrectInvocation"
- },
- {
- "$ref": "#/components/schemas/ColorInvocation"
- },
- {
- "$ref": "#/components/schemas/ColorMapInvocation"
- },
- {
- "$ref": "#/components/schemas/CompelInvocation"
- },
- {
- "$ref": "#/components/schemas/ConditioningCollectionInvocation"
- },
- {
- "$ref": "#/components/schemas/ConditioningInvocation"
- },
- {
- "$ref": "#/components/schemas/ContentShuffleInvocation"
- },
- {
- "$ref": "#/components/schemas/ControlNetInvocation"
- },
- {
- "$ref": "#/components/schemas/CoreMetadataInvocation"
- },
- {
- "$ref": "#/components/schemas/CreateDenoiseMaskInvocation"
- },
- {
- "$ref": "#/components/schemas/CreateGradientMaskInvocation"
- },
- {
- "$ref": "#/components/schemas/CropImageToBoundingBoxInvocation"
- },
- {
- "$ref": "#/components/schemas/CropLatentsCoreInvocation"
- },
- {
- "$ref": "#/components/schemas/CvInpaintInvocation"
- },
- {
- "$ref": "#/components/schemas/DWOpenposeDetectionInvocation"
- },
- {
- "$ref": "#/components/schemas/DecodeInvisibleWatermarkInvocation"
- },
- {
- "$ref": "#/components/schemas/DenoiseLatentsInvocation"
- },
- {
- "$ref": "#/components/schemas/DenoiseLatentsMetaInvocation"
- },
- {
- "$ref": "#/components/schemas/DepthAnythingDepthEstimationInvocation"
- },
- {
- "$ref": "#/components/schemas/DivideInvocation"
- },
- {
- "$ref": "#/components/schemas/DynamicPromptInvocation"
- },
- {
- "$ref": "#/components/schemas/ESRGANInvocation"
- },
- {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Text prompt to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt",
+ "ui_component": "textarea"
+ },
+ "qwen3_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Qwen3EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen3 tokenizer and text encoder",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Qwen3 Encoder"
+ },
+ "max_seq_len": {
+ "default": 512,
+ "description": "Max sequence length for the Qwen3 encoder.",
+ "enum": [256, 512],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Max Seq Len",
+ "type": "integer"
+ },
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TensorField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "A mask defining the region that this conditioning prompt applies to.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "type": {
+ "const": "flux2_klein_text_encoder",
+ "default": "flux2_klein_text_encoder",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "conditioning", "flux", "klein", "qwen3"],
+ "title": "Prompt - Flux2 Klein",
+ "type": "object",
+ "version": "1.1.1",
+ "output": {
+ "$ref": "#/components/schemas/FluxConditioningOutput"
+ }
+ },
+ "Flux2VaeDecodeInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Generates an image from latents using FLUX.2 Klein's 32-channel VAE.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "type": {
+ "const": "flux2_vae_decode",
+ "default": "flux2_vae_decode",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "l2i", "flux2", "klein"],
+ "title": "Latents to Image - FLUX2",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "Flux2VaeEncodeInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Encodes an image into latents using FLUX.2 Klein's 32-channel VAE.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "type": {
+ "const": "flux2_vae_encode",
+ "default": "flux2_vae_encode",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "i2l", "flux2", "klein"],
+ "title": "Image to Latents - FLUX2",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
+ },
+ "Flux2VariantType": {
+ "type": "string",
+ "enum": ["klein_4b", "klein_4b_base", "klein_9b", "klein_9b_base"],
+ "title": "Flux2VariantType",
+ "description": "FLUX.2 model variants."
+ },
+ "FluxConditioningCollectionOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a collection of conditioning tensors",
+ "properties": {
+ "collection": {
+ "description": "The output conditioning tensors",
+ "field_kind": "output",
+ "items": {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ "title": "Collection",
+ "type": "array",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_conditioning_collection_output",
+ "default": "flux_conditioning_collection_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "collection", "type", "type"],
+ "title": "FluxConditioningCollectionOutput",
+ "type": "object"
+ },
+ "FluxConditioningField": {
+ "description": "A conditioning tensor primitive value",
+ "properties": {
+ "conditioning_name": {
+ "description": "The name of conditioning tensor",
+ "title": "Conditioning Name",
+ "type": "string"
+ },
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TensorField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True."
+ }
+ },
+ "required": ["conditioning_name"],
+ "title": "FluxConditioningField",
+ "type": "object"
+ },
+ "FluxConditioningOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a single conditioning tensor",
+ "properties": {
+ "conditioning": {
+ "$ref": "#/components/schemas/FluxConditioningField",
+ "description": "Conditioning tensor",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_conditioning_output",
+ "default": "flux_conditioning_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "conditioning", "type", "type"],
+ "title": "FluxConditioningOutput",
+ "type": "object"
+ },
+ "FluxControlLoRALoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "LoRA model and Image to use with FLUX transformer generation.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "lora": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Control LoRA model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Control LoRA",
+ "ui_model_base": ["flux"],
+ "ui_model_type": ["control_lora"]
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "weight": {
+ "default": 1.0,
+ "description": "The weight of the LoRA.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Weight",
+ "type": "number"
+ },
+ "type": {
+ "const": "flux_control_lora_loader",
+ "default": "flux_control_lora_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["lora", "model", "flux"],
+ "title": "Control LoRA - FLUX",
+ "type": "object",
+ "version": "1.1.1",
+ "output": {
+ "$ref": "#/components/schemas/FluxControlLoRALoaderOutput"
+ }
+ },
+ "FluxControlLoRALoaderOutput": {
+ "class": "output",
+ "description": "Flux Control LoRA Loader Output",
+ "properties": {
+ "control_lora": {
+ "$ref": "#/components/schemas/ControlLoRAField",
+ "default": null,
+ "description": "Control LoRAs to apply on model loading",
+ "field_kind": "output",
+ "title": "Flux Control LoRA",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_control_lora_loader_output",
+ "default": "flux_control_lora_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "control_lora", "type", "type"],
+ "title": "FluxControlLoRALoaderOutput",
+ "type": "object"
+ },
+ "FluxControlNetField": {
+ "properties": {
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The control image"
+ },
+ "control_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "The ControlNet model to use"
+ },
+ "control_weight": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 1,
+ "description": "The weight given to the ControlNet",
+ "title": "Control Weight"
+ },
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the ControlNet is first applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "Begin Step Percent",
+ "type": "number"
+ },
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the ControlNet is last applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "End Step Percent",
+ "type": "number"
+ },
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode to use",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "title": "Resize Mode",
+ "type": "string"
+ },
+ "instantx_control_mode": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": -1,
+ "description": "The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'.",
+ "title": "Instantx Control Mode"
+ }
+ },
+ "required": ["image", "control_model"],
+ "title": "FluxControlNetField",
+ "type": "object"
+ },
+ "FluxControlNetInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Collect FLUX ControlNet info to pass to other nodes.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The control image",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "control_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "ControlNet model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "ui_model_base": ["flux"],
+ "ui_model_type": ["controlnet"]
+ },
+ "control_weight": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 1.0,
+ "description": "The weight given to the ControlNet",
+ "field_kind": "input",
+ "ge": -1,
+ "input": "any",
+ "le": 2,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Control Weight"
+ },
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the ControlNet is first applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Begin Step Percent",
+ "type": "number"
+ },
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the ControlNet is last applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "End Step Percent",
+ "type": "number"
+ },
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode used",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "just_resize",
+ "orig_required": false,
+ "title": "Resize Mode",
+ "type": "string"
+ },
+ "instantx_control_mode": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": -1,
+ "description": "The control mode for InstantX ControlNet union models. Ignored for other ControlNet models. The standard mapping is: canny (0), tile (1), depth (2), blur (3), pose (4), gray (5), low quality (6). Negative values will be treated as 'None'.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": -1,
+ "orig_required": false,
+ "title": "Instantx Control Mode"
+ },
+ "type": {
+ "const": "flux_controlnet",
+ "default": "flux_controlnet",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["controlnet", "flux"],
+ "title": "FLUX ControlNet",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/FluxControlNetOutput"
+ }
+ },
+ "FluxControlNetOutput": {
+ "class": "output",
+ "description": "FLUX ControlNet info",
+ "properties": {
+ "control": {
+ "$ref": "#/components/schemas/FluxControlNetField",
+ "description": "ControlNet(s) to apply",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_controlnet_output",
+ "default": "flux_controlnet_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "control", "type", "type"],
+ "title": "FluxControlNetOutput",
+ "type": "object"
+ },
+ "FluxDenoiseInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Run denoising process with a FLUX transformer model.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "noise": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Noise tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "denoise_mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/DenoiseMaskField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Denoising Start",
+ "type": "number"
+ },
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
+ },
+ "add_noise": {
+ "default": true,
+ "description": "Add noise based on denoising start.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": true,
+ "orig_required": false,
+ "title": "Add Noise",
+ "type": "boolean"
+ },
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Flux model (Transformer) to load",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Transformer"
+ },
+ "control_lora": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ControlLoRAField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Control LoRA model to load",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Control LoRA"
+ },
+ "positive_text_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Positive Text Conditioning"
+ },
+ "negative_text_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Negative conditioning tensor. Can be None if cfg_scale is 1.0.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Negative Text Conditioning"
+ },
+ "redux_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxReduxConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxReduxConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "FLUX Redux conditioning tensor.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Redux Conditioning"
+ },
+ "fill_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxFillConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "FLUX Fill conditioning.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "cfg_scale": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 1.0,
+ "description": "Classifier-Free Guidance scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "CFG Scale"
+ },
+ "cfg_scale_start_step": {
+ "default": 0,
+ "description": "Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step).",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "CFG Scale Start Step",
+ "type": "integer"
+ },
+ "cfg_scale_end_step": {
+ "default": -1,
+ "description": "Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step).",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": -1,
+ "orig_required": false,
+ "title": "CFG Scale End Step",
+ "type": "integer"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "num_steps": {
+ "default": 4,
+ "description": "Number of diffusion steps. Recommended values are schnell: 4, dev: 50.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 4,
+ "orig_required": false,
+ "title": "Num Steps",
+ "type": "integer"
+ },
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler (sampler) for the denoising process. 'euler' is fast and standard. 'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
+ "enum": ["euler", "heun", "lcm"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Scheduler",
+ "type": "string",
+ "ui_choice_labels": {
+ "euler": "Euler",
+ "heun": "Heun (2nd order)",
+ "lcm": "LCM"
+ }
+ },
+ "guidance": {
+ "default": 4.0,
+ "description": "The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 4.0,
+ "orig_required": false,
+ "title": "Guidance",
+ "type": "number"
+ },
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
+ },
+ "control": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxControlNetField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxControlNetField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "ControlNet models.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Control"
+ },
+ "controlnet_vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "ip_adapter": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "IP-Adapter to apply",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "IP-Adapter"
+ },
+ "kontext_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxKontextConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxKontextConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "FLUX Kontext conditioning (reference image).",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Kontext Conditioning"
+ },
+ "dype_preset": {
+ "default": "off",
+ "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. 'area' enables automatically based on image area. '4k' uses optimized settings for 4K output.",
+ "enum": ["off", "manual", "auto", "area", "4k"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "off",
+ "orig_required": false,
+ "title": "Dype Preset",
+ "type": "string",
+ "ui_choice_labels": {
+ "4k": "4K Optimized",
+ "area": "Area (auto)",
+ "auto": "Auto (>1536px)",
+ "manual": "Manual",
+ "off": "Off"
+ },
+ "ui_order": 100
+ },
+ "dype_scale": {
+ "anyOf": [
+ {
+ "maximum": 8.0,
+ "minimum": 0.0,
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "DyPE magnitude (\u03bbs). Higher values = stronger extrapolation. Only used when dype_preset is not 'off'.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Dype Scale",
+ "ui_order": 101
+ },
+ "dype_exponent": {
+ "anyOf": [
+ {
+ "maximum": 1000.0,
+ "minimum": 0.0,
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "DyPE decay speed (\u03bbt). Controls transition from low to high frequency detail. Only used when dype_preset is not 'off'.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Dype Exponent",
+ "ui_order": 102
+ },
+ "type": {
+ "const": "flux_denoise",
+ "default": "flux_denoise",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "flux"],
+ "title": "FLUX Denoise",
+ "type": "object",
+ "version": "4.6.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
+ },
+ "FluxDenoiseLatentsMetaInvocation": {
+ "category": "metadata",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Run denoising process with a FLUX transformer model + metadata.",
+ "node_pack": "invokeai",
+ "properties": {
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "noise": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Noise tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "denoise_mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/DenoiseMaskField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Denoising Start",
+ "type": "number"
+ },
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
+ },
+ "add_noise": {
+ "default": true,
+ "description": "Add noise based on denoising start.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": true,
+ "orig_required": false,
+ "title": "Add Noise",
+ "type": "boolean"
+ },
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Flux model (Transformer) to load",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Transformer"
+ },
+ "control_lora": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ControlLoRAField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Control LoRA model to load",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Control LoRA"
+ },
+ "positive_text_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Positive Text Conditioning"
+ },
+ "negative_text_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Negative conditioning tensor. Can be None if cfg_scale is 1.0.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Negative Text Conditioning"
+ },
+ "redux_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxReduxConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxReduxConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "FLUX Redux conditioning tensor.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Redux Conditioning"
+ },
+ "fill_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxFillConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "FLUX Fill conditioning.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "cfg_scale": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 1.0,
+ "description": "Classifier-Free Guidance scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "CFG Scale"
+ },
+ "cfg_scale_start_step": {
+ "default": 0,
+ "description": "Index of the first step to apply cfg_scale. Negative indices count backwards from the the last step (e.g. a value of -1 refers to the final step).",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "CFG Scale Start Step",
+ "type": "integer"
+ },
+ "cfg_scale_end_step": {
+ "default": -1,
+ "description": "Index of the last step to apply cfg_scale. Negative indices count backwards from the last step (e.g. a value of -1 refers to the final step).",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": -1,
+ "orig_required": false,
+ "title": "CFG Scale End Step",
+ "type": "integer"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "num_steps": {
+ "default": 4,
+ "description": "Number of diffusion steps. Recommended values are schnell: 4, dev: 50.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 4,
+ "orig_required": false,
+ "title": "Num Steps",
+ "type": "integer"
+ },
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler (sampler) for the denoising process. 'euler' is fast and standard. 'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
+ "enum": ["euler", "heun", "lcm"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Scheduler",
+ "type": "string",
+ "ui_choice_labels": {
+ "euler": "Euler",
+ "heun": "Heun (2nd order)",
+ "lcm": "LCM"
+ }
+ },
+ "guidance": {
+ "default": 4.0,
+ "description": "The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 4.0,
+ "orig_required": false,
+ "title": "Guidance",
+ "type": "number"
+ },
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
+ },
+ "control": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxControlNetField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxControlNetField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "ControlNet models.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Control"
+ },
+ "controlnet_vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "ip_adapter": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "IP-Adapter to apply",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "IP-Adapter"
+ },
+ "kontext_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/FluxKontextConditioningField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/FluxKontextConditioningField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "FLUX Kontext conditioning (reference image).",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Kontext Conditioning"
+ },
+ "dype_preset": {
+ "default": "off",
+ "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. 'area' enables automatically based on image area. '4k' uses optimized settings for 4K output.",
+ "enum": ["off", "manual", "auto", "area", "4k"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "off",
+ "orig_required": false,
+ "title": "Dype Preset",
+ "type": "string",
+ "ui_choice_labels": {
+ "4k": "4K Optimized",
+ "area": "Area (auto)",
+ "auto": "Auto (>1536px)",
+ "manual": "Manual",
+ "off": "Off"
+ },
+ "ui_order": 100
+ },
+ "dype_scale": {
+ "anyOf": [
+ {
+ "maximum": 8.0,
+ "minimum": 0.0,
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "DyPE magnitude (\u03bbs). Higher values = stronger extrapolation. Only used when dype_preset is not 'off'.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Dype Scale",
+ "ui_order": 101
+ },
+ "dype_exponent": {
+ "anyOf": [
+ {
+ "maximum": 1000.0,
+ "minimum": 0.0,
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "DyPE decay speed (\u03bbt). Controls transition from low to high frequency detail. Only used when dype_preset is not 'off'.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Dype Exponent",
+ "ui_order": 102
+ },
+ "type": {
+ "const": "flux_denoise_meta",
+ "default": "flux_denoise_meta",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["flux", "latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
+ "title": "FLUX Denoise + Metadata",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/LatentsMetaOutput"
+ }
+ },
+ "FluxFillConditioningField": {
+ "description": "A FLUX Fill conditioning field.",
+ "properties": {
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The FLUX Fill reference image."
+ },
+ "mask": {
+ "$ref": "#/components/schemas/TensorField",
+ "description": "The FLUX Fill inpaint mask."
+ }
+ },
+ "required": ["image", "mask"],
+ "title": "FluxFillConditioningField",
+ "type": "object"
+ },
+ "FluxFillInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "beta",
+ "description": "Prepare the FLUX Fill conditioning data.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The FLUX Fill reference image.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TensorField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The bool inpainting mask. Excluded regions should be set to False, included regions should be set to True.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "type": {
+ "const": "flux_fill",
+ "default": "flux_fill",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["inpaint"],
+ "title": "FLUX Fill Conditioning",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/FluxFillOutput"
+ }
+ },
+ "FluxFillOutput": {
+ "class": "output",
+ "description": "The conditioning output of a FLUX Fill invocation.",
+ "properties": {
+ "fill_cond": {
+ "$ref": "#/components/schemas/FluxFillConditioningField",
+ "description": "FLUX Redux conditioning tensor",
+ "field_kind": "output",
+ "title": "Conditioning",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_fill_output",
+ "default": "flux_fill_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "fill_cond", "type", "type"],
+ "title": "FluxFillOutput",
+ "type": "object"
+ },
+ "FluxIPAdapterInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Collects FLUX IP-Adapter info to pass to other nodes.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The IP-Adapter image prompt(s).",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "ip_adapter_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The IP-Adapter model.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "IP-Adapter Model",
+ "ui_model_base": ["flux"],
+ "ui_model_type": ["ip_adapter"]
+ },
+ "clip_vision_model": {
+ "const": "ViT-L",
+ "default": "ViT-L",
+ "description": "CLIP Vision model to use.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "ViT-L",
+ "orig_required": false,
+ "title": "Clip Vision Model",
+ "type": "string"
+ },
+ "weight": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 1,
+ "description": "The weight given to the IP-Adapter",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Weight"
+ },
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the IP-Adapter is first applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Begin Step Percent",
+ "type": "number"
+ },
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the IP-Adapter is last applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "End Step Percent",
+ "type": "number"
+ },
+ "type": {
+ "const": "flux_ip_adapter",
+ "default": "flux_ip_adapter",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["ip_adapter", "control"],
+ "title": "FLUX IP-Adapter",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/IPAdapterOutput"
+ }
+ },
+ "FluxKontextConcatenateImagesInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Prepares an image or images for use with FLUX Kontext. The first/single image is resized to the nearest\npreferred Kontext resolution. All other images are concatenated horizontally, maintaining their aspect ratio.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "images": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ "maxItems": 10,
+ "minItems": 1,
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The images to concatenate",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Images"
+ },
+ "use_preferred_resolution": {
+ "default": true,
+ "description": "Use FLUX preferred resolutions for the first image",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": true,
+ "orig_required": false,
+ "title": "Use Preferred Resolution",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "flux_kontext_image_prep",
+ "default": "flux_kontext_image_prep",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "concatenate", "flux", "kontext"],
+ "title": "FLUX Kontext Image Prep",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "FluxKontextConditioningField": {
+ "description": "A conditioning field for FLUX Kontext (reference image).",
+ "properties": {
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The Kontext reference image."
+ }
+ },
+ "required": ["image"],
+ "title": "FluxKontextConditioningField",
+ "type": "object"
+ },
+ "FluxKontextInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Prepares a reference image for FLUX Kontext conditioning.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The Kontext reference image.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "type": {
+ "const": "flux_kontext",
+ "default": "flux_kontext",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["conditioning", "kontext", "flux"],
+ "title": "Kontext Conditioning - FLUX",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/FluxKontextOutput"
+ }
+ },
+ "FluxKontextOutput": {
+ "class": "output",
+ "description": "The conditioning output of a FLUX Kontext invocation.",
+ "properties": {
+ "kontext_cond": {
+ "$ref": "#/components/schemas/FluxKontextConditioningField",
+ "description": "FLUX Kontext conditioning (reference image)",
+ "field_kind": "output",
+ "title": "Kontext Conditioning",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_kontext_output",
+ "default": "flux_kontext_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "kontext_cond", "type", "type"],
+ "title": "FluxKontextOutput",
+ "type": "object"
+ },
+ "FluxLoRALoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Apply a LoRA model to a FLUX transformer and/or text encoder.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "lora": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "LoRA model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "LoRA",
+ "ui_model_base": ["flux"],
+ "ui_model_type": ["lora"]
+ },
+ "weight": {
+ "default": 0.75,
+ "description": "The weight at which the LoRA is applied to each model",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.75,
+ "orig_required": false,
+ "title": "Weight",
+ "type": "number"
+ },
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "FLUX Transformer"
+ },
+ "clip": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "CLIP"
+ },
+ "t5_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/T5EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "T5 tokenizer and text encoder",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "T5 Encoder"
+ },
+ "type": {
+ "const": "flux_lora_loader",
+ "default": "flux_lora_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["lora", "model", "flux"],
+ "title": "Apply LoRA - FLUX",
+ "type": "object",
+ "version": "1.2.1",
+ "output": {
+ "$ref": "#/components/schemas/FluxLoRALoaderOutput"
+ }
+ },
+ "FluxLoRALoaderOutput": {
+ "class": "output",
+ "description": "FLUX LoRA Loader Output",
+ "properties": {
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "output",
+ "title": "FLUX Transformer",
+ "ui_hidden": false
+ },
+ "clip": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP",
+ "ui_hidden": false
+ },
+ "t5_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/T5EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "T5 tokenizer and text encoder",
+ "field_kind": "output",
+ "title": "T5 Encoder",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_lora_loader_output",
+ "default": "flux_lora_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "transformer", "clip", "t5_encoder", "type", "type"],
+ "title": "FluxLoRALoaderOutput",
+ "type": "object"
+ },
+ "FluxModelLoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Loads a flux base model, outputting its submodels.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Flux model (Transformer) to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "ui_model_base": ["flux"],
+ "ui_model_type": ["main"]
+ },
+ "t5_encoder_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "T5 tokenizer and text encoder",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "T5 Encoder",
+ "ui_model_type": ["t5_encoder"]
+ },
+ "clip_embed_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP Embed loader",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "CLIP Embed",
+ "ui_model_type": ["clip_embed"]
+ },
+ "vae_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "VAE",
+ "ui_model_base": ["flux"],
+ "ui_model_type": ["vae"]
+ },
+ "type": {
+ "const": "flux_model_loader",
+ "default": "flux_model_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["model", "flux"],
+ "title": "Main Model - FLUX",
+ "type": "object",
+ "version": "1.0.7",
+ "output": {
+ "$ref": "#/components/schemas/FluxModelLoaderOutput"
+ }
+ },
+ "FluxModelLoaderOutput": {
+ "class": "output",
+ "description": "Flux base model loader output",
+ "properties": {
+ "transformer": {
+ "$ref": "#/components/schemas/TransformerField",
+ "description": "Transformer",
+ "field_kind": "output",
+ "title": "Transformer",
+ "ui_hidden": false
+ },
+ "clip": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP",
+ "ui_hidden": false
+ },
+ "t5_encoder": {
+ "$ref": "#/components/schemas/T5EncoderField",
+ "description": "T5 tokenizer and text encoder",
+ "field_kind": "output",
+ "title": "T5 Encoder",
+ "ui_hidden": false
+ },
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
+ },
+ "max_seq_len": {
+ "description": "The max sequence length to used for the T5 encoder. (256 for schnell transformer, 512 for dev transformer)",
+ "enum": [256, 512],
+ "field_kind": "output",
+ "title": "Max Seq Length",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_model_loader_output",
+ "default": "flux_model_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "transformer", "clip", "t5_encoder", "vae", "max_seq_len", "type", "type"],
+ "title": "FluxModelLoaderOutput",
+ "type": "object"
+ },
+ "FluxReduxConditioningField": {
+ "description": "A FLUX Redux conditioning tensor primitive value",
+ "properties": {
+ "conditioning": {
+ "$ref": "#/components/schemas/TensorField",
+ "description": "The Redux image conditioning tensor."
+ },
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TensorField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The mask associated with this conditioning tensor. Excluded regions should be set to False, included regions should be set to True."
+ }
+ },
+ "required": ["conditioning"],
+ "title": "FluxReduxConditioningField",
+ "type": "object"
+ },
+ "FluxReduxInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "beta",
+ "description": "Runs a FLUX Redux model to generate a conditioning tensor.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The FLUX Redux image prompt.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TensorField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The bool mask associated with this FLUX Redux image prompt. Excluded regions should be set to False, included regions should be set to True.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "redux_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The FLUX Redux model to use.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "FLUX Redux Model",
+ "ui_model_base": ["flux"],
+ "ui_model_type": ["flux_redux"]
+ },
+ "downsampling_factor": {
+ "default": 1,
+ "description": "Redux Downsampling Factor (1-9)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 9,
+ "minimum": 1,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Downsampling Factor",
+ "type": "integer"
+ },
+ "downsampling_function": {
+ "default": "area",
+ "description": "Redux Downsampling Function",
+ "enum": ["nearest", "bilinear", "bicubic", "area", "nearest-exact"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "area",
+ "orig_required": false,
+ "title": "Downsampling Function",
+ "type": "string"
+ },
+ "weight": {
+ "default": 1.0,
+ "description": "Redux weight (0.0-1.0)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Weight",
+ "type": "number"
+ },
+ "type": {
+ "const": "flux_redux",
+ "default": "flux_redux",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["ip_adapter", "control"],
+ "title": "FLUX Redux",
+ "type": "object",
+ "version": "2.1.0",
+ "output": {
+ "$ref": "#/components/schemas/FluxReduxOutput"
+ }
+ },
+ "FluxReduxOutput": {
+ "class": "output",
+ "description": "The conditioning output of a FLUX Redux invocation.",
+ "properties": {
+ "redux_cond": {
+ "$ref": "#/components/schemas/FluxReduxConditioningField",
+ "description": "FLUX Redux conditioning tensor",
+ "field_kind": "output",
+ "title": "Conditioning",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "flux_redux_output",
+ "default": "flux_redux_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "redux_cond", "type", "type"],
+ "title": "FluxReduxOutput",
+ "type": "object"
+ },
+ "FluxTextEncoderInvocation": {
+ "category": "prompt",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Encodes and preps a prompt for a flux image.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "clip": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "CLIP"
+ },
+ "t5_encoder": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/T5EncoderField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "T5 tokenizer and text encoder",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "T5Encoder"
+ },
+ "t5_max_seq_len": {
+ "anyOf": [
+ {
+ "enum": [256, 512],
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Max sequence length for the T5 encoder. Expected to be 256 for FLUX schnell models and 512 for FLUX dev models.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "T5 Max Seq Len"
+ },
+ "prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Text prompt to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt",
+ "ui_component": "textarea"
+ },
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TensorField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "A mask defining the region that this conditioning prompt applies to.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "type": {
+ "const": "flux_text_encoder",
+ "default": "flux_text_encoder",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "conditioning", "flux"],
+ "title": "Prompt - FLUX",
+ "type": "object",
+ "version": "1.1.2",
+ "output": {
+ "$ref": "#/components/schemas/FluxConditioningOutput"
+ }
+ },
+ "FluxVaeDecodeInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generates an image from latents.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "type": {
+ "const": "flux_vae_decode",
+ "default": "flux_vae_decode",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "l2i", "flux"],
+ "title": "Latents to Image - FLUX",
+ "type": "object",
+ "version": "1.0.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "FluxVaeEncodeInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Encodes an image into latents.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "type": {
+ "const": "flux_vae_encode",
+ "default": "flux_vae_encode",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "i2l", "flux"],
+ "title": "Image to Latents - FLUX",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
+ },
+ "FluxVariantType": {
+ "type": "string",
+ "enum": ["schnell", "dev", "dev_fill"],
+ "title": "FluxVariantType",
+ "description": "FLUX.1 model variants."
+ },
+ "FoundModel": {
+ "properties": {
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model"
+ },
+ "is_installed": {
+ "type": "boolean",
+ "title": "Is Installed",
+ "description": "Whether or not the model is already installed"
+ }
+ },
+ "type": "object",
+ "required": ["path", "is_installed"],
+ "title": "FoundModel"
+ },
+ "FreeUConfig": {
+ "description": "Configuration for the FreeU hyperparameters.\n- https://huggingface.co/docs/diffusers/main/en/using-diffusers/freeu\n- https://github.com/ChenyangSi/FreeU",
+ "properties": {
+ "s1": {
+ "description": "Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
+ "maximum": 3,
+ "minimum": -1,
+ "title": "S1",
+ "type": "number"
+ },
+ "s2": {
+ "description": "Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
+ "maximum": 3,
+ "minimum": -1,
+ "title": "S2",
+ "type": "number"
+ },
+ "b1": {
+ "description": "Scaling factor for stage 1 to amplify the contributions of backbone features.",
+ "maximum": 3,
+ "minimum": -1,
+ "title": "B1",
+ "type": "number"
+ },
+ "b2": {
+ "description": "Scaling factor for stage 2 to amplify the contributions of backbone features.",
+ "maximum": 3,
+ "minimum": -1,
+ "title": "B2",
+ "type": "number"
+ }
+ },
+ "required": ["s1", "s2", "b1", "b2"],
+ "title": "FreeUConfig",
+ "type": "object"
+ },
+ "FreeUInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Applies FreeU to the UNet. Suggested values (b1/b2/s1/s2):\n\nSD1.5: 1.2/1.4/0.9/0.2,\nSD2: 1.1/1.2/0.9/0.2,\nSDXL: 1.1/1.2/0.6/0.4,",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "unet": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/UNetField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "UNet"
+ },
+ "b1": {
+ "default": 1.2,
+ "description": "Scaling factor for stage 1 to amplify the contributions of backbone features.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 3,
+ "minimum": -1,
+ "orig_default": 1.2,
+ "orig_required": false,
+ "title": "B1",
+ "type": "number"
+ },
+ "b2": {
+ "default": 1.4,
+ "description": "Scaling factor for stage 2 to amplify the contributions of backbone features.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 3,
+ "minimum": -1,
+ "orig_default": 1.4,
+ "orig_required": false,
+ "title": "B2",
+ "type": "number"
+ },
+ "s1": {
+ "default": 0.9,
+ "description": "Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 3,
+ "minimum": -1,
+ "orig_default": 0.9,
+ "orig_required": false,
+ "title": "S1",
+ "type": "number"
+ },
+ "s2": {
+ "default": 0.2,
+ "description": "Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the \"oversmoothing effect\" in the enhanced denoising process.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 3,
+ "minimum": -1,
+ "orig_default": 0.2,
+ "orig_required": false,
+ "title": "S2",
+ "type": "number"
+ },
+ "type": {
+ "const": "freeu",
+ "default": "freeu",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["freeu"],
+ "title": "Apply FreeU - SD1.5, SDXL",
+ "type": "object",
+ "version": "1.0.2",
+ "output": {
+ "$ref": "#/components/schemas/UNetOutput"
+ }
+ },
+ "GalleryItem": {
+ "properties": {
+ "kind": {
+ "$ref": "#/components/schemas/GalleryItemKind",
+ "description": "Whether the item is an image or video."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "The unique name of the image or video."
+ },
+ "full_url": {
+ "type": "string",
+ "title": "Full Url",
+ "description": "URL to the full-resolution image PNG or the full-quality video MP4."
+ },
+ "thumbnail_url": {
+ "type": "string",
+ "title": "Thumbnail Url",
+ "description": "URL to the static (WebP) thumbnail."
+ },
+ "width": {
+ "type": "integer",
+ "title": "Width",
+ "description": "The width of the item in pixels."
+ },
+ "height": {
+ "type": "integer",
+ "title": "Height",
+ "description": "The height of the item in pixels."
+ },
+ "category": {
+ "$ref": "#/components/schemas/ImageCategory",
+ "description": "The category of the item (images and videos share the same enum)."
+ },
+ "starred": {
+ "type": "boolean",
+ "title": "Starred",
+ "description": "Whether the item is starred."
+ },
+ "is_intermediate": {
+ "type": "boolean",
+ "title": "Is Intermediate",
+ "description": "Whether the item is an intermediate output."
+ },
+ "board_id": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Board Id",
+ "description": "Owning board id, if any."
+ },
+ "created_at": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "string"
+ }
+ ],
+ "title": "Created At",
+ "description": "The created timestamp of the item."
+ },
+ "duration": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Duration",
+ "description": "Video duration in seconds. None for images."
+ },
+ "fps": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Fps",
+ "description": "Video frames per second. None for images."
+ }
+ },
+ "type": "object",
+ "required": [
+ "kind",
+ "name",
+ "full_url",
+ "thumbnail_url",
+ "width",
+ "height",
+ "category",
+ "starred",
+ "is_intermediate",
+ "created_at"
+ ],
+ "title": "GalleryItem",
+ "description": "A gallery item \u2014 either an image or a video, with shared fields and a discriminator.\n\nFrontend code should dispatch on `kind` to render image- vs video-specific UI."
+ },
+ "GalleryItemKind": {
+ "type": "string",
+ "enum": ["image", "video"],
+ "title": "GalleryItemKind",
+ "description": "Discriminator for polymorphic gallery items."
+ },
+ "GalleryItemNamesResult": {
+ "properties": {
+ "items": {
+ "items": {
+ "$ref": "#/components/schemas/GalleryItemRef"
+ },
+ "type": "array",
+ "title": "Items",
+ "description": "Ordered list of (kind, name) references."
+ },
+ "starred_count": {
+ "type": "integer",
+ "title": "Starred Count",
+ "description": "Number of starred items (when starred_first=True)."
+ },
+ "total_count": {
+ "type": "integer",
+ "title": "Total Count",
+ "description": "Total number of items matching the query."
+ }
+ },
+ "type": "object",
+ "required": ["items", "starred_count", "total_count"],
+ "title": "GalleryItemNamesResult",
+ "description": "Ordered list of gallery item references plus counts for optimistic UI."
+ },
+ "GalleryItemRef": {
+ "properties": {
+ "kind": {
+ "$ref": "#/components/schemas/GalleryItemKind",
+ "description": "Whether the item is an image or video."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "The unique name of the image or video."
+ }
+ },
+ "type": "object",
+ "required": ["kind", "name"],
+ "title": "GalleryItemRef",
+ "description": "A thin reference to a gallery item \u2014 used for ordered name lists."
+ },
+ "GeminiImageGenerationInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generate images using a Gemini-hosted external model.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Main model (UNet, VAE, CLIP) to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "ui_model_base": ["external"],
+ "ui_model_format": ["external_api"],
+ "ui_model_provider_id": ["gemini"],
+ "ui_model_type": ["external_image_generator"]
+ },
+ "mode": {
+ "default": "txt2img",
+ "description": "Generation mode.",
+ "enum": ["txt2img", "img2img", "inpaint"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "txt2img",
+ "orig_required": false,
+ "title": "Mode",
+ "type": "string",
+ "ui_hidden": true
+ },
+ "prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Prompt",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt"
+ },
+ "seed": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Seed for random number generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Seed"
+ },
+ "num_images": {
+ "default": 1,
+ "description": "Number of images to generate",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Num Images",
+ "type": "integer"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Width of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "image_size": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Image size preset (e.g. 1K, 2K, 4K)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Image Size"
+ },
+ "init_image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Init image for img2img/inpaint",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_hidden": true
+ },
+ "mask_image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Mask image for inpaint",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_hidden": true
+ },
+ "reference_images": {
+ "default": [],
+ "description": "Reference images",
+ "field_kind": "input",
+ "input": "any",
+ "items": {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ "orig_default": [],
+ "orig_required": false,
+ "title": "Reference Images",
+ "type": "array"
+ },
+ "temperature": {
+ "anyOf": [
+ {
+ "maximum": 2.0,
+ "minimum": 0.0,
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Sampling temperature",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Temperature"
+ },
+ "thinking_level": {
+ "anyOf": [
+ {
+ "enum": ["minimal", "high"],
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Thinking level for image generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Thinking Level"
+ },
+ "type": {
+ "const": "gemini_image_generation",
+ "default": "gemini_image_generation",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["external", "generation", "gemini"],
+ "title": "Gemini Image Generation",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageCollectionOutput"
+ }
+ },
+ "GeneratePasswordResponse": {
+ "properties": {
+ "password": {
+ "type": "string",
+ "title": "Password",
+ "description": "Generated strong password"
+ }
+ },
+ "type": "object",
+ "required": ["password"],
+ "title": "GeneratePasswordResponse",
+ "description": "Response containing a generated password."
+ },
+ "GetMaskBoundingBoxInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Gets the bounding box of the given mask image.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The mask to crop.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "margin": {
+ "default": 0,
+ "description": "Margin to add to the bounding box.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Margin",
+ "type": "integer"
+ },
+ "mask_color": {
+ "$ref": "#/components/schemas/ColorField",
+ "default": {
+ "r": 255,
+ "g": 255,
+ "b": 255,
+ "a": 255
+ },
+ "description": "Color of the mask in the image.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": {
+ "a": 255,
+ "b": 255,
+ "g": 255,
+ "r": 255
+ },
+ "orig_required": false
+ },
+ "type": {
+ "const": "get_image_mask_bounding_box",
+ "default": "get_image_mask_bounding_box",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["mask"],
+ "title": "Get Image Mask Bounding Box",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/BoundingBoxOutput"
+ }
+ },
+ "GlmEncoderField": {
+ "properties": {
+ "tokenizer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load tokenizer submodel"
+ },
+ "text_encoder": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load text_encoder submodel"
+ }
+ },
+ "required": ["tokenizer", "text_encoder"],
+ "title": "GlmEncoderField",
+ "type": "object"
+ },
+ "GradientMaskOutput": {
+ "class": "output",
+ "description": "Outputs a denoise mask and an image representing the total gradient of the mask.",
+ "properties": {
+ "denoise_mask": {
+ "$ref": "#/components/schemas/DenoiseMaskField",
+ "description": "Mask for denoise model run. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "expanded_mask_area": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "Image representing the total gradient area of the mask. For paste-back purposes.",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "gradient_mask_output",
+ "default": "gradient_mask_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "denoise_mask", "expanded_mask_area", "type", "type"],
+ "title": "GradientMaskOutput",
+ "type": "object"
+ },
+ "Graph": {
+ "properties": {
+ "id": {
+ "type": "string",
+ "title": "Id",
+ "description": "The id of this graph"
+ },
+ "nodes": {
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/AddInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AlibabaCloudImageGenerationInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AlphaMaskToTensorInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaDenoiseInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaImageToLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaLLLiteInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaLatentsToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaLoRACollectionLoader"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaLoRALoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaModelLoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/AnimaTextEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ApplyMaskTensorToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ApplyMaskToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/BlankImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/BlendLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/BooleanCollectionInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/BooleanInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/BoundingBoxInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPSkipInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CV2InfillInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CalculateImageTilesEvenSplitInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CalculateImageTilesInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CalculateImageTilesMinimumOverlapInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CallSavedWorkflowInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CannyEdgeDetectionInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CanvasOutputInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CanvasPasteBackInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CanvasV2MaskAndCropInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CenterPadCropInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CogView4DenoiseInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CogView4ImageToLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CogView4LatentsToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CogView4ModelLoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CogView4TextEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CollectInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ColorCorrectInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ColorInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ColorMapInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CompelInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ConditioningCollectionInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ConditioningInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ContentShuffleInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNetInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CoreMetadataInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CreateDenoiseMaskInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CreateGradientMaskInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CropImageToBoundingBoxInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CropLatentsCoreInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/CvInpaintInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/DWOpenposeDetectionInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/DecodeInvisibleWatermarkInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/DenoiseLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/DenoiseLatentsMetaInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/DepthAnythingDepthEstimationInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/DivideInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/DynamicPromptInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/ESRGANInvocation"
+ },
+ {
"$ref": "#/components/schemas/ExpandMaskWithFadeInvocation"
},
+ {
+ "$ref": "#/components/schemas/ExtractVideoRangeInvocation"
+ },
{
"$ref": "#/components/schemas/FLUXLoRACollectionLoader"
},
@@ -29954,6 +32103,51 @@
{
"$ref": "#/components/schemas/VAELoaderInvocation"
},
+ {
+ "$ref": "#/components/schemas/VideoConcatInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoFrameExtractInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanDenoiseInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanImageToLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToVideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRACollectionLoader"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRALoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanModelLoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanRefImageEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTextEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanVideoDenoiseInvocation"
+ },
{
"$ref": "#/components/schemas/WorkflowReturnGetInvocation"
},
@@ -30107,6 +32301,9 @@
{
"$ref": "#/components/schemas/DenoiseMaskOutput"
},
+ {
+ "$ref": "#/components/schemas/ExtractVideoRangeOutput"
+ },
{
"$ref": "#/components/schemas/FaceMaskOutput"
},
@@ -30308,6 +32505,21 @@
{
"$ref": "#/components/schemas/VAEOutput"
},
+ {
+ "$ref": "#/components/schemas/VideoOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanConditioningOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRALoaderOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanModelLoaderOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanRefImageOutput"
+ },
{
"$ref": "#/components/schemas/WorkflowReturnGetOutput"
},
@@ -37089,6 +39301,9 @@
{
"$ref": "#/components/schemas/ExpandMaskWithFadeInvocation"
},
+ {
+ "$ref": "#/components/schemas/ExtractVideoRangeInvocation"
+ },
{
"$ref": "#/components/schemas/FLUXLoRACollectionLoader"
},
@@ -37656,6 +39871,51 @@
{
"$ref": "#/components/schemas/VAELoaderInvocation"
},
+ {
+ "$ref": "#/components/schemas/VideoConcatInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoFrameExtractInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanDenoiseInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanImageToLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToVideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRACollectionLoader"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRALoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanModelLoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanRefImageEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTextEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanVideoDenoiseInvocation"
+ },
{
"$ref": "#/components/schemas/WorkflowReturnGetInvocation"
},
@@ -37766,6 +40026,9 @@
{
"$ref": "#/components/schemas/DenoiseMaskOutput"
},
+ {
+ "$ref": "#/components/schemas/ExtractVideoRangeOutput"
+ },
{
"$ref": "#/components/schemas/FaceMaskOutput"
},
@@ -37967,6 +40230,21 @@
{
"$ref": "#/components/schemas/VAEOutput"
},
+ {
+ "$ref": "#/components/schemas/VideoOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanConditioningOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRALoaderOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanModelLoaderOutput"
+ },
+ {
+ "$ref": "#/components/schemas/WanRefImageOutput"
+ },
{
"$ref": "#/components/schemas/WorkflowReturnGetOutput"
},
@@ -38245,6 +40523,9 @@
{
"$ref": "#/components/schemas/ExpandMaskWithFadeInvocation"
},
+ {
+ "$ref": "#/components/schemas/ExtractVideoRangeInvocation"
+ },
{
"$ref": "#/components/schemas/FLUXLoRACollectionLoader"
},
@@ -38812,6 +41093,51 @@
{
"$ref": "#/components/schemas/VAELoaderInvocation"
},
+ {
+ "$ref": "#/components/schemas/VideoConcatInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoFrameExtractInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanDenoiseInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanImageToLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToVideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRACollectionLoader"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRALoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanModelLoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanRefImageEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTextEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanVideoDenoiseInvocation"
+ },
{
"$ref": "#/components/schemas/WorkflowReturnGetInvocation"
},
@@ -39061,6 +41387,9 @@
"expand_mask_with_fade": {
"$ref": "#/components/schemas/ImageOutput"
},
+ "extract_video_range": {
+ "$ref": "#/components/schemas/ExtractVideoRangeOutput"
+ },
"face_identifier": {
"$ref": "#/components/schemas/ImageOutput"
},
@@ -39637,6 +41966,51 @@
"vae_loader": {
"$ref": "#/components/schemas/VAEOutput"
},
+ "video": {
+ "$ref": "#/components/schemas/VideoOutput"
+ },
+ "video_concat": {
+ "$ref": "#/components/schemas/VideoOutput"
+ },
+ "video_frame_extract": {
+ "$ref": "#/components/schemas/ImageOutput"
+ },
+ "wan_denoise": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ },
+ "wan_i2l": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ },
+ "wan_i2v_ideal_dimensions": {
+ "$ref": "#/components/schemas/IdealSizeOutput"
+ },
+ "wan_l2i": {
+ "$ref": "#/components/schemas/ImageOutput"
+ },
+ "wan_l2v": {
+ "$ref": "#/components/schemas/VideoOutput"
+ },
+ "wan_lora_collection_loader": {
+ "$ref": "#/components/schemas/WanLoRALoaderOutput"
+ },
+ "wan_lora_loader": {
+ "$ref": "#/components/schemas/WanLoRALoaderOutput"
+ },
+ "wan_model_loader": {
+ "$ref": "#/components/schemas/WanModelLoaderOutput"
+ },
+ "wan_ref_image_encoder": {
+ "$ref": "#/components/schemas/WanRefImageOutput"
+ },
+ "wan_text_encoder": {
+ "$ref": "#/components/schemas/WanConditioningOutput"
+ },
+ "wan_ti2v_ideal_dimensions": {
+ "$ref": "#/components/schemas/IdealSizeOutput"
+ },
+ "wan_video_denoise": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ },
"workflow_return": {
"$ref": "#/components/schemas/WorkflowReturnOutput"
},
@@ -39733,6 +42107,7 @@
"dynamic_prompt",
"esrgan",
"expand_mask_with_fade",
+ "extract_video_range",
"face_identifier",
"face_mask_detection",
"face_off",
@@ -39925,6 +42300,21 @@
"unsharp_mask",
"unsharp_mask_oklab",
"vae_loader",
+ "video",
+ "video_concat",
+ "video_frame_extract",
+ "wan_denoise",
+ "wan_i2l",
+ "wan_i2v_ideal_dimensions",
+ "wan_l2i",
+ "wan_l2v",
+ "wan_lora_collection_loader",
+ "wan_lora_loader",
+ "wan_model_loader",
+ "wan_ref_image_encoder",
+ "wan_text_encoder",
+ "wan_ti2v_ideal_dimensions",
+ "wan_video_denoise",
"workflow_return",
"workflow_return_get",
"workflow_return_value",
@@ -40177,6 +42567,9 @@
{
"$ref": "#/components/schemas/ExpandMaskWithFadeInvocation"
},
+ {
+ "$ref": "#/components/schemas/ExtractVideoRangeInvocation"
+ },
{
"$ref": "#/components/schemas/FLUXLoRACollectionLoader"
},
@@ -40744,6 +43137,51 @@
{
"$ref": "#/components/schemas/VAELoaderInvocation"
},
+ {
+ "$ref": "#/components/schemas/VideoConcatInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoFrameExtractInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanDenoiseInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanImageToLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToVideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRACollectionLoader"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRALoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanModelLoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanRefImageEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTextEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanVideoDenoiseInvocation"
+ },
{
"$ref": "#/components/schemas/WorkflowReturnGetInvocation"
},
@@ -41079,6 +43517,9 @@
{
"$ref": "#/components/schemas/ExpandMaskWithFadeInvocation"
},
+ {
+ "$ref": "#/components/schemas/ExtractVideoRangeInvocation"
+ },
{
"$ref": "#/components/schemas/FLUXLoRACollectionLoader"
},
@@ -41646,6 +44087,51 @@
{
"$ref": "#/components/schemas/VAELoaderInvocation"
},
+ {
+ "$ref": "#/components/schemas/VideoConcatInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoFrameExtractInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/VideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanDenoiseInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanImageToLatentsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToImageInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLatentsToVideoInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRACollectionLoader"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRALoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanModelLoaderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanRefImageEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTI2VIdealDimensionsInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanTextEncoderInvocation"
+ },
+ {
+ "$ref": "#/components/schemas/WanVideoDenoiseInvocation"
+ },
{
"$ref": "#/components/schemas/WorkflowReturnGetInvocation"
},
@@ -45514,9 +48000,321 @@
},
"base": {
"type": "string",
- "const": "sd-2",
+ "const": "sd-2",
+ "title": "Base",
+ "default": "sd-2"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "trigger_phrases",
+ "default_settings",
+ "format",
+ "base"
+ ],
+ "title": "LoRA_Diffusers_SD2_Config"
+ },
+ "LoRA_Diffusers_SDXL_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
+ "type": "string",
+ "const": "lora",
+ "title": "Type",
+ "default": "lora"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LoraModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Default settings for this model"
+ },
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "trigger_phrases",
+ "default_settings",
+ "format",
+ "base"
+ ],
+ "title": "LoRA_Diffusers_SDXL_Config"
+ },
+ "LoRA_Diffusers_ZImage_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
+ "type": "string",
+ "const": "lora",
+ "title": "Type",
+ "default": "lora"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LoraModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Default settings for this model"
+ },
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "base": {
+ "type": "string",
+ "const": "z-image",
"title": "Base",
- "default": "sd-2"
+ "default": "z-image"
+ },
+ "variant": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ZImageVariantType"
+ },
+ {
+ "type": "null"
+ }
+ ]
}
},
"type": "object",
@@ -45536,11 +48334,13 @@
"trigger_phrases",
"default_settings",
"format",
- "base"
+ "base",
+ "variant"
],
- "title": "LoRA_Diffusers_SD2_Config"
+ "title": "LoRA_Diffusers_ZImage_Config",
+ "description": "Model config for Z-Image LoRA models in Diffusers format."
},
- "LoRA_Diffusers_SDXL_Config": {
+ "LoRA_LyCORIS_Anima_Config": {
"properties": {
"key": {
"type": "string",
@@ -45659,15 +48459,15 @@
},
"format": {
"type": "string",
- "const": "diffusers",
+ "const": "lycoris",
"title": "Format",
- "default": "diffusers"
+ "default": "lycoris"
},
"base": {
"type": "string",
- "const": "sdxl",
+ "const": "anima",
"title": "Base",
- "default": "sdxl"
+ "default": "anima"
}
},
"type": "object",
@@ -45689,9 +48489,10 @@
"format",
"base"
],
- "title": "LoRA_Diffusers_SDXL_Config"
+ "title": "LoRA_LyCORIS_Anima_Config",
+ "description": "Model config for Anima LoRA models in LyCORIS format."
},
- "LoRA_Diffusers_ZImage_Config": {
+ "LoRA_LyCORIS_FLUX_Config": {
"properties": {
"key": {
"type": "string",
@@ -45810,25 +48611,15 @@
},
"format": {
"type": "string",
- "const": "diffusers",
+ "const": "lycoris",
"title": "Format",
- "default": "diffusers"
+ "default": "lycoris"
},
"base": {
"type": "string",
- "const": "z-image",
+ "const": "flux",
"title": "Base",
- "default": "z-image"
- },
- "variant": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ZImageVariantType"
- },
- {
- "type": "null"
- }
- ]
+ "default": "flux"
}
},
"type": "object",
@@ -45848,13 +48639,11 @@
"trigger_phrases",
"default_settings",
"format",
- "base",
- "variant"
+ "base"
],
- "title": "LoRA_Diffusers_ZImage_Config",
- "description": "Model config for Z-Image LoRA models in Diffusers format."
+ "title": "LoRA_LyCORIS_FLUX_Config"
},
- "LoRA_LyCORIS_Anima_Config": {
+ "LoRA_LyCORIS_Flux2_Config": {
"properties": {
"key": {
"type": "string",
@@ -45979,9 +48768,19 @@
},
"base": {
"type": "string",
- "const": "anima",
+ "const": "flux2",
"title": "Base",
- "default": "anima"
+ "default": "flux2"
+ },
+ "variant": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Flux2VariantType"
+ },
+ {
+ "type": "null"
+ }
+ ]
}
},
"type": "object",
@@ -46001,12 +48800,13 @@
"trigger_phrases",
"default_settings",
"format",
- "base"
+ "base",
+ "variant"
],
- "title": "LoRA_LyCORIS_Anima_Config",
- "description": "Model config for Anima LoRA models in LyCORIS format."
+ "title": "LoRA_LyCORIS_Flux2_Config",
+ "description": "Model config for FLUX.2 (Klein) LoRA models in LyCORIS format."
},
- "LoRA_LyCORIS_FLUX_Config": {
+ "LoRA_LyCORIS_QwenImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -46131,9 +48931,9 @@
},
"base": {
"type": "string",
- "const": "flux",
+ "const": "qwen-image",
"title": "Base",
- "default": "flux"
+ "default": "qwen-image"
}
},
"type": "object",
@@ -46155,9 +48955,10 @@
"format",
"base"
],
- "title": "LoRA_LyCORIS_FLUX_Config"
+ "title": "LoRA_LyCORIS_QwenImage_Config",
+ "description": "Model config for Qwen Image Edit LoRA models in LyCORIS format."
},
- "LoRA_LyCORIS_Flux2_Config": {
+ "LoRA_LyCORIS_SD1_Config": {
"properties": {
"key": {
"type": "string",
@@ -46282,19 +49083,9 @@
},
"base": {
"type": "string",
- "const": "flux2",
+ "const": "sd-1",
"title": "Base",
- "default": "flux2"
- },
- "variant": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Flux2VariantType"
- },
- {
- "type": "null"
- }
- ]
+ "default": "sd-1"
}
},
"type": "object",
@@ -46314,13 +49105,11 @@
"trigger_phrases",
"default_settings",
"format",
- "base",
- "variant"
+ "base"
],
- "title": "LoRA_LyCORIS_Flux2_Config",
- "description": "Model config for FLUX.2 (Klein) LoRA models in LyCORIS format."
+ "title": "LoRA_LyCORIS_SD1_Config"
},
- "LoRA_LyCORIS_QwenImage_Config": {
+ "LoRA_LyCORIS_SD2_Config": {
"properties": {
"key": {
"type": "string",
@@ -46445,9 +49234,9 @@
},
"base": {
"type": "string",
- "const": "qwen-image",
+ "const": "sd-2",
"title": "Base",
- "default": "qwen-image"
+ "default": "sd-2"
}
},
"type": "object",
@@ -46469,10 +49258,9 @@
"format",
"base"
],
- "title": "LoRA_LyCORIS_QwenImage_Config",
- "description": "Model config for Qwen Image Edit LoRA models in LyCORIS format."
+ "title": "LoRA_LyCORIS_SD2_Config"
},
- "LoRA_LyCORIS_SD1_Config": {
+ "LoRA_LyCORIS_SDXL_Config": {
"properties": {
"key": {
"type": "string",
@@ -46597,9 +49385,9 @@
},
"base": {
"type": "string",
- "const": "sd-1",
+ "const": "sdxl",
"title": "Base",
- "default": "sd-1"
+ "default": "sdxl"
}
},
"type": "object",
@@ -46621,9 +49409,9 @@
"format",
"base"
],
- "title": "LoRA_LyCORIS_SD1_Config"
+ "title": "LoRA_LyCORIS_SDXL_Config"
},
- "LoRA_LyCORIS_SD2_Config": {
+ "LoRA_LyCORIS_Wan_Config": {
"properties": {
"key": {
"type": "string",
@@ -46748,160 +49536,33 @@
},
"base": {
"type": "string",
- "const": "sd-2",
+ "const": "wan",
"title": "Base",
- "default": "sd-2"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "type",
- "trigger_phrases",
- "default_settings",
- "format",
- "base"
- ],
- "title": "LoRA_LyCORIS_SD2_Config"
- },
- "LoRA_LyCORIS_SDXL_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "default": "wan"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
- },
- "source_api_response": {
+ "expert": {
"anyOf": [
{
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
- },
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
- },
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "type": {
- "type": "string",
- "const": "lora",
- "title": "Type",
- "default": "lora"
- },
- "trigger_phrases": {
- "anyOf": [
- {
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true
+ "type": "string",
+ "enum": ["high", "low"]
},
{
"type": "null"
}
],
- "title": "Trigger Phrases",
- "description": "Set of trigger phrases for this model"
+ "title": "Expert",
+ "description": "For Wan 2.2 A14B dual-expert LoRAs: 'high' targets the high-noise expert, 'low' targets the low-noise expert. None means the LoRA is expert-agnostic (TI2V-5B, or community LoRAs without explicit tagging) and is applied to both."
},
- "default_settings": {
+ "variant": {
"anyOf": [
{
- "$ref": "#/components/schemas/LoraModelDefaultSettings"
+ "$ref": "#/components/schemas/WanLoRAVariantType"
},
{
"type": "null"
}
],
- "description": "Default settings for this model"
- },
- "format": {
- "type": "string",
- "const": "lycoris",
- "title": "Format",
- "default": "lycoris"
- },
- "base": {
- "type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
+ "description": "The Wan model family this LoRA targets, detected from its inner-dim (5120 -> A14B, 3072 -> TI2V-5B). A14B LoRAs are incompatible with TI2V-5B mains (and vice versa) \u2014 they crash with a shape mismatch in the layer patcher. The linear-view graph builder filters LoRAs on variant when building the LoRA collection. None means the LoRA's inner-dim couldn't be identified."
}
},
"type": "object",
@@ -46921,9 +49582,12 @@
"trigger_phrases",
"default_settings",
"format",
- "base"
+ "base",
+ "expert",
+ "variant"
],
- "title": "LoRA_LyCORIS_SDXL_Config"
+ "title": "LoRA_LyCORIS_Wan_Config",
+ "description": "Model config for Wan 2.2 LoRA models in LyCORIS format.\n\nWan LoRAs target ``WanTransformer3DModel`` blocks. The Wan 2.2 A14B family\nis dual-expert (high-noise + low-noise) \u2014 LoRAs are typically trained\nagainst one expert. ``expert`` records which one so the model loader\ninvocation can wire it to the correct ``loras`` / ``loras_low_noise`` list.\nMany LoRAs are expert-agnostic (TI2V-5B family, or community LoRAs that\njust don't tag the expert) \u2014 these get ``expert=None`` and are applied to\nboth experts by default."
},
"LoRA_LyCORIS_ZImage_Config": {
"properties": {
@@ -48133,20 +50797,523 @@
"title": "Config Path",
"description": "Path to the config for this model, if any."
},
- "base": {
- "type": "string",
- "const": "flux",
- "title": "Base",
- "default": "flux"
- },
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
+ },
+ "format": {
+ "type": "string",
+ "const": "bnb_quantized_nf4b",
+ "title": "Format",
+ "default": "bnb_quantized_nf4b"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/FluxVariantType"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "trigger_phrases",
+ "default_settings",
+ "config_path",
+ "base",
+ "format",
+ "variant"
+ ],
+ "title": "Main_BnBNF4_FLUX_Config",
+ "description": "Model config for main checkpoint models."
+ },
+ "Main_Checkpoint_Anima_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
+ "type": "string",
+ "const": "main",
+ "title": "Type",
+ "default": "main"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MainModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Default settings for this model"
+ },
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
+ "base": {
+ "type": "string",
+ "const": "anima",
+ "title": "Base",
+ "default": "anima"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "trigger_phrases",
+ "default_settings",
+ "config_path",
+ "base",
+ "format"
+ ],
+ "title": "Main_Checkpoint_Anima_Config",
+ "description": "Model config for Anima single-file checkpoint models (safetensors).\n\nAnima is built on NVIDIA Cosmos Predict2 DiT with a custom LLM Adapter\nthat bridges Qwen3 0.6B text encoder outputs to the DiT."
+ },
+ "Main_Checkpoint_FLUX_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
+ "type": "string",
+ "const": "main",
+ "title": "Type",
+ "default": "main"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MainModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Default settings for this model"
+ },
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ },
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/FluxVariantType"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "trigger_phrases",
+ "default_settings",
+ "config_path",
+ "format",
+ "base",
+ "variant"
+ ],
+ "title": "Main_Checkpoint_FLUX_Config",
+ "description": "Model config for main checkpoint models."
+ },
+ "Main_Checkpoint_Flux2_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
+ "type": "string",
+ "const": "main",
+ "title": "Type",
+ "default": "main"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MainModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Default settings for this model"
+ },
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
"format": {
"type": "string",
- "const": "bnb_quantized_nf4b",
+ "const": "checkpoint",
"title": "Format",
- "default": "bnb_quantized_nf4b"
+ "default": "checkpoint"
+ },
+ "base": {
+ "type": "string",
+ "const": "flux2",
+ "title": "Base",
+ "default": "flux2"
},
"variant": {
- "$ref": "#/components/schemas/FluxVariantType"
+ "$ref": "#/components/schemas/Flux2VariantType"
}
},
"type": "object",
@@ -48166,14 +51333,14 @@
"trigger_phrases",
"default_settings",
"config_path",
- "base",
"format",
+ "base",
"variant"
],
- "title": "Main_BnBNF4_FLUX_Config",
- "description": "Model config for main checkpoint models."
+ "title": "Main_Checkpoint_Flux2_Config",
+ "description": "Model config for FLUX.2 checkpoint models (e.g. Klein)."
},
- "Main_Checkpoint_Anima_Config": {
+ "Main_Checkpoint_QwenImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -48304,183 +51471,25 @@
},
"base": {
"type": "string",
- "const": "anima",
+ "const": "qwen-image",
"title": "Base",
- "default": "anima"
+ "default": "qwen-image"
},
"format": {
"type": "string",
"const": "checkpoint",
"title": "Format",
"default": "checkpoint"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "type",
- "trigger_phrases",
- "default_settings",
- "config_path",
- "base",
- "format"
- ],
- "title": "Main_Checkpoint_Anima_Config",
- "description": "Model config for Anima single-file checkpoint models (safetensors).\n\nAnima is built on NVIDIA Cosmos Predict2 DiT with a custom LLM Adapter\nthat bridges Qwen3 0.6B text encoder outputs to the DiT."
- },
- "Main_Checkpoint_FLUX_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
- },
- "source_api_response": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
- },
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
- },
- "cover_image": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "type": {
- "type": "string",
- "const": "main",
- "title": "Type",
- "default": "main"
- },
- "trigger_phrases": {
- "anyOf": [
- {
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true
- },
- {
- "type": "null"
- }
- ],
- "title": "Trigger Phrases",
- "description": "Set of trigger phrases for this model"
- },
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MainModelDefaultSettings"
- },
- {
- "type": "null"
- }
- ],
- "description": "Default settings for this model"
- },
- "config_path": {
+ "variant": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/QwenImageVariantType"
},
{
"type": "null"
}
- ],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
- },
- "base": {
- "type": "string",
- "const": "flux",
- "title": "Base",
- "default": "flux"
- },
- "variant": {
- "$ref": "#/components/schemas/FluxVariantType"
+ ]
}
},
"type": "object",
@@ -48500,14 +51509,14 @@
"trigger_phrases",
"default_settings",
"config_path",
- "format",
"base",
+ "format",
"variant"
],
- "title": "Main_Checkpoint_FLUX_Config",
- "description": "Model config for main checkpoint models."
+ "title": "Main_Checkpoint_QwenImage_Config",
+ "description": "Model config for Qwen Image single-file checkpoint models (safetensors, etc).\n\nCovers both raw bf16/fp16 checkpoints and ComfyUI-style fp8_scaled checkpoints.\nThe loader dequantizes fp8 weights back to bf16 at load time; the\n`default_settings.fp8_storage` toggle can then optionally re-cast to fp8 for\nVRAM savings."
},
- "Main_Checkpoint_Flux2_Config": {
+ "Main_Checkpoint_SD1_Config": {
"properties": {
"key": {
"type": "string",
@@ -48642,14 +51651,17 @@
"title": "Format",
"default": "checkpoint"
},
+ "prediction_type": {
+ "$ref": "#/components/schemas/SchedulerPredictionType"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
"base": {
"type": "string",
- "const": "flux2",
+ "const": "sd-1",
"title": "Base",
- "default": "flux2"
- },
- "variant": {
- "$ref": "#/components/schemas/Flux2VariantType"
+ "default": "sd-1"
}
},
"type": "object",
@@ -48670,13 +51682,13 @@
"default_settings",
"config_path",
"format",
- "base",
- "variant"
+ "prediction_type",
+ "variant",
+ "base"
],
- "title": "Main_Checkpoint_Flux2_Config",
- "description": "Model config for FLUX.2 checkpoint models (e.g. Klein)."
+ "title": "Main_Checkpoint_SD1_Config"
},
- "Main_Checkpoint_QwenImage_Config": {
+ "Main_Checkpoint_SD2_Config": {
"properties": {
"key": {
"type": "string",
@@ -48805,27 +51817,23 @@
"title": "Config Path",
"description": "Path to the config for this model, if any."
},
- "base": {
- "type": "string",
- "const": "qwen-image",
- "title": "Base",
- "default": "qwen-image"
- },
"format": {
"type": "string",
"const": "checkpoint",
"title": "Format",
"default": "checkpoint"
},
+ "prediction_type": {
+ "$ref": "#/components/schemas/SchedulerPredictionType"
+ },
"variant": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/QwenImageVariantType"
- },
- {
- "type": "null"
- }
- ]
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
+ "base": {
+ "type": "string",
+ "const": "sd-2",
+ "title": "Base",
+ "default": "sd-2"
}
},
"type": "object",
@@ -48845,14 +51853,14 @@
"trigger_phrases",
"default_settings",
"config_path",
- "base",
"format",
- "variant"
+ "prediction_type",
+ "variant",
+ "base"
],
- "title": "Main_Checkpoint_QwenImage_Config",
- "description": "Model config for Qwen Image single-file checkpoint models (safetensors, etc).\n\nCovers both raw bf16/fp16 checkpoints and ComfyUI-style fp8_scaled checkpoints.\nThe loader dequantizes fp8 weights back to bf16 at load time; the\n`default_settings.fp8_storage` toggle can then optionally re-cast to fp8 for\nVRAM savings."
+ "title": "Main_Checkpoint_SD2_Config"
},
- "Main_Checkpoint_SD1_Config": {
+ "Main_Checkpoint_SDXLRefiner_Config": {
"properties": {
"key": {
"type": "string",
@@ -48995,9 +52003,9 @@
},
"base": {
"type": "string",
- "const": "sd-1",
+ "const": "sdxl-refiner",
"title": "Base",
- "default": "sd-1"
+ "default": "sdxl-refiner"
}
},
"type": "object",
@@ -49022,9 +52030,9 @@
"variant",
"base"
],
- "title": "Main_Checkpoint_SD1_Config"
+ "title": "Main_Checkpoint_SDXLRefiner_Config"
},
- "Main_Checkpoint_SD2_Config": {
+ "Main_Checkpoint_SDXL_Config": {
"properties": {
"key": {
"type": "string",
@@ -49167,9 +52175,9 @@
},
"base": {
"type": "string",
- "const": "sd-2",
+ "const": "sdxl",
"title": "Base",
- "default": "sd-2"
+ "default": "sdxl"
}
},
"type": "object",
@@ -49194,9 +52202,9 @@
"variant",
"base"
],
- "title": "Main_Checkpoint_SD2_Config"
+ "title": "Main_Checkpoint_SDXL_Config"
},
- "Main_Checkpoint_SDXLRefiner_Config": {
+ "Main_Checkpoint_ZImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -49325,23 +52333,20 @@
"title": "Config Path",
"description": "Path to the config for this model, if any."
},
+ "base": {
+ "type": "string",
+ "const": "z-image",
+ "title": "Base",
+ "default": "z-image"
+ },
"format": {
"type": "string",
"const": "checkpoint",
"title": "Format",
"default": "checkpoint"
},
- "prediction_type": {
- "$ref": "#/components/schemas/SchedulerPredictionType"
- },
"variant": {
- "$ref": "#/components/schemas/ModelVariantType"
- },
- "base": {
- "type": "string",
- "const": "sdxl-refiner",
- "title": "Base",
- "default": "sdxl-refiner"
+ "$ref": "#/components/schemas/ZImageVariantType"
}
},
"type": "object",
@@ -49361,14 +52366,14 @@
"trigger_phrases",
"default_settings",
"config_path",
+ "base",
"format",
- "prediction_type",
- "variant",
- "base"
+ "variant"
],
- "title": "Main_Checkpoint_SDXLRefiner_Config"
+ "title": "Main_Checkpoint_ZImage_Config",
+ "description": "Model config for Z-Image single-file checkpoint models (safetensors, etc)."
},
- "Main_Checkpoint_SDXL_Config": {
+ "Main_Diffusers_CogView4_Config": {
"properties": {
"key": {
"type": "string",
@@ -49485,35 +52490,21 @@
],
"description": "Default settings for this model"
},
- "config_path": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
"format": {
"type": "string",
- "const": "checkpoint",
+ "const": "diffusers",
"title": "Format",
- "default": "checkpoint"
- },
- "prediction_type": {
- "$ref": "#/components/schemas/SchedulerPredictionType"
+ "default": "diffusers"
},
- "variant": {
- "$ref": "#/components/schemas/ModelVariantType"
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
"base": {
"type": "string",
- "const": "sdxl",
+ "const": "cogview4",
"title": "Base",
- "default": "sdxl"
+ "default": "cogview4"
}
},
"type": "object",
@@ -49532,15 +52523,13 @@
"type",
"trigger_phrases",
"default_settings",
- "config_path",
"format",
- "prediction_type",
- "variant",
+ "repo_variant",
"base"
],
- "title": "Main_Checkpoint_SDXL_Config"
+ "title": "Main_Diffusers_CogView4_Config"
},
- "Main_Checkpoint_ZImage_Config": {
+ "Main_Diffusers_FLUX_Config": {
"properties": {
"key": {
"type": "string",
@@ -49657,7 +52646,78 @@
],
"description": "Default settings for this model"
},
- "config_path": {
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/FluxVariantType"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "trigger_phrases",
+ "default_settings",
+ "format",
+ "repo_variant",
+ "base",
+ "variant"
+ ],
+ "title": "Main_Diffusers_FLUX_Config",
+ "description": "Model config for FLUX.1 models in diffusers format."
+ },
+ "Main_Diffusers_Flux2_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
"type": "string"
@@ -49666,23 +52726,105 @@
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "title": "Description",
+ "description": "Model description"
},
- "base": {
+ "source": {
"type": "string",
- "const": "z-image",
- "title": "Base",
- "default": "z-image"
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
+ "type": "string",
+ "const": "main",
+ "title": "Type",
+ "default": "main"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MainModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Default settings for this model"
},
"format": {
"type": "string",
- "const": "checkpoint",
+ "const": "diffusers",
"title": "Format",
- "default": "checkpoint"
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "base": {
+ "type": "string",
+ "const": "flux2",
+ "title": "Base",
+ "default": "flux2"
},
"variant": {
- "$ref": "#/components/schemas/ZImageVariantType"
+ "$ref": "#/components/schemas/Flux2VariantType"
}
},
"type": "object",
@@ -49701,15 +52843,15 @@
"type",
"trigger_phrases",
"default_settings",
- "config_path",
- "base",
"format",
+ "repo_variant",
+ "base",
"variant"
],
- "title": "Main_Checkpoint_ZImage_Config",
- "description": "Model config for Z-Image single-file checkpoint models (safetensors, etc)."
+ "title": "Main_Diffusers_Flux2_Config",
+ "description": "Model config for FLUX.2 models in diffusers format (e.g. FLUX.2 Klein)."
},
- "Main_Diffusers_CogView4_Config": {
+ "Main_Diffusers_QwenImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -49838,9 +52980,19 @@
},
"base": {
"type": "string",
- "const": "cogview4",
+ "const": "qwen-image",
"title": "Base",
- "default": "cogview4"
+ "default": "qwen-image"
+ },
+ "variant": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/QwenImageVariantType"
+ },
+ {
+ "type": "null"
+ }
+ ]
}
},
"type": "object",
@@ -49861,11 +53013,13 @@
"default_settings",
"format",
"repo_variant",
- "base"
+ "base",
+ "variant"
],
- "title": "Main_Diffusers_CogView4_Config"
+ "title": "Main_Diffusers_QwenImage_Config",
+ "description": "Model config for Qwen Image diffusers models (both txt2img and edit)."
},
- "Main_Diffusers_FLUX_Config": {
+ "Main_Diffusers_SD1_Config": {
"properties": {
"key": {
"type": "string",
@@ -49992,14 +53146,17 @@
"$ref": "#/components/schemas/ModelRepoVariant",
"default": ""
},
+ "prediction_type": {
+ "$ref": "#/components/schemas/SchedulerPredictionType"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
"base": {
"type": "string",
- "const": "flux",
+ "const": "sd-1",
"title": "Base",
- "default": "flux"
- },
- "variant": {
- "$ref": "#/components/schemas/FluxVariantType"
+ "default": "sd-1"
}
},
"type": "object",
@@ -50020,13 +53177,13 @@
"default_settings",
"format",
"repo_variant",
- "base",
- "variant"
+ "prediction_type",
+ "variant",
+ "base"
],
- "title": "Main_Diffusers_FLUX_Config",
- "description": "Model config for FLUX.1 models in diffusers format."
+ "title": "Main_Diffusers_SD1_Config"
},
- "Main_Diffusers_Flux2_Config": {
+ "Main_Diffusers_SD2_Config": {
"properties": {
"key": {
"type": "string",
@@ -50153,14 +53310,17 @@
"$ref": "#/components/schemas/ModelRepoVariant",
"default": ""
},
+ "prediction_type": {
+ "$ref": "#/components/schemas/SchedulerPredictionType"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
"base": {
"type": "string",
- "const": "flux2",
+ "const": "sd-2",
"title": "Base",
- "default": "flux2"
- },
- "variant": {
- "$ref": "#/components/schemas/Flux2VariantType"
+ "default": "sd-2"
}
},
"type": "object",
@@ -50181,13 +53341,13 @@
"default_settings",
"format",
"repo_variant",
- "base",
- "variant"
+ "prediction_type",
+ "variant",
+ "base"
],
- "title": "Main_Diffusers_Flux2_Config",
- "description": "Model config for FLUX.2 models in diffusers format (e.g. FLUX.2 Klein)."
+ "title": "Main_Diffusers_SD2_Config"
},
- "Main_Diffusers_QwenImage_Config": {
+ "Main_Diffusers_SD3_Config": {
"properties": {
"key": {
"type": "string",
@@ -50316,19 +53476,27 @@
},
"base": {
"type": "string",
- "const": "qwen-image",
+ "const": "sd-3",
"title": "Base",
- "default": "qwen-image"
+ "default": "sd-3"
},
- "variant": {
+ "submodels": {
"anyOf": [
{
- "$ref": "#/components/schemas/QwenImageVariantType"
+ "additionalProperties": {
+ "$ref": "#/components/schemas/SubmodelDefinition"
+ },
+ "propertyNames": {
+ "$ref": "#/components/schemas/SubModelType"
+ },
+ "type": "object"
},
{
"type": "null"
}
- ]
+ ],
+ "title": "Submodels",
+ "description": "Loadable submodels in this model"
}
},
"type": "object",
@@ -50350,12 +53518,11 @@
"format",
"repo_variant",
"base",
- "variant"
+ "submodels"
],
- "title": "Main_Diffusers_QwenImage_Config",
- "description": "Model config for Qwen Image diffusers models (both txt2img and edit)."
+ "title": "Main_Diffusers_SD3_Config"
},
- "Main_Diffusers_SD1_Config": {
+ "Main_Diffusers_SDXLRefiner_Config": {
"properties": {
"key": {
"type": "string",
@@ -50490,9 +53657,9 @@
},
"base": {
"type": "string",
- "const": "sd-1",
+ "const": "sdxl-refiner",
"title": "Base",
- "default": "sd-1"
+ "default": "sdxl-refiner"
}
},
"type": "object",
@@ -50517,9 +53684,173 @@
"variant",
"base"
],
- "title": "Main_Diffusers_SD1_Config"
+ "title": "Main_Diffusers_SDXLRefiner_Config"
},
- "Main_Diffusers_SD2_Config": {
+ "Main_Diffusers_SDXL_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
+ "type": "string",
+ "const": "main",
+ "title": "Type",
+ "default": "main"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MainModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Default settings for this model"
+ },
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "prediction_type": {
+ "$ref": "#/components/schemas/SchedulerPredictionType"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "trigger_phrases",
+ "default_settings",
+ "format",
+ "repo_variant",
+ "prediction_type",
+ "variant",
+ "base"
+ ],
+ "title": "Main_Diffusers_SDXL_Config"
+ },
+ "Main_Diffusers_Wan_Config": {
"properties": {
"key": {
"type": "string",
@@ -50646,17 +53977,32 @@
"$ref": "#/components/schemas/ModelRepoVariant",
"default": ""
},
- "prediction_type": {
- "$ref": "#/components/schemas/SchedulerPredictionType"
- },
- "variant": {
- "$ref": "#/components/schemas/ModelVariantType"
- },
"base": {
"type": "string",
- "const": "sd-2",
+ "const": "wan",
"title": "Base",
- "default": "sd-2"
+ "default": "wan"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/WanVariantType"
+ },
+ "has_dual_expert": {
+ "type": "boolean",
+ "title": "Has Dual Expert",
+ "description": "Whether this model ships two transformer experts (Wan 2.2 A14B MoE). False for TI2V-5B.",
+ "default": false
+ },
+ "boundary_ratio": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Boundary Ratio",
+ "description": "MoE expert switch point as a fraction of num_train_timesteps (typically 1000). None for single-transformer models. Read from model_index.json by Diffusers' WanPipeline."
}
},
"type": "object",
@@ -50677,13 +54023,15 @@
"default_settings",
"format",
"repo_variant",
- "prediction_type",
+ "base",
"variant",
- "base"
+ "has_dual_expert",
+ "boundary_ratio"
],
- "title": "Main_Diffusers_SD2_Config"
+ "title": "Main_Diffusers_Wan_Config",
+ "description": "Model config for Wan 2.2 diffusers models.\n\nCovers both the dual-expert T2V-A14B family and the single-transformer TI2V-5B\nfamily. Variant is detected from the on-disk transformer config (latent channel\ncount) plus the presence of a sibling ``transformer_2/`` directory."
},
- "Main_Diffusers_SD3_Config": {
+ "Main_Diffusers_ZImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -50812,27 +54160,12 @@
},
"base": {
"type": "string",
- "const": "sd-3",
+ "const": "z-image",
"title": "Base",
- "default": "sd-3"
+ "default": "z-image"
},
- "submodels": {
- "anyOf": [
- {
- "additionalProperties": {
- "$ref": "#/components/schemas/SubmodelDefinition"
- },
- "propertyNames": {
- "$ref": "#/components/schemas/SubModelType"
- },
- "type": "object"
- },
- {
- "type": "null"
- }
- ],
- "title": "Submodels",
- "description": "Loadable submodels in this model"
+ "variant": {
+ "$ref": "#/components/schemas/ZImageVariantType"
}
},
"type": "object",
@@ -50854,11 +54187,12 @@
"format",
"repo_variant",
"base",
- "submodels"
+ "variant"
],
- "title": "Main_Diffusers_SD3_Config"
+ "title": "Main_Diffusers_ZImage_Config",
+ "description": "Model config for Z-Image diffusers models (Z-Image-Turbo, Z-Image-Base)."
},
- "Main_Diffusers_SDXLRefiner_Config": {
+ "Main_GGUF_FLUX_Config": {
"properties": {
"key": {
"type": "string",
@@ -50975,27 +54309,32 @@
],
"description": "Default settings for this model"
},
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
+ "base": {
+ "type": "string",
+ "const": "flux",
+ "title": "Base",
+ "default": "flux"
+ },
"format": {
"type": "string",
- "const": "diffusers",
+ "const": "gguf_quantized",
"title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
- },
- "prediction_type": {
- "$ref": "#/components/schemas/SchedulerPredictionType"
+ "default": "gguf_quantized"
},
"variant": {
- "$ref": "#/components/schemas/ModelVariantType"
- },
- "base": {
- "type": "string",
- "const": "sdxl-refiner",
- "title": "Base",
- "default": "sdxl-refiner"
+ "$ref": "#/components/schemas/FluxVariantType"
}
},
"type": "object",
@@ -51014,15 +54353,15 @@
"type",
"trigger_phrases",
"default_settings",
+ "config_path",
+ "base",
"format",
- "repo_variant",
- "prediction_type",
- "variant",
- "base"
+ "variant"
],
- "title": "Main_Diffusers_SDXLRefiner_Config"
+ "title": "Main_GGUF_FLUX_Config",
+ "description": "Model config for main checkpoint models."
},
- "Main_Diffusers_SDXL_Config": {
+ "Main_GGUF_Flux2_Config": {
"properties": {
"key": {
"type": "string",
@@ -51139,27 +54478,32 @@
],
"description": "Default settings for this model"
},
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
+ "base": {
+ "type": "string",
+ "const": "flux2",
+ "title": "Base",
+ "default": "flux2"
+ },
"format": {
"type": "string",
- "const": "diffusers",
+ "const": "gguf_quantized",
"title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
- },
- "prediction_type": {
- "$ref": "#/components/schemas/SchedulerPredictionType"
+ "default": "gguf_quantized"
},
"variant": {
- "$ref": "#/components/schemas/ModelVariantType"
- },
- "base": {
- "type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
+ "$ref": "#/components/schemas/Flux2VariantType"
}
},
"type": "object",
@@ -51178,15 +54522,15 @@
"type",
"trigger_phrases",
"default_settings",
+ "config_path",
+ "base",
"format",
- "repo_variant",
- "prediction_type",
- "variant",
- "base"
+ "variant"
],
- "title": "Main_Diffusers_SDXL_Config"
+ "title": "Main_GGUF_Flux2_Config",
+ "description": "Model config for GGUF-quantized FLUX.2 checkpoint models (e.g. Klein)."
},
- "Main_Diffusers_ZImage_Config": {
+ "Main_GGUF_QwenImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -51303,24 +54647,39 @@
],
"description": "Default settings for this model"
},
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
"base": {
"type": "string",
- "const": "z-image",
+ "const": "qwen-image",
"title": "Base",
- "default": "z-image"
+ "default": "qwen-image"
+ },
+ "format": {
+ "type": "string",
+ "const": "gguf_quantized",
+ "title": "Format",
+ "default": "gguf_quantized"
},
"variant": {
- "$ref": "#/components/schemas/ZImageVariantType"
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/QwenImageVariantType"
+ },
+ {
+ "type": "null"
+ }
+ ]
}
},
"type": "object",
@@ -51339,15 +54698,15 @@
"type",
"trigger_phrases",
"default_settings",
- "format",
- "repo_variant",
+ "config_path",
"base",
+ "format",
"variant"
],
- "title": "Main_Diffusers_ZImage_Config",
- "description": "Model config for Z-Image diffusers models (Z-Image-Turbo, Z-Image-Base)."
+ "title": "Main_GGUF_QwenImage_Config",
+ "description": "Model config for GGUF-quantized Qwen Image transformer models."
},
- "Main_GGUF_FLUX_Config": {
+ "Main_GGUF_Wan_Config": {
"properties": {
"key": {
"type": "string",
@@ -51478,9 +54837,9 @@
},
"base": {
"type": "string",
- "const": "flux",
+ "const": "wan",
"title": "Base",
- "default": "flux"
+ "default": "wan"
},
"format": {
"type": "string",
@@ -51489,7 +54848,14 @@
"default": "gguf_quantized"
},
"variant": {
- "$ref": "#/components/schemas/FluxVariantType"
+ "$ref": "#/components/schemas/WanVariantType"
+ },
+ "expert": {
+ "type": "string",
+ "enum": ["high", "low", "none"],
+ "title": "Expert",
+ "description": "For Wan 2.2 A14B's dual-expert MoE: 'high' for the high-noise expert, 'low' for the low-noise expert. 'none' for single-transformer models (TI2V-5B).",
+ "default": "none"
}
},
"type": "object",
@@ -51511,12 +54877,13 @@
"config_path",
"base",
"format",
- "variant"
+ "variant",
+ "expert"
],
- "title": "Main_GGUF_FLUX_Config",
- "description": "Model config for main checkpoint models."
+ "title": "Main_GGUF_Wan_Config",
+ "description": "Model config for GGUF-quantized Wan 2.2 transformer models.\n\nA14B's MoE ships as two GGUF files (one per expert); ``expert`` records\nwhich one this is so the model loader invocation can pair them. TI2V-5B\nis a single-transformer model and stores ``expert='none'``."
},
- "Main_GGUF_Flux2_Config": {
+ "Main_GGUF_ZImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -51647,9 +55014,9 @@
},
"base": {
"type": "string",
- "const": "flux2",
+ "const": "z-image",
"title": "Base",
- "default": "flux2"
+ "default": "z-image"
},
"format": {
"type": "string",
@@ -51658,7 +55025,7 @@
"default": "gguf_quantized"
},
"variant": {
- "$ref": "#/components/schemas/Flux2VariantType"
+ "$ref": "#/components/schemas/ZImageVariantType"
}
},
"type": "object",
@@ -51682,359 +55049,554 @@
"format",
"variant"
],
- "title": "Main_GGUF_Flux2_Config",
- "description": "Model config for GGUF-quantized FLUX.2 checkpoint models (e.g. Klein)."
+ "title": "Main_GGUF_ZImage_Config",
+ "description": "Model config for GGUF-quantized Z-Image transformer models."
},
- "Main_GGUF_QwenImage_Config": {
+ "MaskCombineInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "description": {
+ "mask1": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The first mask to combine",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "source_api_response": {
+ "mask2": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "The second image to combine",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "source_url": {
+ "type": {
+ "const": "mask_combine",
+ "default": "mask_combine",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "mask", "multiply"],
+ "title": "Combine Masks",
+ "type": "object",
+ "version": "1.2.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "MaskEdgeInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Applies an edge mask to an image",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "cover_image": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "type": {
- "type": "string",
- "const": "main",
- "title": "Type",
- "default": "main"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "trigger_phrases": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Trigger Phrases",
- "description": "Set of trigger phrases for this model"
+ "default": null,
+ "description": "The image to apply the mask to",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "default_settings": {
+ "edge_size": {
"anyOf": [
{
- "$ref": "#/components/schemas/MainModelDefaultSettings"
+ "type": "integer"
},
{
"type": "null"
}
],
- "description": "Default settings for this model"
+ "default": null,
+ "description": "The size of the edge",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Edge Size"
},
- "config_path": {
+ "edge_blur": {
"anyOf": [
{
- "type": "string"
+ "type": "integer"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "base": {
- "type": "string",
- "const": "qwen-image",
- "title": "Base",
- "default": "qwen-image"
+ "default": null,
+ "description": "The amount of blur on the edge",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Edge Blur"
},
- "format": {
- "type": "string",
- "const": "gguf_quantized",
- "title": "Format",
- "default": "gguf_quantized"
+ "low_threshold": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "First threshold for the hysteresis procedure in Canny edge detection",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Low Threshold"
},
- "variant": {
+ "high_threshold": {
"anyOf": [
{
- "$ref": "#/components/schemas/QwenImageVariantType"
+ "type": "integer"
},
{
"type": "null"
}
- ]
+ ],
+ "default": null,
+ "description": "Second threshold for the hysteresis procedure in Canny edge detection",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "High Threshold"
+ },
+ "type": {
+ "const": "mask_edge",
+ "default": "mask_edge",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["image", "mask", "inpaint"],
+ "title": "Mask Edge",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "type",
- "trigger_phrases",
- "default_settings",
- "config_path",
- "base",
- "format",
- "variant"
- ],
- "title": "Main_GGUF_QwenImage_Config",
- "description": "Model config for GGUF-quantized Qwen Image transformer models."
+ "version": "1.2.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "Main_GGUF_ZImage_Config": {
+ "MaskFromAlphaInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Extracts the alpha channel of an image as a mask.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The image to create the mask from",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "cover_image": {
+ "invert": {
+ "default": false,
+ "description": "Whether or not to invert the mask",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Invert",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "tomask",
+ "default": "tomask",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "mask"],
+ "title": "Mask from Alpha",
+ "type": "object",
+ "version": "1.2.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "MaskFromIDInvocation": {
+ "category": "mask",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generate a mask for a particular color in an ID Map",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "type": {
- "type": "string",
- "const": "main",
- "title": "Type",
- "default": "main"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "trigger_phrases": {
+ "metadata": {
"anyOf": [
{
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Trigger Phrases",
- "description": "Set of trigger phrases for this model"
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "default_settings": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/MainModelDefaultSettings"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "description": "Default settings for this model"
+ "default": null,
+ "description": "The image to create the mask from",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "config_path": {
+ "color": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ColorField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "ID color to mask",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "threshold": {
+ "default": 100,
+ "description": "Threshold for color detection",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 100,
+ "orig_required": false,
+ "title": "Threshold",
+ "type": "integer"
+ },
+ "invert": {
+ "default": false,
+ "description": "Whether or not to invert the mask",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Invert",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "mask_from_id",
+ "default": "mask_from_id",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "mask", "id"],
+ "title": "Mask from Segmented Image",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "MaskOutput": {
+ "class": "output",
+ "description": "A torch mask tensor.",
+ "properties": {
+ "mask": {
+ "$ref": "#/components/schemas/TensorField",
+ "description": "The mask.",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "base": {
- "type": "string",
- "const": "z-image",
- "title": "Base",
- "default": "z-image"
+ "width": {
+ "description": "The width of the mask in pixels.",
+ "field_kind": "output",
+ "title": "Width",
+ "type": "integer",
+ "ui_hidden": false
},
- "format": {
- "type": "string",
- "const": "gguf_quantized",
- "title": "Format",
- "default": "gguf_quantized"
+ "height": {
+ "description": "The height of the mask in pixels.",
+ "field_kind": "output",
+ "title": "Height",
+ "type": "integer",
+ "ui_hidden": false
},
- "variant": {
- "$ref": "#/components/schemas/ZImageVariantType"
+ "type": {
+ "const": "mask_output",
+ "default": "mask_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "type",
- "trigger_phrases",
- "default_settings",
- "config_path",
- "base",
- "format",
- "variant"
- ],
- "title": "Main_GGUF_ZImage_Config",
- "description": "Model config for GGUF-quantized Z-Image transformer models."
+ "required": ["output_meta", "mask", "width", "height", "type", "type"],
+ "title": "MaskOutput",
+ "type": "object"
},
- "MaskCombineInvocation": {
+ "MaskTensorToImageInvocation": {
"category": "mask",
"class": "invocation",
"classification": "stable",
- "description": "Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`.",
+ "description": "Convert a mask tensor to an image.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -52093,58 +55655,43 @@
"title": "Use Cache",
"type": "boolean"
},
- "mask1": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The first mask to combine",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "mask2": {
+ "mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/TensorField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The second image to combine",
+ "description": "The mask tensor to convert.",
"field_kind": "input",
"input": "any",
"orig_required": true
},
"type": {
- "const": "mask_combine",
- "default": "mask_combine",
+ "const": "tensor_mask_to_image",
+ "default": "tensor_mask_to_image",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "mask", "multiply"],
- "title": "Combine Masks",
+ "tags": ["mask"],
+ "title": "Tensor Mask to Image",
"type": "object",
- "version": "1.2.2",
+ "version": "1.1.0",
"output": {
"$ref": "#/components/schemas/ImageOutput"
}
},
- "MaskEdgeInvocation": {
- "category": "mask",
+ "MediaPipeFaceDetectionInvocation": {
+ "category": "controlnet_preprocessors",
"class": "invocation",
"classification": "stable",
- "description": "Applies an edge mask to an image",
+ "description": "Detects faces using MediaPipe.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -52213,97 +55760,123 @@
}
],
"default": null,
- "description": "The image to apply the mask to",
+ "description": "The image to process",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "edge_size": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The size of the edge",
+ "max_faces": {
+ "default": 1,
+ "description": "Maximum number of faces to detect",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Edge Size"
+ "minimum": 1,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Max Faces",
+ "type": "integer"
},
- "edge_blur": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The amount of blur on the edge",
+ "min_confidence": {
+ "default": 0.5,
+ "description": "Minimum confidence for face detection",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Edge Blur"
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.5,
+ "orig_required": false,
+ "title": "Min Confidence",
+ "type": "number"
},
- "low_threshold": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "First threshold for the hysteresis procedure in Canny edge detection",
- "field_kind": "input",
- "input": "any",
+ "type": {
+ "const": "mediapipe_face_detection",
+ "default": "mediapipe_face_detection",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["controlnet", "face"],
+ "title": "MediaPipe Face Detection",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "MergeMetadataInvocation": {
+ "category": "metadata",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Merged a collection of MetadataDict into a single MetadataDict.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
"orig_required": true,
- "title": "Low Threshold"
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "high_threshold": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "collection": {
"anyOf": [
{
- "type": "integer"
+ "items": {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Second threshold for the hysteresis procedure in Canny edge detection",
+ "description": "Collection of Metadata",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "title": "High Threshold"
+ "title": "Collection"
},
"type": {
- "const": "mask_edge",
- "default": "mask_edge",
+ "const": "merge_metadata",
+ "default": "merge_metadata",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "mask", "inpaint"],
- "title": "Mask Edge",
+ "tags": ["metadata"],
+ "title": "Metadata Merge",
"type": "object",
- "version": "1.2.2",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/MetadataOutput"
}
},
- "MaskFromAlphaInvocation": {
- "category": "mask",
+ "MergeTilesToImageInvocation": {
+ "category": "tiles",
"class": "invocation",
"classification": "stable",
- "description": "Extracts the alpha channel of an image as a mask.",
+ "description": "Merge multiple tile images into a single image.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -52362,87 +55935,156 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "tiles_with_images": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "items": {
+ "$ref": "#/components/schemas/TileWithImage"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to create the mask from",
+ "description": "A list of tile images with tile properties.",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_required": true,
+ "title": "Tiles With Images"
},
- "invert": {
- "default": false,
- "description": "Whether or not to invert the mask",
+ "blend_mode": {
+ "default": "Seam",
+ "description": "blending type Linear or Seam",
+ "enum": ["Linear", "Seam"],
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": "Seam",
+ "orig_required": false,
+ "title": "Blend Mode",
+ "type": "string"
+ },
+ "blend_amount": {
+ "default": 32,
+ "description": "The amount to blend adjacent tiles in pixels. Must be <= the amount of overlap between adjacent tiles.",
"field_kind": "input",
"input": "any",
- "orig_default": false,
+ "minimum": 0,
+ "orig_default": 32,
"orig_required": false,
- "title": "Invert",
- "type": "boolean"
+ "title": "Blend Amount",
+ "type": "integer"
},
"type": {
- "const": "tomask",
- "default": "tomask",
+ "const": "merge_tiles_to_image",
+ "default": "merge_tiles_to_image",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "mask"],
- "title": "Mask from Alpha",
+ "tags": ["tiles"],
+ "title": "Merge Tiles to Image",
"type": "object",
- "version": "1.2.2",
+ "version": "1.1.1",
"output": {
"$ref": "#/components/schemas/ImageOutput"
}
},
- "MaskFromIDInvocation": {
- "category": "mask",
+ "MetadataField": {
+ "additionalProperties": true,
+ "type": "object",
+ "title": "MetadataField",
+ "description": "Pydantic model for metadata with custom root of type dict[str, Any].\nMetadata is stored without a strict schema."
+ },
+ "MetadataFieldExtractorInvocation": {
+ "category": "metadata",
"class": "invocation",
- "classification": "stable",
- "description": "Generate a mask for a particular color in an ID Map",
+ "classification": "deprecated",
+ "description": "Extracts the text value from an image's metadata given a key.\nRaises an error if the image has no metadata or if the value is not a string (nesting not permitted).",
"node_pack": "invokeai",
"properties": {
- "board": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "description": "The image to extract metadata from",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "metadata": {
+ "key": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "description": "The key in the image's metadata to extract the value from",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Key"
},
+ "type": {
+ "const": "metadata_field_extractor",
+ "default": "metadata_field_extractor",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["metadata"],
+ "title": "Metadata Field Extractor",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/StringOutput"
+ }
+ },
+ "MetadataFromImageInvocation": {
+ "category": "metadata",
+ "class": "invocation",
+ "classification": "beta",
+ "description": "Used to create a core metadata item then Add/Update it to the provided metadata",
+ "node_pack": "invokeai",
+ "properties": {
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -52477,138 +56119,121 @@
}
],
"default": null,
- "description": "The image to create the mask from",
+ "description": "The image to process",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "color": {
+ "type": {
+ "const": "metadata_from_image",
+ "default": "metadata_from_image",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["metadata"],
+ "title": "Metadata From Image",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/MetadataOutput"
+ }
+ },
+ "MetadataInvocation": {
+ "category": "metadata",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Takes a MetadataItem or collection of MetadataItems and outputs a MetadataDict.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "items": {
"anyOf": [
{
- "$ref": "#/components/schemas/ColorField"
+ "items": {
+ "$ref": "#/components/schemas/MetadataItemField"
+ },
+ "type": "array"
+ },
+ {
+ "$ref": "#/components/schemas/MetadataItemField"
},
{
"type": "null"
}
],
"default": null,
- "description": "ID color to mask",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "threshold": {
- "default": 100,
- "description": "Threshold for color detection",
- "field_kind": "input",
- "input": "any",
- "orig_default": 100,
- "orig_required": false,
- "title": "Threshold",
- "type": "integer"
- },
- "invert": {
- "default": false,
- "description": "Whether or not to invert the mask",
+ "description": "A single metadata item or collection of metadata items",
"field_kind": "input",
"input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Invert",
- "type": "boolean"
+ "orig_required": true,
+ "title": "Items"
},
"type": {
- "const": "mask_from_id",
- "default": "mask_from_id",
+ "const": "metadata",
+ "default": "metadata",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "mask", "id"],
- "title": "Mask from Segmented Image",
+ "tags": ["metadata"],
+ "title": "Metadata",
"type": "object",
"version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/MetadataOutput"
}
},
- "MaskOutput": {
- "class": "output",
- "description": "A torch mask tensor.",
+ "MetadataItemField": {
"properties": {
- "mask": {
- "$ref": "#/components/schemas/TensorField",
- "description": "The mask.",
- "field_kind": "output",
- "ui_hidden": false
- },
- "width": {
- "description": "The width of the mask in pixels.",
- "field_kind": "output",
- "title": "Width",
- "type": "integer",
- "ui_hidden": false
- },
- "height": {
- "description": "The height of the mask in pixels.",
- "field_kind": "output",
- "title": "Height",
- "type": "integer",
- "ui_hidden": false
- },
- "type": {
- "const": "mask_output",
- "default": "mask_output",
- "field_kind": "node_attribute",
- "title": "type",
+ "label": {
+ "description": "Label for this metadata item",
+ "title": "Label",
"type": "string"
+ },
+ "value": {
+ "description": "The value for this metadata item (may be any type)",
+ "title": "Value"
}
},
- "required": ["output_meta", "mask", "width", "height", "type", "type"],
- "title": "MaskOutput",
+ "required": ["label", "value"],
+ "title": "MetadataItemField",
"type": "object"
},
- "MaskTensorToImageInvocation": {
- "category": "mask",
+ "MetadataItemInvocation": {
+ "category": "metadata",
"class": "invocation",
"classification": "stable",
- "description": "Convert a mask tensor to an image.",
+ "description": "Used to create an arbitrary metadata item. Provide \"label\" and make a connection to \"value\" to store that data as the value.",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -52633,61 +56258,61 @@
"title": "Use Cache",
"type": "boolean"
},
- "mask": {
+ "label": {
"anyOf": [
{
- "$ref": "#/components/schemas/TensorField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "The mask tensor to convert.",
+ "description": "Label for this metadata item",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_required": true,
+ "title": "Label"
+ },
+ "value": {
+ "anyOf": [
+ {},
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The value for this metadata item (may be any type)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Value",
+ "ui_type": "AnyField"
},
"type": {
- "const": "tensor_mask_to_image",
- "default": "tensor_mask_to_image",
+ "const": "metadata_item",
+ "default": "metadata_item",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["mask"],
- "title": "Tensor Mask to Image",
+ "tags": ["metadata"],
+ "title": "Metadata Item",
"type": "object",
- "version": "1.1.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/MetadataItemOutput"
}
},
- "MediaPipeFaceDetectionInvocation": {
- "category": "controlnet_preprocessors",
+ "MetadataItemLinkedInvocation": {
+ "category": "metadata",
"class": "invocation",
- "classification": "stable",
- "description": "Detects faces using MediaPipe.",
+ "classification": "beta",
+ "description": "Used to Create/Add/Update a value into a metadata label",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
"metadata": {
"anyOf": [
{
@@ -52728,68 +56353,153 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "label": {
+ "default": "* CUSTOM LABEL *",
+ "description": "Label for this metadata item",
+ "enum": [
+ "* CUSTOM LABEL *",
+ "positive_prompt",
+ "positive_style_prompt",
+ "negative_prompt",
+ "negative_style_prompt",
+ "width",
+ "height",
+ "seed",
+ "cfg_scale",
+ "cfg_rescale_multiplier",
+ "steps",
+ "scheduler",
+ "clip_skip",
+ "model",
+ "vae",
+ "seamless_x",
+ "seamless_y",
+ "guidance",
+ "cfg_scale_start_step",
+ "cfg_scale_end_step"
+ ],
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": "* CUSTOM LABEL *",
+ "orig_required": false,
+ "title": "Label",
+ "type": "string"
+ },
+ "custom_label": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to process",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "max_faces": {
- "default": 1,
- "description": "Maximum number of faces to detect",
+ "description": "Label for this metadata item",
"field_kind": "input",
- "input": "any",
- "minimum": 1,
- "orig_default": 1,
+ "input": "direct",
+ "orig_default": null,
"orig_required": false,
- "title": "Max Faces",
- "type": "integer"
+ "title": "Custom Label"
},
- "min_confidence": {
- "default": 0.5,
- "description": "Minimum confidence for face detection",
+ "value": {
+ "anyOf": [
+ {},
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The value for this metadata item (may be any type)",
"field_kind": "input",
"input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.5,
- "orig_required": false,
- "title": "Min Confidence",
- "type": "number"
+ "orig_required": true,
+ "title": "Value",
+ "ui_type": "AnyField"
},
"type": {
- "const": "mediapipe_face_detection",
- "default": "mediapipe_face_detection",
+ "const": "metadata_item_linked",
+ "default": "metadata_item_linked",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["controlnet", "face"],
- "title": "MediaPipe Face Detection",
+ "tags": ["metadata"],
+ "title": "Metadata Item Linked",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/MetadataOutput"
}
},
- "MergeMetadataInvocation": {
+ "MetadataItemOutput": {
+ "class": "output",
+ "description": "Metadata Item Output",
+ "properties": {
+ "item": {
+ "$ref": "#/components/schemas/MetadataItemField",
+ "description": "Metadata Item",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "metadata_item_output",
+ "default": "metadata_item_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "item", "type", "type"],
+ "title": "MetadataItemOutput",
+ "type": "object"
+ },
+ "MetadataOutput": {
+ "class": "output",
+ "properties": {
+ "metadata": {
+ "$ref": "#/components/schemas/MetadataField",
+ "description": "Metadata Dict",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "metadata_output",
+ "default": "metadata_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "metadata", "type", "type"],
+ "title": "MetadataOutput",
+ "type": "object"
+ },
+ "MetadataToBoolCollectionInvocation": {
"category": "metadata",
"class": "invocation",
- "classification": "stable",
- "description": "Merged a collection of MetadataDict into a single MetadataDict.",
+ "classification": "beta",
+ "description": "Extracts a Boolean value Collection of a label from metadata",
"node_pack": "invokeai",
"properties": {
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -52814,11 +56524,39 @@
"title": "Use Cache",
"type": "boolean"
},
- "collection": {
+ "label": {
+ "default": "* CUSTOM LABEL *",
+ "description": "Label for this metadata item",
+ "enum": ["* CUSTOM LABEL *", "seamless_x", "seamless_y"],
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": "* CUSTOM LABEL *",
+ "orig_required": false,
+ "title": "Label",
+ "type": "string"
+ },
+ "custom_label": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Label for this metadata item",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Custom Label"
+ },
+ "default_value": {
"anyOf": [
{
"items": {
- "$ref": "#/components/schemas/MetadataField"
+ "type": "boolean"
},
"type": "array"
},
@@ -52827,15 +56565,15 @@
}
],
"default": null,
- "description": "Collection of Metadata",
+ "description": "The default bool to use if not found in the metadata",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "title": "Collection"
+ "title": "Default Value"
},
"type": {
- "const": "merge_metadata",
- "default": "merge_metadata",
+ "const": "metadata_to_bool_collection",
+ "default": "metadata_to_bool_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -52843,36 +56581,20 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata Merge",
+ "title": "Metadata To Bool Collection",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/MetadataOutput"
+ "$ref": "#/components/schemas/BooleanCollectionOutput"
}
},
- "MergeTilesToImageInvocation": {
- "category": "tiles",
+ "MetadataToBoolInvocation": {
+ "category": "metadata",
"class": "invocation",
- "classification": "stable",
- "description": "Merge multiple tile images into a single image.",
+ "classification": "beta",
+ "description": "Extracts a Boolean value of a label from metadata",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
"metadata": {
"anyOf": [
{
@@ -52913,77 +56635,90 @@
"title": "Use Cache",
"type": "boolean"
},
- "tiles_with_images": {
+ "label": {
+ "default": "* CUSTOM LABEL *",
+ "description": "Label for this metadata item",
+ "enum": ["* CUSTOM LABEL *", "seamless_x", "seamless_y"],
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": "* CUSTOM LABEL *",
+ "orig_required": false,
+ "title": "Label",
+ "type": "string"
+ },
+ "custom_label": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/TileWithImage"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "A list of tile images with tile properties.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Tiles With Images"
- },
- "blend_mode": {
- "default": "Seam",
- "description": "blending type Linear or Seam",
- "enum": ["Linear", "Seam"],
+ "description": "Label for this metadata item",
"field_kind": "input",
"input": "direct",
- "orig_default": "Seam",
+ "orig_default": null,
"orig_required": false,
- "title": "Blend Mode",
- "type": "string"
+ "title": "Custom Label"
},
- "blend_amount": {
- "default": 32,
- "description": "The amount to blend adjacent tiles in pixels. Must be <= the amount of overlap between adjacent tiles.",
+ "default_value": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The default bool to use if not found in the metadata",
"field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 32,
- "orig_required": false,
- "title": "Blend Amount",
- "type": "integer"
+ "orig_required": true,
+ "title": "Default Value"
},
"type": {
- "const": "merge_tiles_to_image",
- "default": "merge_tiles_to_image",
+ "const": "metadata_to_bool",
+ "default": "metadata_to_bool",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["tiles"],
- "title": "Merge Tiles to Image",
+ "tags": ["metadata"],
+ "title": "Metadata To Bool",
"type": "object",
- "version": "1.1.1",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/BooleanOutput"
}
},
- "MetadataField": {
- "additionalProperties": true,
- "type": "object",
- "title": "MetadataField",
- "description": "Pydantic model for metadata with custom root of type dict[str, Any].\nMetadata is stored without a strict schema."
- },
- "MetadataFieldExtractorInvocation": {
+ "MetadataToControlnetsInvocation": {
"category": "metadata",
"class": "invocation",
- "classification": "deprecated",
- "description": "Extracts the text value from an image's metadata given a key.\nRaises an error if the image has no metadata or if the value is not a string (nesting not permitted).",
+ "classification": "beta",
+ "description": "Extracts a Controlnets value of a label from metadata",
"node_pack": "invokeai",
"properties": {
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -53008,40 +56743,31 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "control_list": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/ControlField"
},
{
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to extract metadata from",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "key": {
- "anyOf": [
- {
- "type": "string"
+ "items": {
+ "$ref": "#/components/schemas/ControlField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "The key in the image's metadata to extract the value from",
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Key"
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "ControlNet-List"
},
"type": {
- "const": "metadata_field_extractor",
- "default": "metadata_field_extractor",
+ "const": "metadata_to_controlnets",
+ "default": "metadata_to_controlnets",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53049,20 +56775,36 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata Field Extractor",
+ "title": "Metadata To ControlNets",
"type": "object",
- "version": "1.0.0",
+ "version": "1.2.0",
"output": {
- "$ref": "#/components/schemas/StringOutput"
+ "$ref": "#/components/schemas/MDControlListOutput"
}
},
- "MetadataFromImageInvocation": {
+ "MetadataToFloatCollectionInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Used to create a core metadata item then Add/Update it to the provided metadata",
+ "description": "Extracts a Float value Collection of a label from metadata",
"node_pack": "invokeai",
"properties": {
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -53087,24 +56829,56 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "label": {
+ "default": "* CUSTOM LABEL *",
+ "description": "Label for this metadata item",
+ "enum": ["* CUSTOM LABEL *", "cfg_scale", "cfg_rescale_multiplier", "guidance"],
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": "* CUSTOM LABEL *",
+ "orig_required": false,
+ "title": "Label",
+ "type": "string"
+ },
+ "custom_label": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to process",
+ "description": "Label for this metadata item",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Custom Label"
+ },
+ "default_value": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The default float to use if not found in the metadata",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_required": true,
+ "title": "Default Value"
},
"type": {
- "const": "metadata_from_image",
- "default": "metadata_from_image",
+ "const": "metadata_to_float_collection",
+ "default": "metadata_to_float_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53112,20 +56886,36 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata From Image",
+ "title": "Metadata To Float Collection",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/MetadataOutput"
+ "$ref": "#/components/schemas/FloatCollectionOutput"
}
},
- "MetadataInvocation": {
+ "MetadataToFloatInvocation": {
"category": "metadata",
"class": "invocation",
- "classification": "stable",
- "description": "Takes a MetadataItem or collection of MetadataItems and outputs a MetadataDict.",
+ "classification": "beta",
+ "description": "Extracts a Float value of a label from metadata",
"node_pack": "invokeai",
"properties": {
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -53150,31 +56940,53 @@
"title": "Use Cache",
"type": "boolean"
},
- "items": {
+ "label": {
+ "default": "* CUSTOM LABEL *",
+ "description": "Label for this metadata item",
+ "enum": ["* CUSTOM LABEL *", "cfg_scale", "cfg_rescale_multiplier", "guidance"],
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": "* CUSTOM LABEL *",
+ "orig_required": false,
+ "title": "Label",
+ "type": "string"
+ },
+ "custom_label": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/MetadataItemField"
- },
- "type": "array"
+ "type": "string"
},
{
- "$ref": "#/components/schemas/MetadataItemField"
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Label for this metadata item",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Custom Label"
+ },
+ "default_value": {
+ "anyOf": [
+ {
+ "type": "number"
},
{
"type": "null"
}
],
"default": null,
- "description": "A single metadata item or collection of metadata items",
+ "description": "The default float to use if not found in the metadata",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "title": "Items"
+ "title": "Default Value"
},
"type": {
- "const": "metadata",
- "default": "metadata",
+ "const": "metadata_to_float",
+ "default": "metadata_to_float",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53182,36 +56994,36 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata",
+ "title": "Metadata To Float",
"type": "object",
- "version": "1.0.1",
+ "version": "1.1.0",
"output": {
- "$ref": "#/components/schemas/MetadataOutput"
+ "$ref": "#/components/schemas/FloatOutput"
}
},
- "MetadataItemField": {
- "properties": {
- "label": {
- "description": "Label for this metadata item",
- "title": "Label",
- "type": "string"
- },
- "value": {
- "description": "The value for this metadata item (may be any type)",
- "title": "Value"
- }
- },
- "required": ["label", "value"],
- "title": "MetadataItemField",
- "type": "object"
- },
- "MetadataItemInvocation": {
+ "MetadataToIPAdaptersInvocation": {
"category": "metadata",
"class": "invocation",
- "classification": "stable",
- "description": "Used to create an arbitrary metadata item. Provide \"label\" and make a connection to \"value\" to store that data as the value.",
+ "classification": "beta",
+ "description": "Extracts a IP-Adapters value of a label from metadata",
"node_pack": "invokeai",
"properties": {
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -53236,40 +57048,32 @@
"title": "Use Cache",
"type": "boolean"
},
- "label": {
+ "ip_adapter_list": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/IPAdapterField"
},
{
- "type": "null"
- }
- ],
- "default": null,
- "description": "Label for this metadata item",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Label"
- },
- "value": {
- "anyOf": [
- {},
+ "items": {
+ "$ref": "#/components/schemas/IPAdapterField"
+ },
+ "type": "array"
+ },
{
"type": "null"
}
],
"default": null,
- "description": "The value for this metadata item (may be any type)",
+ "description": "IP-Adapter to apply",
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Value",
- "ui_type": "AnyField"
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "IP-Adapter-List"
},
"type": {
- "const": "metadata_item",
- "default": "metadata_item",
+ "const": "metadata_to_ip_adapters",
+ "default": "metadata_to_ip_adapters",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53277,18 +57081,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata Item",
+ "title": "Metadata To IP-Adapters",
"type": "object",
- "version": "1.0.1",
+ "version": "1.2.0",
"output": {
- "$ref": "#/components/schemas/MetadataItemOutput"
+ "$ref": "#/components/schemas/MDIPAdapterListOutput"
}
},
- "MetadataItemLinkedInvocation": {
+ "MetadataToIntegerCollectionInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Used to Create/Add/Update a value into a metadata label",
+ "description": "Extracts an integer value Collection of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -53336,23 +57140,11 @@
"description": "Label for this metadata item",
"enum": [
"* CUSTOM LABEL *",
- "positive_prompt",
- "positive_style_prompt",
- "negative_prompt",
- "negative_style_prompt",
"width",
"height",
"seed",
- "cfg_scale",
- "cfg_rescale_multiplier",
"steps",
- "scheduler",
"clip_skip",
- "model",
- "vae",
- "seamless_x",
- "seamless_y",
- "guidance",
"cfg_scale_start_step",
"cfg_scale_end_step"
],
@@ -53380,24 +57172,28 @@
"orig_required": false,
"title": "Custom Label"
},
- "value": {
+ "default_value": {
"anyOf": [
- {},
+ {
+ "items": {
+ "type": "integer"
+ },
+ "type": "array"
+ },
{
"type": "null"
}
],
"default": null,
- "description": "The value for this metadata item (may be any type)",
+ "description": "The default integer to use if not found in the metadata",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "title": "Value",
- "ui_type": "AnyField"
+ "title": "Default Value"
},
"type": {
- "const": "metadata_item_linked",
- "default": "metadata_item_linked",
+ "const": "metadata_to_integer_collection",
+ "default": "metadata_to_integer_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53405,61 +57201,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata Item Linked",
+ "title": "Metadata To Integer Collection",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/MetadataOutput"
+ "$ref": "#/components/schemas/IntegerCollectionOutput"
}
},
- "MetadataItemOutput": {
- "class": "output",
- "description": "Metadata Item Output",
- "properties": {
- "item": {
- "$ref": "#/components/schemas/MetadataItemField",
- "description": "Metadata Item",
- "field_kind": "output",
- "ui_hidden": false
- },
- "type": {
- "const": "metadata_item_output",
- "default": "metadata_item_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "item", "type", "type"],
- "title": "MetadataItemOutput",
- "type": "object"
- },
- "MetadataOutput": {
- "class": "output",
- "properties": {
- "metadata": {
- "$ref": "#/components/schemas/MetadataField",
- "description": "Metadata Dict",
- "field_kind": "output",
- "ui_hidden": false
- },
- "type": {
- "const": "metadata_output",
- "default": "metadata_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "metadata", "type", "type"],
- "title": "MetadataOutput",
- "type": "object"
- },
- "MetadataToBoolCollectionInvocation": {
+ "MetadataToIntegerInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a Boolean value Collection of a label from metadata",
+ "description": "Extracts an integer value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -53505,7 +57258,16 @@
"label": {
"default": "* CUSTOM LABEL *",
"description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "seamless_x", "seamless_y"],
+ "enum": [
+ "* CUSTOM LABEL *",
+ "width",
+ "height",
+ "seed",
+ "steps",
+ "clip_skip",
+ "cfg_scale_start_step",
+ "cfg_scale_end_step"
+ ],
"field_kind": "input",
"input": "direct",
"orig_default": "* CUSTOM LABEL *",
@@ -53533,25 +57295,22 @@
"default_value": {
"anyOf": [
{
- "items": {
- "type": "boolean"
- },
- "type": "array"
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "The default bool to use if not found in the metadata",
+ "description": "The default integer to use if not found in the metadata",
"field_kind": "input",
"input": "any",
"orig_required": true,
"title": "Default Value"
},
"type": {
- "const": "metadata_to_bool_collection",
- "default": "metadata_to_bool_collection",
+ "const": "metadata_to_integer",
+ "default": "metadata_to_integer",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53559,18 +57318,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To Bool Collection",
+ "title": "Metadata To Integer",
"type": "object",
"version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/BooleanCollectionOutput"
+ "$ref": "#/components/schemas/IntegerOutput"
}
},
- "MetadataToBoolInvocation": {
+ "MetadataToLorasCollectionInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a Boolean value of a label from metadata",
+ "description": "Extracts Lora(s) from metadata into a collection",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -53613,53 +57372,42 @@
"title": "Use Cache",
"type": "boolean"
},
- "label": {
- "default": "* CUSTOM LABEL *",
+ "custom_label": {
+ "default": "loras",
"description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "seamless_x", "seamless_y"],
"field_kind": "input",
"input": "direct",
- "orig_default": "* CUSTOM LABEL *",
+ "orig_default": "loras",
"orig_required": false,
- "title": "Label",
+ "title": "Custom Label",
"type": "string"
},
- "custom_label": {
+ "loras": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LoRAField"
},
{
- "type": "null"
- }
- ],
- "default": null,
- "description": "Label for this metadata item",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Custom Label"
- },
- "default_value": {
- "anyOf": [
- {
- "type": "boolean"
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The default bool to use if not found in the metadata",
+ "default": [],
+ "description": "LoRA models and weights. May be a single LoRA or collection.",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Default Value"
+ "orig_default": [],
+ "orig_required": false,
+ "title": "LoRAs"
},
"type": {
- "const": "metadata_to_bool",
- "default": "metadata_to_bool",
+ "const": "metadata_to_lora_collection",
+ "default": "metadata_to_lora_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53667,18 +57415,44 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To Bool",
+ "title": "Metadata To LoRA Collection",
"type": "object",
- "version": "1.0.0",
+ "version": "1.1.0",
"output": {
- "$ref": "#/components/schemas/BooleanOutput"
+ "$ref": "#/components/schemas/MetadataToLorasCollectionOutput"
}
},
- "MetadataToControlnetsInvocation": {
+ "MetadataToLorasCollectionOutput": {
+ "class": "output",
+ "description": "Model loader output",
+ "properties": {
+ "lora": {
+ "description": "Collection of LoRA model and weights",
+ "field_kind": "output",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "LoRAs",
+ "type": "array",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "metadata_to_lora_collection_output",
+ "default": "metadata_to_lora_collection_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "lora", "type", "type"],
+ "title": "MetadataToLorasCollectionOutput",
+ "type": "object"
+ },
+ "MetadataToLorasInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a Controlnets value of a label from metadata",
+ "description": "Extracts a Loras value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -53721,31 +57495,43 @@
"title": "Use Cache",
"type": "boolean"
},
- "control_list": {
+ "unet": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlField"
+ "$ref": "#/components/schemas/UNetField"
},
{
- "items": {
- "$ref": "#/components/schemas/ControlField"
- },
- "type": "array"
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "UNet"
+ },
+ "clip": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "ControlNet-List"
+ "title": "CLIP"
},
"type": {
- "const": "metadata_to_controlnets",
- "default": "metadata_to_controlnets",
+ "const": "metadata_to_loras",
+ "default": "metadata_to_loras",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53753,18 +57539,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To ControlNets",
+ "title": "Metadata To LoRAs",
"type": "object",
- "version": "1.2.0",
+ "version": "1.1.1",
"output": {
- "$ref": "#/components/schemas/MDControlListOutput"
+ "$ref": "#/components/schemas/LoRALoaderOutput"
}
},
- "MetadataToFloatCollectionInvocation": {
+ "MetadataToModelInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a Float value Collection of a label from metadata",
+ "description": "Extracts a Model value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -53808,12 +57594,12 @@
"type": "boolean"
},
"label": {
- "default": "* CUSTOM LABEL *",
+ "default": "model",
"description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "cfg_scale", "cfg_rescale_multiplier", "guidance"],
+ "enum": ["* CUSTOM LABEL *", "model"],
"field_kind": "input",
"input": "direct",
- "orig_default": "* CUSTOM LABEL *",
+ "orig_default": "model",
"orig_required": false,
"title": "Label",
"type": "string"
@@ -53838,25 +57624,22 @@
"default_value": {
"anyOf": [
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The default float to use if not found in the metadata",
+ "description": "The default model to use if not found in the metadata",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "title": "Default Value"
+ "ui_model_type": ["main"]
},
"type": {
- "const": "metadata_to_float_collection",
- "default": "metadata_to_float_collection",
+ "const": "metadata_to_model",
+ "default": "metadata_to_model",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53864,18 +57647,69 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To Float Collection",
+ "title": "Metadata To Model",
"type": "object",
- "version": "1.0.0",
+ "version": "1.3.0",
"output": {
- "$ref": "#/components/schemas/FloatCollectionOutput"
+ "$ref": "#/components/schemas/MetadataToModelOutput"
}
},
- "MetadataToFloatInvocation": {
+ "MetadataToModelOutput": {
+ "class": "output",
+ "description": "String to main model output",
+ "properties": {
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Main model (UNet, VAE, CLIP) to load",
+ "field_kind": "output",
+ "title": "Model",
+ "ui_hidden": false
+ },
+ "name": {
+ "description": "Model Name",
+ "field_kind": "output",
+ "title": "Name",
+ "type": "string",
+ "ui_hidden": false
+ },
+ "unet": {
+ "$ref": "#/components/schemas/UNetField",
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
+ },
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
+ },
+ "clip": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "metadata_to_model_output",
+ "default": "metadata_to_model_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "model", "name", "unet", "vae", "clip", "type", "type"],
+ "title": "MetadataToModelOutput",
+ "type": "object"
+ },
+ "MetadataToSDXLLorasInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a Float value of a label from metadata",
+ "description": "Extracts a SDXL Loras value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -53918,53 +57752,60 @@
"title": "Use Cache",
"type": "boolean"
},
- "label": {
- "default": "* CUSTOM LABEL *",
- "description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "cfg_scale", "cfg_rescale_multiplier", "guidance"],
+ "unet": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/UNetField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
"field_kind": "input",
- "input": "direct",
- "orig_default": "* CUSTOM LABEL *",
+ "input": "connection",
+ "orig_default": null,
"orig_required": false,
- "title": "Label",
- "type": "string"
+ "title": "UNet"
},
- "custom_label": {
+ "clip": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Label for this metadata item",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
- "input": "direct",
+ "input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Custom Label"
+ "title": "CLIP 1"
},
- "default_value": {
+ "clip2": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The default float to use if not found in the metadata",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Default Value"
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "CLIP 2"
},
"type": {
- "const": "metadata_to_float",
- "default": "metadata_to_float",
+ "const": "metadata_to_sdlx_loras",
+ "default": "metadata_to_sdlx_loras",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -53972,18 +57813,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To Float",
+ "title": "Metadata To SDXL LoRAs",
"type": "object",
- "version": "1.1.0",
+ "version": "1.1.1",
"output": {
- "$ref": "#/components/schemas/FloatOutput"
+ "$ref": "#/components/schemas/SDXLLoRALoaderOutput"
}
},
- "MetadataToIPAdaptersInvocation": {
+ "MetadataToSDXLModelInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a IP-Adapters value of a label from metadata",
+ "description": "Extracts a SDXL Model value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -54026,32 +57867,54 @@
"title": "Use Cache",
"type": "boolean"
},
- "ip_adapter_list": {
+ "label": {
+ "default": "model",
+ "description": "Label for this metadata item",
+ "enum": ["* CUSTOM LABEL *", "model"],
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": "model",
+ "orig_required": false,
+ "title": "Label",
+ "type": "string"
+ },
+ "custom_label": {
"anyOf": [
{
- "$ref": "#/components/schemas/IPAdapterField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/IPAdapterField"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "IP-Adapter to apply",
+ "description": "Label for this metadata item",
"field_kind": "input",
- "input": "connection",
+ "input": "direct",
"orig_default": null,
"orig_required": false,
- "title": "IP-Adapter-List"
+ "title": "Custom Label"
+ },
+ "default_value": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The default SDXL Model to use if not found in the metadata",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "ui_model_base": ["sdxl"],
+ "ui_model_type": ["main"]
},
"type": {
- "const": "metadata_to_ip_adapters",
- "default": "metadata_to_ip_adapters",
+ "const": "metadata_to_sdxl_model",
+ "default": "metadata_to_sdxl_model",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -54059,18 +57922,76 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To IP-Adapters",
+ "title": "Metadata To SDXL Model",
"type": "object",
- "version": "1.2.0",
+ "version": "1.3.0",
"output": {
- "$ref": "#/components/schemas/MDIPAdapterListOutput"
+ "$ref": "#/components/schemas/MetadataToSDXLModelOutput"
}
},
- "MetadataToIntegerCollectionInvocation": {
+ "MetadataToSDXLModelOutput": {
+ "class": "output",
+ "description": "String to SDXL main model output",
+ "properties": {
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Main model (UNet, VAE, CLIP) to load",
+ "field_kind": "output",
+ "title": "Model",
+ "ui_hidden": false
+ },
+ "name": {
+ "description": "Model Name",
+ "field_kind": "output",
+ "title": "Name",
+ "type": "string",
+ "ui_hidden": false
+ },
+ "unet": {
+ "$ref": "#/components/schemas/UNetField",
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
+ },
+ "clip": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP 1",
+ "ui_hidden": false
+ },
+ "clip2": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP 2",
+ "ui_hidden": false
+ },
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "metadata_to_sdxl_model_output",
+ "default": "metadata_to_sdxl_model_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "model", "name", "unet", "clip", "clip2", "vae", "type", "type"],
+ "title": "MetadataToSDXLModelOutput",
+ "type": "object"
+ },
+ "MetadataToSchedulerInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts an integer value Collection of a label from metadata",
+ "description": "Extracts a Scheduler value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -54114,21 +58035,12 @@
"type": "boolean"
},
"label": {
- "default": "* CUSTOM LABEL *",
+ "default": "scheduler",
"description": "Label for this metadata item",
- "enum": [
- "* CUSTOM LABEL *",
- "width",
- "height",
- "seed",
- "steps",
- "clip_skip",
- "cfg_scale_start_step",
- "cfg_scale_end_step"
- ],
+ "enum": ["* CUSTOM LABEL *", "scheduler"],
"field_kind": "input",
"input": "direct",
- "orig_default": "* CUSTOM LABEL *",
+ "orig_default": "scheduler",
"orig_required": false,
"title": "Label",
"type": "string"
@@ -54151,27 +58063,52 @@
"title": "Custom Label"
},
"default_value": {
- "anyOf": [
- {
- "items": {
- "type": "integer"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
+ "default": "euler",
+ "description": "The default scheduler to use if not found in the metadata",
+ "enum": [
+ "ddim",
+ "ddpm",
+ "deis",
+ "deis_k",
+ "lms",
+ "lms_k",
+ "pndm",
+ "heun",
+ "heun_k",
+ "euler",
+ "euler_k",
+ "euler_a",
+ "kdpm_2",
+ "kdpm_2_k",
+ "kdpm_2_a",
+ "kdpm_2_a_k",
+ "dpmpp_2s",
+ "dpmpp_2s_k",
+ "dpmpp_2m",
+ "dpmpp_2m_k",
+ "dpmpp_2m_sde",
+ "dpmpp_2m_sde_k",
+ "dpmpp_3m",
+ "dpmpp_3m_k",
+ "dpmpp_sde",
+ "dpmpp_sde_k",
+ "er_sde",
+ "unipc",
+ "unipc_k",
+ "lcm",
+ "tcd"
],
- "default": null,
- "description": "The default integer to use if not found in the metadata",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Default Value"
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Default Value",
+ "type": "string",
+ "ui_type": "SchedulerField"
},
"type": {
- "const": "metadata_to_integer_collection",
- "default": "metadata_to_integer_collection",
+ "const": "metadata_to_scheduler",
+ "default": "metadata_to_scheduler",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -54179,18 +58116,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To Integer Collection",
+ "title": "Metadata To Scheduler",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/IntegerCollectionOutput"
+ "$ref": "#/components/schemas/SchedulerOutput"
}
},
- "MetadataToIntegerInvocation": {
+ "MetadataToStringCollectionInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts an integer value of a label from metadata",
+ "description": "Extracts a string collection value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -54238,13 +58175,10 @@
"description": "Label for this metadata item",
"enum": [
"* CUSTOM LABEL *",
- "width",
- "height",
- "seed",
- "steps",
- "clip_skip",
- "cfg_scale_start_step",
- "cfg_scale_end_step"
+ "positive_prompt",
+ "positive_style_prompt",
+ "negative_prompt",
+ "negative_style_prompt"
],
"field_kind": "input",
"input": "direct",
@@ -54273,22 +58207,25 @@
"default_value": {
"anyOf": [
{
- "type": "integer"
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "The default integer to use if not found in the metadata",
+ "description": "The default string collection to use if not found in the metadata",
"field_kind": "input",
"input": "any",
"orig_required": true,
"title": "Default Value"
},
"type": {
- "const": "metadata_to_integer",
- "default": "metadata_to_integer",
+ "const": "metadata_to_string_collection",
+ "default": "metadata_to_string_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -54296,18 +58233,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To Integer",
+ "title": "Metadata To String Collection",
"type": "object",
"version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/IntegerOutput"
+ "$ref": "#/components/schemas/StringCollectionOutput"
}
},
- "MetadataToLorasCollectionInvocation": {
+ "MetadataToStringInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts Lora(s) from metadata into a collection",
+ "description": "Extracts a string value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -54350,42 +58287,59 @@
"title": "Use Cache",
"type": "boolean"
},
- "custom_label": {
- "default": "loras",
+ "label": {
+ "default": "* CUSTOM LABEL *",
"description": "Label for this metadata item",
+ "enum": [
+ "* CUSTOM LABEL *",
+ "positive_prompt",
+ "positive_style_prompt",
+ "negative_prompt",
+ "negative_style_prompt"
+ ],
"field_kind": "input",
"input": "direct",
- "orig_default": "loras",
+ "orig_default": "* CUSTOM LABEL *",
"orig_required": false,
- "title": "Custom Label",
+ "title": "Label",
"type": "string"
},
- "loras": {
+ "custom_label": {
"anyOf": [
{
- "$ref": "#/components/schemas/LoRAField"
+ "type": "string"
},
{
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "type": "array"
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Label for this metadata item",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Custom Label"
+ },
+ "default_value": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": [],
- "description": "LoRA models and weights. May be a single LoRA or collection.",
+ "default": null,
+ "description": "The default string to use if not found in the metadata",
"field_kind": "input",
"input": "any",
- "orig_default": [],
- "orig_required": false,
- "title": "LoRAs"
+ "orig_required": true,
+ "title": "Default Value"
},
"type": {
- "const": "metadata_to_lora_collection",
- "default": "metadata_to_lora_collection",
+ "const": "metadata_to_string",
+ "default": "metadata_to_string",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -54393,44 +58347,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To LoRA Collection",
+ "title": "Metadata To String",
"type": "object",
- "version": "1.1.0",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/MetadataToLorasCollectionOutput"
+ "$ref": "#/components/schemas/StringOutput"
}
},
- "MetadataToLorasCollectionOutput": {
- "class": "output",
- "description": "Model loader output",
- "properties": {
- "lora": {
- "description": "Collection of LoRA model and weights",
- "field_kind": "output",
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "title": "LoRAs",
- "type": "array",
- "ui_hidden": false
- },
- "type": {
- "const": "metadata_to_lora_collection_output",
- "default": "metadata_to_lora_collection_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "lora", "type", "type"],
- "title": "MetadataToLorasCollectionOutput",
- "type": "object"
- },
- "MetadataToLorasInvocation": {
+ "MetadataToT2IAdaptersInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a Loras value of a label from metadata",
+ "description": "Extracts a T2I-Adapters value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -54473,43 +58401,32 @@
"title": "Use Cache",
"type": "boolean"
},
- "unet": {
+ "t2i_adapter_list": {
"anyOf": [
{
- "$ref": "#/components/schemas/UNetField"
+ "$ref": "#/components/schemas/T2IAdapterField"
},
{
- "type": "null"
- }
- ],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "UNet"
- },
- "clip": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/CLIPField"
+ "items": {
+ "$ref": "#/components/schemas/T2IAdapterField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "description": "IP-Adapter to apply",
"field_kind": "input",
"input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "CLIP"
+ "title": "T2I-Adapter"
},
"type": {
- "const": "metadata_to_loras",
- "default": "metadata_to_loras",
+ "const": "metadata_to_t2i_adapters",
+ "default": "metadata_to_t2i_adapters",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -54517,18 +58434,18 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To LoRAs",
+ "title": "Metadata To T2I-Adapters",
"type": "object",
- "version": "1.1.1",
+ "version": "1.2.0",
"output": {
- "$ref": "#/components/schemas/LoRALoaderOutput"
+ "$ref": "#/components/schemas/MDT2IAdapterListOutput"
}
},
- "MetadataToModelInvocation": {
+ "MetadataToVAEInvocation": {
"category": "metadata",
"class": "invocation",
"classification": "beta",
- "description": "Extracts a Model value of a label from metadata",
+ "description": "Extracts a VAE value of a label from metadata",
"node_pack": "invokeai",
"properties": {
"metadata": {
@@ -54572,12 +58489,12 @@
"type": "boolean"
},
"label": {
- "default": "model",
+ "default": "vae",
"description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "model"],
+ "enum": ["* CUSTOM LABEL *", "vae"],
"field_kind": "input",
"input": "direct",
- "orig_default": "model",
+ "orig_default": "vae",
"orig_required": false,
"title": "Label",
"type": "string"
@@ -54602,22 +58519,21 @@
"default_value": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The default model to use if not found in the metadata",
+ "description": "The default VAE to use if not found in the metadata",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "ui_model_type": ["main"]
+ "orig_required": true
},
"type": {
- "const": "metadata_to_model",
- "default": "metadata_to_model",
+ "const": "metadata_to_vae",
+ "default": "metadata_to_vae",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
@@ -54625,87 +58541,87 @@
},
"required": ["type", "id"],
"tags": ["metadata"],
- "title": "Metadata To Model",
+ "title": "Metadata To VAE",
"type": "object",
- "version": "1.3.0",
+ "version": "1.2.1",
"output": {
- "$ref": "#/components/schemas/MetadataToModelOutput"
+ "$ref": "#/components/schemas/VAEOutput"
}
},
- "MetadataToModelOutput": {
- "class": "output",
- "description": "String to main model output",
+ "ModelFormat": {
+ "type": "string",
+ "enum": [
+ "omi",
+ "diffusers",
+ "checkpoint",
+ "lycoris",
+ "onnx",
+ "olive",
+ "embedding_file",
+ "embedding_folder",
+ "invokeai",
+ "t5_encoder",
+ "qwen3_encoder",
+ "qwen_vl_encoder",
+ "wan_t5_encoder",
+ "bnb_quantized_int8b",
+ "bnb_quantized_nf4b",
+ "gguf_quantized",
+ "external_api",
+ "unknown"
+ ],
+ "title": "ModelFormat",
+ "description": "Storage format of model."
+ },
+ "ModelIdentifierField": {
"properties": {
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Main model (UNet, VAE, CLIP) to load",
- "field_kind": "output",
- "title": "Model",
- "ui_hidden": false
+ "key": {
+ "description": "The model's unique key",
+ "title": "Key",
+ "type": "string"
+ },
+ "hash": {
+ "description": "The model's BLAKE3 hash",
+ "title": "Hash",
+ "type": "string"
},
"name": {
- "description": "Model Name",
- "field_kind": "output",
+ "description": "The model's name",
"title": "Name",
- "type": "string",
- "ui_hidden": false
- },
- "unet": {
- "$ref": "#/components/schemas/UNetField",
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "output",
- "title": "UNet",
- "ui_hidden": false
- },
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
+ "type": "string"
},
- "clip": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP",
- "ui_hidden": false
+ "base": {
+ "$ref": "#/components/schemas/BaseModelType",
+ "description": "The model's base model type"
},
"type": {
- "const": "metadata_to_model_output",
- "default": "metadata_to_model_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "model", "name", "unet", "vae", "clip", "type", "type"],
- "title": "MetadataToModelOutput",
- "type": "object"
- },
- "MetadataToSDXLLorasInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "beta",
- "description": "Extracts a SDXL Loras value of a label from metadata",
- "node_pack": "invokeai",
- "properties": {
- "metadata": {
+ "$ref": "#/components/schemas/ModelType",
+ "description": "The model's type"
+ },
+ "submodel_type": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "$ref": "#/components/schemas/SubModelType"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
+ "description": "The submodel to load, if this is a main model"
+ }
+ },
+ "required": ["key", "hash", "name", "base", "type"],
+ "title": "ModelIdentifierField",
+ "type": "object"
+ },
+ "ModelIdentifierInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Selects any model, outputting it its identifier. Be careful with this one! The identifier will be accepted as\ninput for any model, even if the model types don't match. If you connect this to a mismatched input, you'll get an\nerror.",
+ "node_pack": "invokeai",
+ "properties": {
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -54730,957 +58646,1184 @@
"title": "Use Cache",
"type": "boolean"
},
- "unet": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/UNetField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "UNet"
- },
- "clip": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/CLIPField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP 1"
- },
- "clip2": {
+ "model": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "description": "The model to select",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP 2"
+ "input": "any",
+ "orig_required": true,
+ "title": "Model"
},
"type": {
- "const": "metadata_to_sdlx_loras",
- "default": "metadata_to_sdlx_loras",
+ "const": "model_identifier",
+ "default": "model_identifier",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Metadata To SDXL LoRAs",
+ "tags": ["model"],
+ "title": "Any Model",
"type": "object",
- "version": "1.1.1",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/SDXLLoRALoaderOutput"
+ "$ref": "#/components/schemas/ModelIdentifierOutput"
}
},
- "MetadataToSDXLModelInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "beta",
- "description": "Extracts a SDXL Model value of a label from metadata",
- "node_pack": "invokeai",
+ "ModelIdentifierOutput": {
+ "class": "output",
+ "description": "Model identifier output",
"properties": {
- "metadata": {
- "anyOf": [
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Model identifier",
+ "field_kind": "output",
+ "title": "Model",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "model_identifier_output",
+ "default": "model_identifier_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "model", "type", "type"],
+ "title": "ModelIdentifierOutput",
+ "type": "object"
+ },
+ "ModelInstallCancelledEvent": {
+ "description": "Event model for model_install_cancelled",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "id": {
+ "description": "The ID of the install job",
+ "title": "Id",
+ "type": "integer"
+ },
+ "source": {
+ "description": "Source of the model; local path, repo_id or url",
+ "discriminator": {
+ "mapping": {
+ "external": "#/components/schemas/ExternalModelSource",
+ "hf": "#/components/schemas/HFModelSource",
+ "local": "#/components/schemas/LocalModelSource",
+ "url": "#/components/schemas/URLModelSource"
+ },
+ "propertyName": "type"
+ },
+ "oneOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "$ref": "#/components/schemas/LocalModelSource"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/HFModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/URLModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalModelSource"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Source"
+ }
+ },
+ "required": ["timestamp", "id", "source"],
+ "title": "ModelInstallCancelledEvent",
+ "type": "object"
+ },
+ "ModelInstallCompleteEvent": {
+ "description": "Event model for model_install_complete",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
"id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
+ "description": "The ID of the install job",
"title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "type": "integer"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "source": {
+ "description": "Source of the model; local path, repo_id or url",
+ "discriminator": {
+ "mapping": {
+ "external": "#/components/schemas/ExternalModelSource",
+ "hf": "#/components/schemas/HFModelSource",
+ "local": "#/components/schemas/LocalModelSource",
+ "url": "#/components/schemas/URLModelSource"
+ },
+ "propertyName": "type"
+ },
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/LocalModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/HFModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/URLModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalModelSource"
+ }
+ ],
+ "title": "Source"
},
- "label": {
- "default": "model",
- "description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "model"],
- "field_kind": "input",
- "input": "direct",
- "orig_default": "model",
- "orig_required": false,
- "title": "Label",
+ "key": {
+ "description": "Model config record key",
+ "title": "Key",
"type": "string"
},
- "custom_label": {
+ "total_bytes": {
"anyOf": [
{
- "type": "string"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Label for this metadata item",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Custom Label"
+ "description": "Size of the model (may be None for installation of a local path)",
+ "title": "Total Bytes"
},
- "default_value": {
- "anyOf": [
+ "config": {
+ "description": "The installed model's config",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
+ },
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/ExternalApiModelConfig"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/Unknown_Config"
}
],
- "default": null,
- "description": "The default SDXL Model to use if not found in the metadata",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_model_base": ["sdxl"],
- "ui_model_type": ["main"]
- },
- "type": {
- "const": "metadata_to_sdxl_model",
- "default": "metadata_to_sdxl_model",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Config"
}
},
- "required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Metadata To SDXL Model",
- "type": "object",
- "version": "1.3.0",
- "output": {
- "$ref": "#/components/schemas/MetadataToSDXLModelOutput"
- }
+ "required": ["timestamp", "id", "source", "key", "total_bytes", "config"],
+ "title": "ModelInstallCompleteEvent",
+ "type": "object"
},
- "MetadataToSDXLModelOutput": {
- "class": "output",
- "description": "String to SDXL main model output",
+ "ModelInstallDownloadProgressEvent": {
+ "description": "Event model for model_install_download_progress",
"properties": {
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Main model (UNet, VAE, CLIP) to load",
- "field_kind": "output",
- "title": "Model",
- "ui_hidden": false
- },
- "name": {
- "description": "Model Name",
- "field_kind": "output",
- "title": "Name",
- "type": "string",
- "ui_hidden": false
- },
- "unet": {
- "$ref": "#/components/schemas/UNetField",
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "output",
- "title": "UNet",
- "ui_hidden": false
- },
- "clip": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP 1",
- "ui_hidden": false
- },
- "clip2": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP 2",
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
+ "id": {
+ "description": "The ID of the install job",
+ "title": "Id",
+ "type": "integer"
},
- "type": {
- "const": "metadata_to_sdxl_model_output",
- "default": "metadata_to_sdxl_model_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "model", "name", "unet", "clip", "clip2", "vae", "type", "type"],
- "title": "MetadataToSDXLModelOutput",
- "type": "object"
- },
- "MetadataToSchedulerInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "beta",
- "description": "Extracts a Scheduler value of a label from metadata",
- "node_pack": "invokeai",
- "properties": {
- "metadata": {
- "anyOf": [
+ "source": {
+ "description": "Source of the model; local path, repo_id or url",
+ "discriminator": {
+ "mapping": {
+ "external": "#/components/schemas/ExternalModelSource",
+ "hf": "#/components/schemas/HFModelSource",
+ "local": "#/components/schemas/LocalModelSource",
+ "url": "#/components/schemas/URLModelSource"
+ },
+ "propertyName": "type"
+ },
+ "oneOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "$ref": "#/components/schemas/LocalModelSource"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/HFModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/URLModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalModelSource"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Source"
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
+ "local_path": {
+ "description": "Where model is downloading to",
+ "title": "Local Path",
"type": "string"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "bytes": {
+ "description": "Number of bytes downloaded so far",
+ "title": "Bytes",
+ "type": "integer"
},
- "label": {
- "default": "scheduler",
- "description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "scheduler"],
- "field_kind": "input",
- "input": "direct",
- "orig_default": "scheduler",
- "orig_required": false,
- "title": "Label",
- "type": "string"
+ "total_bytes": {
+ "description": "Total size of download, including all files",
+ "title": "Total Bytes",
+ "type": "integer"
},
- "custom_label": {
- "anyOf": [
- {
- "type": "string"
+ "parts": {
+ "description": "Progress of downloading URLs that comprise the model, if any",
+ "items": {
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
},
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Label for this metadata item",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Custom Label"
- },
- "default_value": {
- "default": "euler",
- "description": "The default scheduler to use if not found in the metadata",
- "enum": [
- "ddim",
- "ddpm",
- "deis",
- "deis_k",
- "lms",
- "lms_k",
- "pndm",
- "heun",
- "heun_k",
- "euler",
- "euler_k",
- "euler_a",
- "kdpm_2",
- "kdpm_2_k",
- "kdpm_2_a",
- "kdpm_2_a_k",
- "dpmpp_2s",
- "dpmpp_2s_k",
- "dpmpp_2m",
- "dpmpp_2m_k",
- "dpmpp_2m_sde",
- "dpmpp_2m_sde_k",
- "dpmpp_3m",
- "dpmpp_3m_k",
- "dpmpp_sde",
- "dpmpp_sde_k",
- "er_sde",
- "unipc",
- "unipc_k",
- "lcm",
- "tcd"
- ],
- "field_kind": "input",
- "input": "any",
- "orig_default": "euler",
- "orig_required": false,
- "title": "Default Value",
- "type": "string",
- "ui_type": "SchedulerField"
- },
- "type": {
- "const": "metadata_to_scheduler",
- "default": "metadata_to_scheduler",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "object"
+ },
+ "title": "Parts",
+ "type": "array"
}
},
- "required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Metadata To Scheduler",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/SchedulerOutput"
- }
+ "required": ["timestamp", "id", "source", "local_path", "bytes", "total_bytes", "parts"],
+ "title": "ModelInstallDownloadProgressEvent",
+ "type": "object"
},
- "MetadataToStringCollectionInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "beta",
- "description": "Extracts a string collection value of a label from metadata",
- "node_pack": "invokeai",
+ "ModelInstallDownloadStartedEvent": {
+ "description": "Event model for model_install_download_started",
"properties": {
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
"id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
+ "description": "The ID of the install job",
"title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "label": {
- "default": "* CUSTOM LABEL *",
- "description": "Label for this metadata item",
- "enum": [
- "* CUSTOM LABEL *",
- "positive_prompt",
- "positive_style_prompt",
- "negative_prompt",
- "negative_style_prompt"
- ],
- "field_kind": "input",
- "input": "direct",
- "orig_default": "* CUSTOM LABEL *",
- "orig_required": false,
- "title": "Label",
- "type": "string"
+ "type": "integer"
},
- "custom_label": {
- "anyOf": [
+ "source": {
+ "description": "Source of the model; local path, repo_id or url",
+ "discriminator": {
+ "mapping": {
+ "external": "#/components/schemas/ExternalModelSource",
+ "hf": "#/components/schemas/HFModelSource",
+ "local": "#/components/schemas/LocalModelSource",
+ "url": "#/components/schemas/URLModelSource"
+ },
+ "propertyName": "type"
+ },
+ "oneOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LocalModelSource"
},
{
- "type": "null"
- }
- ],
- "default": null,
- "description": "Label for this metadata item",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Custom Label"
- },
- "default_value": {
- "anyOf": [
+ "$ref": "#/components/schemas/HFModelSource"
+ },
{
- "items": {
- "type": "string"
- },
- "type": "array"
+ "$ref": "#/components/schemas/URLModelSource"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/ExternalModelSource"
}
],
- "default": null,
- "description": "The default string collection to use if not found in the metadata",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Default Value"
+ "title": "Source"
},
- "type": {
- "const": "metadata_to_string_collection",
- "default": "metadata_to_string_collection",
- "field_kind": "node_attribute",
- "title": "type",
+ "local_path": {
+ "description": "Where model is downloading to",
+ "title": "Local Path",
"type": "string"
+ },
+ "bytes": {
+ "description": "Number of bytes downloaded so far",
+ "title": "Bytes",
+ "type": "integer"
+ },
+ "total_bytes": {
+ "description": "Total size of download, including all files",
+ "title": "Total Bytes",
+ "type": "integer"
+ },
+ "parts": {
+ "description": "Progress of downloading URLs that comprise the model, if any",
+ "items": {
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "type": "object"
+ },
+ "title": "Parts",
+ "type": "array"
}
},
- "required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Metadata To String Collection",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/StringCollectionOutput"
- }
+ "required": ["timestamp", "id", "source", "local_path", "bytes", "total_bytes", "parts"],
+ "title": "ModelInstallDownloadStartedEvent",
+ "type": "object"
},
- "MetadataToStringInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "beta",
- "description": "Extracts a string value of a label from metadata",
- "node_pack": "invokeai",
+ "ModelInstallDownloadsCompleteEvent": {
+ "description": "Emitted once when an install job becomes active.",
"properties": {
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
"id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
+ "description": "The ID of the install job",
"title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "label": {
- "default": "* CUSTOM LABEL *",
- "description": "Label for this metadata item",
- "enum": [
- "* CUSTOM LABEL *",
- "positive_prompt",
- "positive_style_prompt",
- "negative_prompt",
- "negative_style_prompt"
- ],
- "field_kind": "input",
- "input": "direct",
- "orig_default": "* CUSTOM LABEL *",
- "orig_required": false,
- "title": "Label",
- "type": "string"
+ "type": "integer"
},
- "custom_label": {
- "anyOf": [
+ "source": {
+ "description": "Source of the model; local path, repo_id or url",
+ "discriminator": {
+ "mapping": {
+ "external": "#/components/schemas/ExternalModelSource",
+ "hf": "#/components/schemas/HFModelSource",
+ "local": "#/components/schemas/LocalModelSource",
+ "url": "#/components/schemas/URLModelSource"
+ },
+ "propertyName": "type"
+ },
+ "oneOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LocalModelSource"
},
{
- "type": "null"
- }
- ],
- "default": null,
- "description": "Label for this metadata item",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Custom Label"
- },
- "default_value": {
- "anyOf": [
+ "$ref": "#/components/schemas/HFModelSource"
+ },
{
- "type": "string"
+ "$ref": "#/components/schemas/URLModelSource"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/ExternalModelSource"
}
],
- "default": null,
- "description": "The default string to use if not found in the metadata",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Default Value"
- },
- "type": {
- "const": "metadata_to_string",
- "default": "metadata_to_string",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Source"
}
},
- "required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Metadata To String",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/StringOutput"
- }
+ "required": ["timestamp", "id", "source"],
+ "title": "ModelInstallDownloadsCompleteEvent",
+ "type": "object"
},
- "MetadataToT2IAdaptersInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "beta",
- "description": "Extracts a T2I-Adapters value of a label from metadata",
- "node_pack": "invokeai",
+ "ModelInstallErrorEvent": {
+ "description": "Event model for model_install_error",
"properties": {
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
"id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
+ "description": "The ID of the install job",
"title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "type": "integer"
},
- "t2i_adapter_list": {
- "anyOf": [
+ "source": {
+ "description": "Source of the model; local path, repo_id or url",
+ "discriminator": {
+ "mapping": {
+ "external": "#/components/schemas/ExternalModelSource",
+ "hf": "#/components/schemas/HFModelSource",
+ "local": "#/components/schemas/LocalModelSource",
+ "url": "#/components/schemas/URLModelSource"
+ },
+ "propertyName": "type"
+ },
+ "oneOf": [
{
- "$ref": "#/components/schemas/T2IAdapterField"
+ "$ref": "#/components/schemas/LocalModelSource"
},
{
- "items": {
- "$ref": "#/components/schemas/T2IAdapterField"
- },
- "type": "array"
+ "$ref": "#/components/schemas/HFModelSource"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/URLModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalModelSource"
}
],
- "default": null,
- "description": "IP-Adapter to apply",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "T2I-Adapter"
+ "title": "Source"
},
- "type": {
- "const": "metadata_to_t2i_adapters",
- "default": "metadata_to_t2i_adapters",
- "field_kind": "node_attribute",
- "title": "type",
+ "error_type": {
+ "description": "The name of the exception",
+ "title": "Error Type",
+ "type": "string"
+ },
+ "error": {
+ "description": "A text description of the exception",
+ "title": "Error",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Metadata To T2I-Adapters",
- "type": "object",
- "version": "1.2.0",
- "output": {
- "$ref": "#/components/schemas/MDT2IAdapterListOutput"
- }
+ "required": ["timestamp", "id", "source", "error_type", "error"],
+ "title": "ModelInstallErrorEvent",
+ "type": "object"
},
- "MetadataToVAEInvocation": {
- "category": "metadata",
- "class": "invocation",
- "classification": "beta",
- "description": "Extracts a VAE value of a label from metadata",
- "node_pack": "invokeai",
+ "ModelInstallJob": {
"properties": {
- "metadata": {
+ "id": {
+ "type": "integer",
+ "title": "Id",
+ "description": "Unique ID for this job"
+ },
+ "status": {
+ "$ref": "#/components/schemas/InstallStatus",
+ "description": "Current status of install process",
+ "default": "waiting"
+ },
+ "error_reason": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Error Reason",
+ "description": "Information about why the job failed"
},
- "label": {
- "default": "vae",
- "description": "Label for this metadata item",
- "enum": ["* CUSTOM LABEL *", "vae"],
- "field_kind": "input",
- "input": "direct",
- "orig_default": "vae",
- "orig_required": false,
- "title": "Label",
- "type": "string"
+ "config_in": {
+ "$ref": "#/components/schemas/ModelRecordChanges",
+ "description": "Configuration information (e.g. 'description') to apply to model."
},
- "custom_label": {
+ "config_out": {
"anyOf": [
{
- "type": "string"
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalApiModelConfig"
+ },
+ {
+ "$ref": "#/components/schemas/Unknown_Config"
+ }
+ ]
},
{
"type": "null"
}
],
- "default": null,
- "description": "Label for this metadata item",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "Custom Label"
+ "title": "Config Out",
+ "description": "After successful installation, this will hold the configuration object."
},
- "default_value": {
- "anyOf": [
+ "inplace": {
+ "type": "boolean",
+ "title": "Inplace",
+ "description": "Leave model in its current location; otherwise install under models directory",
+ "default": false
+ },
+ "source": {
+ "oneOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "$ref": "#/components/schemas/LocalModelSource"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/HFModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/URLModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalModelSource"
}
],
- "default": null,
- "description": "The default VAE to use if not found in the metadata",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "type": {
- "const": "metadata_to_vae",
- "default": "metadata_to_vae",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["metadata"],
- "title": "Metadata To VAE",
- "type": "object",
- "version": "1.2.1",
- "output": {
- "$ref": "#/components/schemas/VAEOutput"
- }
- },
- "ModelFormat": {
- "type": "string",
- "enum": [
- "omi",
- "diffusers",
- "checkpoint",
- "lycoris",
- "onnx",
- "olive",
- "embedding_file",
- "embedding_folder",
- "invokeai",
- "t5_encoder",
- "qwen3_encoder",
- "qwen_vl_encoder",
- "bnb_quantized_int8b",
- "bnb_quantized_nf4b",
- "gguf_quantized",
- "external_api",
- "unknown"
- ],
- "title": "ModelFormat",
- "description": "Storage format of model."
- },
- "ModelIdentifierField": {
- "properties": {
- "key": {
- "description": "The model's unique key",
- "title": "Key",
- "type": "string"
- },
- "hash": {
- "description": "The model's BLAKE3 hash",
- "title": "Hash",
- "type": "string"
+ "title": "Source",
+ "description": "Source (URL, repo_id, or local path) of model",
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "external": "#/components/schemas/ExternalModelSource",
+ "hf": "#/components/schemas/HFModelSource",
+ "local": "#/components/schemas/LocalModelSource",
+ "url": "#/components/schemas/URLModelSource"
+ }
+ }
},
- "name": {
- "description": "The model's name",
- "title": "Name",
- "type": "string"
+ "local_path": {
+ "type": "string",
+ "format": "path",
+ "title": "Local Path",
+ "description": "Path to locally-downloaded model; may be the same as the source"
},
- "base": {
- "$ref": "#/components/schemas/BaseModelType",
- "description": "The model's base model type"
+ "bytes": {
+ "type": "integer",
+ "title": "Bytes",
+ "description": "For a remote model, the number of bytes downloaded so far (may not be available)",
+ "default": 0
},
- "type": {
- "$ref": "#/components/schemas/ModelType",
- "description": "The model's type"
+ "total_bytes": {
+ "type": "integer",
+ "title": "Total Bytes",
+ "description": "Total size of the model to be installed",
+ "default": 0
},
- "submodel_type": {
+ "source_metadata": {
"anyOf": [
{
- "$ref": "#/components/schemas/SubModelType"
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/BaseMetadata"
+ },
+ {
+ "$ref": "#/components/schemas/HuggingFaceMetadata"
+ }
+ ],
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "basemetadata": "#/components/schemas/BaseMetadata",
+ "huggingface": "#/components/schemas/HuggingFaceMetadata"
+ }
+ }
},
{
"type": "null"
}
],
- "default": null,
- "description": "The submodel to load, if this is a main model"
- }
- },
- "required": ["key", "hash", "name", "base", "type"],
- "title": "ModelIdentifierField",
- "type": "object"
- },
- "ModelIdentifierInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "stable",
- "description": "Selects any model, outputting it its identifier. Be careful with this one! The identifier will be accepted as\ninput for any model, even if the model types don't match. If you connect this to a mismatched input, you'll get an\nerror.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "title": "Source Metadata",
+ "description": "Metadata provided by the model source"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "download_parts": {
+ "items": {
+ "$ref": "#/components/schemas/DownloadJob"
+ },
+ "type": "array",
+ "uniqueItems": true,
+ "title": "Download Parts",
+ "description": "Download jobs contributing to this install"
},
- "model": {
+ "error": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The model to select",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Model"
+ "title": "Error",
+ "description": "On an error condition, this field will contain the text of the exception"
},
- "type": {
- "const": "model_identifier",
- "default": "model_identifier",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "error_traceback": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Error Traceback",
+ "description": "On an error condition, this field will contain the exception traceback"
}
},
- "required": ["type", "id"],
- "tags": ["model"],
- "title": "Any Model",
"type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/ModelIdentifierOutput"
- }
- },
- "ModelIdentifierOutput": {
- "class": "output",
- "description": "Model identifier output",
- "properties": {
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Model identifier",
- "field_kind": "output",
- "title": "Model",
- "ui_hidden": false
- },
- "type": {
- "const": "model_identifier_output",
- "default": "model_identifier_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "model", "type", "type"],
- "title": "ModelIdentifierOutput",
- "type": "object"
+ "required": ["id", "source", "local_path"],
+ "title": "ModelInstallJob",
+ "description": "Object that tracks the current status of an install request."
},
- "ModelInstallCancelledEvent": {
- "description": "Event model for model_install_cancelled",
+ "ModelInstallStartedEvent": {
+ "description": "Event model for model_install_started",
"properties": {
"timestamp": {
"description": "The timestamp of the event",
@@ -55701,88 +59844,367 @@
"local": "#/components/schemas/LocalModelSource",
"url": "#/components/schemas/URLModelSource"
},
- "propertyName": "type"
- },
- "oneOf": [
+ "propertyName": "type"
+ },
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/LocalModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/HFModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/URLModelSource"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalModelSource"
+ }
+ ],
+ "title": "Source"
+ }
+ },
+ "required": ["timestamp", "id", "source"],
+ "title": "ModelInstallStartedEvent",
+ "type": "object"
+ },
+ "ModelLoadCompleteEvent": {
+ "description": "Event model for model_load_complete",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "config": {
+ "description": "The model's config",
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD2_Config"
+ },
{
- "$ref": "#/components/schemas/LocalModelSource"
+ "$ref": "#/components/schemas/TI_File_SDXL_Config"
},
{
- "$ref": "#/components/schemas/HFModelSource"
+ "$ref": "#/components/schemas/TI_Folder_SD1_Config"
},
{
- "$ref": "#/components/schemas/URLModelSource"
+ "$ref": "#/components/schemas/TI_Folder_SD2_Config"
},
{
- "$ref": "#/components/schemas/ExternalModelSource"
- }
- ],
- "title": "Source"
- }
- },
- "required": ["timestamp", "id", "source"],
- "title": "ModelInstallCancelledEvent",
- "type": "object"
- },
- "ModelInstallCompleteEvent": {
- "description": "Event model for model_install_complete",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "id": {
- "description": "The ID of the install job",
- "title": "Id",
- "type": "integer"
- },
- "source": {
- "description": "Source of the model; local path, repo_id or url",
- "discriminator": {
- "mapping": {
- "external": "#/components/schemas/ExternalModelSource",
- "hf": "#/components/schemas/HFModelSource",
- "local": "#/components/schemas/LocalModelSource",
- "url": "#/components/schemas/URLModelSource"
+ "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
},
- "propertyName": "type"
- },
- "oneOf": [
{
- "$ref": "#/components/schemas/LocalModelSource"
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
},
{
- "$ref": "#/components/schemas/HFModelSource"
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
},
{
- "$ref": "#/components/schemas/URLModelSource"
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
},
{
- "$ref": "#/components/schemas/ExternalModelSource"
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalApiModelConfig"
+ },
+ {
+ "$ref": "#/components/schemas/Unknown_Config"
}
],
- "title": "Source"
- },
- "key": {
- "description": "Model config record key",
- "title": "Key",
- "type": "string"
+ "title": "Config"
},
- "total_bytes": {
+ "submodel_type": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/SubModelType"
},
{
"type": "null"
}
],
- "description": "Size of the model (may be None for installation of a local path)",
- "title": "Total Bytes"
+ "default": null,
+ "description": "The submodel type, if any"
+ }
+ },
+ "required": ["timestamp", "config", "submodel_type"],
+ "title": "ModelLoadCompleteEvent",
+ "type": "object"
+ },
+ "ModelLoadStartedEvent": {
+ "description": "Event model for model_load_started",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
"config": {
- "description": "The installed model's config",
+ "description": "The model's config",
"oneOf": [
{
"$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
@@ -55811,6 +60233,9 @@
{
"$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
},
@@ -55853,6 +60278,9 @@
{
"$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
{
"$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
},
@@ -55871,6 +60299,9 @@
{
"$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
{
"$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
},
@@ -55886,6 +60317,9 @@
{
"$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
},
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
{
"$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
},
@@ -55937,6 +60371,9 @@
{
"$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
},
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
{
"$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
},
@@ -55988,6 +60425,9 @@
{
"$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
},
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
{
"$ref": "#/components/schemas/TI_File_SD1_Config"
},
@@ -56065,1432 +60505,2629 @@
}
],
"title": "Config"
+ },
+ "submodel_type": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/SubModelType"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The submodel type, if any"
}
},
- "required": ["timestamp", "id", "source", "key", "total_bytes", "config"],
- "title": "ModelInstallCompleteEvent",
+ "required": ["timestamp", "config", "submodel_type"],
+ "title": "ModelLoadStartedEvent",
"type": "object"
},
- "ModelInstallDownloadProgressEvent": {
- "description": "Event model for model_install_download_progress",
+ "ModelLoaderOutput": {
+ "class": "output",
+ "description": "Model loader output",
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
},
- "id": {
- "description": "The ID of the install job",
- "title": "Id",
- "type": "integer"
+ "type": {
+ "const": "model_loader_output",
+ "default": "model_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ },
+ "clip": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP",
+ "ui_hidden": false
},
+ "unet": {
+ "$ref": "#/components/schemas/UNetField",
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
+ }
+ },
+ "required": ["output_meta", "vae", "type", "clip", "unet", "type"],
+ "title": "ModelLoaderOutput",
+ "type": "object"
+ },
+ "ModelRecordChanges": {
+ "properties": {
"source": {
- "description": "Source of the model; local path, repo_id or url",
- "discriminator": {
- "mapping": {
- "external": "#/components/schemas/ExternalModelSource",
- "hf": "#/components/schemas/HFModelSource",
- "local": "#/components/schemas/LocalModelSource",
- "url": "#/components/schemas/URLModelSource"
+ "anyOf": [
+ {
+ "type": "string"
},
- "propertyName": "type"
- },
- "oneOf": [
{
- "$ref": "#/components/schemas/LocalModelSource"
+ "type": "null"
+ }
+ ],
+ "title": "Source",
+ "description": "original source of the model"
+ },
+ "source_type": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelSourceType"
},
{
- "$ref": "#/components/schemas/HFModelSource"
+ "type": "null"
+ }
+ ],
+ "description": "type of model source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
- "$ref": "#/components/schemas/URLModelSource"
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "metadata from remote source"
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
- "$ref": "#/components/schemas/ExternalModelSource"
+ "type": "null"
}
],
- "title": "Source"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page)"
},
- "local_path": {
- "description": "Where model is downloading to",
- "title": "Local Path",
- "type": "string"
+ "name": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Name",
+ "description": "Name of the model."
},
- "bytes": {
- "description": "Number of bytes downloaded so far",
- "title": "Bytes",
- "type": "integer"
+ "path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Path",
+ "description": "Path to the model."
},
- "total_bytes": {
- "description": "Total size of download, including all files",
- "title": "Total Bytes",
- "type": "integer"
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
},
- "parts": {
- "description": "Progress of downloading URLs that comprise the model, if any",
- "items": {
- "additionalProperties": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "string"
- }
- ]
+ "base": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BaseModelType"
},
- "type": "object"
- },
- "title": "Parts",
- "type": "array"
- }
- },
- "required": ["timestamp", "id", "source", "local_path", "bytes", "total_bytes", "parts"],
- "title": "ModelInstallDownloadProgressEvent",
- "type": "object"
- },
- "ModelInstallDownloadStartedEvent": {
- "description": "Event model for model_install_download_started",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The base model."
},
- "id": {
- "description": "The ID of the install job",
- "title": "Id",
- "type": "integer"
+ "type": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelType"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Type of model"
},
- "source": {
- "description": "Source of the model; local path, repo_id or url",
- "discriminator": {
- "mapping": {
- "external": "#/components/schemas/ExternalModelSource",
- "hf": "#/components/schemas/HFModelSource",
- "local": "#/components/schemas/LocalModelSource",
- "url": "#/components/schemas/URLModelSource"
+ "key": {
+ "anyOf": [
+ {
+ "type": "string"
},
- "propertyName": "type"
- },
- "oneOf": [
{
- "$ref": "#/components/schemas/LocalModelSource"
+ "type": "null"
+ }
+ ],
+ "title": "Key",
+ "description": "Database ID for this model"
+ },
+ "hash": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
- "$ref": "#/components/schemas/HFModelSource"
+ "type": "null"
+ }
+ ],
+ "title": "Hash",
+ "description": "hash of model file"
+ },
+ "file_size": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "File Size",
+ "description": "Size of model file"
+ },
+ "format": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Format",
+ "description": "format of model file"
+ },
+ "trigger_phrases": {
+ "anyOf": [
+ {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "uniqueItems": true
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Trigger Phrases",
+ "description": "Set of trigger phrases for this model"
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MainModelDefaultSettings"
+ },
+ {
+ "$ref": "#/components/schemas/LoraModelDefaultSettings"
+ },
+ {
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Default Settings",
+ "description": "Default settings for this model"
+ },
+ "provider_id": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Provider Id",
+ "description": "External provider identifier"
+ },
+ "provider_model_id": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Provider Model Id",
+ "description": "External provider model identifier"
+ },
+ "capabilities": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ExternalModelCapabilities"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "External model capabilities"
+ },
+ "cpu_only": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
+ },
+ "variant": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ClipVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/FluxVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/Flux2VariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ZImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/QwenImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRAVariantType"
},
{
- "$ref": "#/components/schemas/URLModelSource"
+ "$ref": "#/components/schemas/Qwen3VariantType"
},
{
- "$ref": "#/components/schemas/ExternalModelSource"
+ "type": "null"
}
],
- "title": "Source"
- },
- "local_path": {
- "description": "Where model is downloading to",
- "title": "Local Path",
- "type": "string"
- },
- "bytes": {
- "description": "Number of bytes downloaded so far",
- "title": "Bytes",
- "type": "integer"
- },
- "total_bytes": {
- "description": "Total size of download, including all files",
- "title": "Total Bytes",
- "type": "integer"
+ "title": "Variant",
+ "description": "The variant of the model."
},
- "parts": {
- "description": "Progress of downloading URLs that comprise the model, if any",
- "items": {
- "additionalProperties": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "string"
- }
- ]
+ "prediction_type": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/SchedulerPredictionType"
},
- "type": "object"
- },
- "title": "Parts",
- "type": "array"
- }
- },
- "required": ["timestamp", "id", "source", "local_path", "bytes", "total_bytes", "parts"],
- "title": "ModelInstallDownloadStartedEvent",
- "type": "object"
- },
- "ModelInstallDownloadsCompleteEvent": {
- "description": "Emitted once when an install job becomes active.",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "id": {
- "description": "The ID of the install job",
- "title": "Id",
- "type": "integer"
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The prediction type of the model."
},
- "source": {
- "description": "Source of the model; local path, repo_id or url",
- "discriminator": {
- "mapping": {
- "external": "#/components/schemas/ExternalModelSource",
- "hf": "#/components/schemas/HFModelSource",
- "local": "#/components/schemas/LocalModelSource",
- "url": "#/components/schemas/URLModelSource"
- },
- "propertyName": "type"
- },
- "oneOf": [
+ "upcast_attention": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LocalModelSource"
+ "type": "boolean"
},
{
- "$ref": "#/components/schemas/HFModelSource"
- },
+ "type": "null"
+ }
+ ],
+ "title": "Upcast Attention",
+ "description": "Whether to upcast attention."
+ },
+ "config_path": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/URLModelSource"
+ "type": "string"
},
{
- "$ref": "#/components/schemas/ExternalModelSource"
+ "type": "null"
}
],
- "title": "Source"
+ "title": "Config Path",
+ "description": "Path to config file for model"
}
},
- "required": ["timestamp", "id", "source"],
- "title": "ModelInstallDownloadsCompleteEvent",
- "type": "object"
+ "type": "object",
+ "title": "ModelRecordChanges",
+ "description": "A set of changes to apply to a model."
},
- "ModelInstallErrorEvent": {
- "description": "Event model for model_install_error",
+ "ModelRecordOrderBy": {
+ "type": "string",
+ "enum": ["default", "type", "base", "name", "format", "size", "created_at", "updated_at", "path"],
+ "title": "ModelRecordOrderBy",
+ "description": "The order in which to return model summaries."
+ },
+ "ModelRelationshipBatchRequest": {
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "id": {
- "description": "The ID of the install job",
- "title": "Id",
- "type": "integer"
+ "model_keys": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Model Keys",
+ "description": "List of model keys to fetch related models for",
+ "examples": [
+ ["aa3b247f-90c9-4416-bfcd-aeaa57a5339e", "ac32b914-10ab-496e-a24a-3068724b9c35"],
+ [
+ "b1c2d3e4-f5a6-7890-abcd-ef1234567890",
+ "12345678-90ab-cdef-1234-567890abcdef",
+ "fedcba98-7654-3210-fedc-ba9876543210"
+ ],
+ ["3bb7c0eb-b6c8-469c-ad8c-4d69c06075e4"]
+ ]
+ }
+ },
+ "type": "object",
+ "required": ["model_keys"],
+ "title": "ModelRelationshipBatchRequest"
+ },
+ "ModelRelationshipCreateRequest": {
+ "properties": {
+ "model_key_1": {
+ "type": "string",
+ "title": "Model Key 1",
+ "description": "The key of the first model in the relationship",
+ "examples": [
+ "aa3b247f-90c9-4416-bfcd-aeaa57a5339e",
+ "ac32b914-10ab-496e-a24a-3068724b9c35",
+ "d944abfd-c7c3-42e2-a4ff-da640b29b8b4",
+ "b1c2d3e4-f5a6-7890-abcd-ef1234567890",
+ "12345678-90ab-cdef-1234-567890abcdef",
+ "fedcba98-7654-3210-fedc-ba9876543210"
+ ]
},
- "source": {
- "description": "Source of the model; local path, repo_id or url",
- "discriminator": {
- "mapping": {
- "external": "#/components/schemas/ExternalModelSource",
- "hf": "#/components/schemas/HFModelSource",
- "local": "#/components/schemas/LocalModelSource",
- "url": "#/components/schemas/URLModelSource"
- },
- "propertyName": "type"
+ "model_key_2": {
+ "type": "string",
+ "title": "Model Key 2",
+ "description": "The key of the second model in the relationship",
+ "examples": [
+ "3bb7c0eb-b6c8-469c-ad8c-4d69c06075e4",
+ "f0c3da4e-d9ff-42b5-a45c-23be75c887c9",
+ "38170dd8-f1e5-431e-866c-2c81f1277fcc",
+ "c57fea2d-7646-424c-b9ad-c0ba60fc68be",
+ "10f7807b-ab54-46a9-ab03-600e88c630a1",
+ "f6c1d267-cf87-4ee0-bee0-37e791eacab7"
+ ]
+ }
+ },
+ "type": "object",
+ "required": ["model_key_1", "model_key_2"],
+ "title": "ModelRelationshipCreateRequest"
+ },
+ "ModelRepoVariant": {
+ "type": "string",
+ "enum": ["", "fp16", "fp32", "onnx", "openvino", "flax"],
+ "title": "ModelRepoVariant",
+ "description": "Various hugging face variants on the diffusers format."
+ },
+ "ModelSourceType": {
+ "type": "string",
+ "enum": ["path", "url", "hf_repo_id", "external"],
+ "title": "ModelSourceType",
+ "description": "Model source type."
+ },
+ "ModelType": {
+ "type": "string",
+ "enum": [
+ "onnx",
+ "main",
+ "vae",
+ "lora",
+ "control_lora",
+ "controlnet",
+ "embedding",
+ "ip_adapter",
+ "clip_vision",
+ "clip_embed",
+ "t2i_adapter",
+ "t5_encoder",
+ "qwen3_encoder",
+ "qwen_vl_encoder",
+ "wan_t5_encoder",
+ "spandrel_image_to_image",
+ "siglip",
+ "flux_redux",
+ "llava_onevision",
+ "text_llm",
+ "external_image_generator",
+ "unknown"
+ ],
+ "title": "ModelType",
+ "description": "Model type."
+ },
+ "ModelVariantType": {
+ "type": "string",
+ "enum": ["normal", "inpaint", "depth"],
+ "title": "ModelVariantType",
+ "description": "Variant type."
+ },
+ "ModelsList": {
+ "properties": {
+ "models": {
+ "items": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/VAE_Diffusers_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Wan_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/WanT5Encoder_WanT5Encoder_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_File_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
+ },
+ {
+ "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
+ },
+ {
+ "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
+ },
+ {
+ "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
+ },
+ {
+ "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
+ },
+ {
+ "$ref": "#/components/schemas/ExternalApiModelConfig"
+ },
+ {
+ "$ref": "#/components/schemas/Unknown_Config"
+ }
+ ]
},
- "oneOf": [
- {
- "$ref": "#/components/schemas/LocalModelSource"
- },
- {
- "$ref": "#/components/schemas/HFModelSource"
- },
- {
- "$ref": "#/components/schemas/URLModelSource"
- },
- {
- "$ref": "#/components/schemas/ExternalModelSource"
- }
- ],
- "title": "Source"
- },
- "error_type": {
- "description": "The name of the exception",
- "title": "Error Type",
- "type": "string"
- },
- "error": {
- "description": "A text description of the exception",
- "title": "Error",
- "type": "string"
+ "type": "array",
+ "title": "Models"
}
},
- "required": ["timestamp", "id", "source", "error_type", "error"],
- "title": "ModelInstallErrorEvent",
- "type": "object"
+ "type": "object",
+ "required": ["models"],
+ "title": "ModelsList",
+ "description": "Return list of configs."
},
- "ModelInstallJob": {
+ "MultiplyInvocation": {
+ "category": "math",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Multiplies two numbers",
+ "node_pack": "invokeai",
"properties": {
"id": {
- "type": "integer",
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
"title": "Id",
- "description": "Unique ID for this job"
- },
- "status": {
- "$ref": "#/components/schemas/InstallStatus",
- "description": "Current status of install process",
- "default": "waiting"
- },
- "error_reason": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Error Reason",
- "description": "Information about why the job failed"
- },
- "config_in": {
- "$ref": "#/components/schemas/ModelRecordChanges",
- "description": "Configuration information (e.g. 'description') to apply to model."
- },
- "config_out": {
- "anyOf": [
- {
- "oneOf": [
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/ExternalApiModelConfig"
- },
- {
- "$ref": "#/components/schemas/Unknown_Config"
- }
- ]
- },
- {
- "type": "null"
- }
- ],
- "title": "Config Out",
- "description": "After successful installation, this will hold the configuration object."
+ "type": "string"
},
- "inplace": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
"type": "boolean",
- "title": "Inplace",
- "description": "Leave model in its current location; otherwise install under models directory",
- "default": false
- },
- "source": {
- "oneOf": [
- {
- "$ref": "#/components/schemas/LocalModelSource"
- },
- {
- "$ref": "#/components/schemas/HFModelSource"
- },
- {
- "$ref": "#/components/schemas/URLModelSource"
- },
- {
- "$ref": "#/components/schemas/ExternalModelSource"
- }
- ],
- "title": "Source",
- "description": "Source (URL, repo_id, or local path) of model",
- "discriminator": {
- "propertyName": "type",
- "mapping": {
- "external": "#/components/schemas/ExternalModelSource",
- "hf": "#/components/schemas/HFModelSource",
- "local": "#/components/schemas/LocalModelSource",
- "url": "#/components/schemas/URLModelSource"
- }
- }
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "local_path": {
- "type": "string",
- "format": "path",
- "title": "Local Path",
- "description": "Path to locally-downloaded model; may be the same as the source"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "bytes": {
- "type": "integer",
- "title": "Bytes",
- "description": "For a remote model, the number of bytes downloaded so far (may not be available)",
- "default": 0
+ "a": {
+ "default": 0,
+ "description": "The first number",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "A",
+ "type": "integer"
},
- "total_bytes": {
- "type": "integer",
- "title": "Total Bytes",
- "description": "Total size of the model to be installed",
- "default": 0
+ "b": {
+ "default": 0,
+ "description": "The second number",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "B",
+ "type": "integer"
},
- "source_metadata": {
- "anyOf": [
- {
- "oneOf": [
- {
- "$ref": "#/components/schemas/BaseMetadata"
- },
- {
- "$ref": "#/components/schemas/HuggingFaceMetadata"
- }
- ],
- "discriminator": {
- "propertyName": "type",
- "mapping": {
- "basemetadata": "#/components/schemas/BaseMetadata",
- "huggingface": "#/components/schemas/HuggingFaceMetadata"
- }
- }
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Metadata",
- "description": "Metadata provided by the model source"
+ "type": {
+ "const": "mul",
+ "default": "mul",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["math", "multiply"],
+ "title": "Multiply Integers",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/IntegerOutput"
+ }
+ },
+ "NodeFieldValue": {
+ "properties": {
+ "node_path": {
+ "type": "string",
+ "title": "Node Path",
+ "description": "The node into which this batch data item will be substituted."
},
- "download_parts": {
- "items": {
- "$ref": "#/components/schemas/DownloadJob"
- },
- "type": "array",
- "uniqueItems": true,
- "title": "Download Parts",
- "description": "Download jobs contributing to this install"
+ "field_name": {
+ "type": "string",
+ "title": "Field Name",
+ "description": "The field into which this batch data item will be substituted."
},
- "error": {
+ "value": {
"anyOf": [
{
"type": "string"
},
{
- "type": "null"
- }
- ],
- "title": "Error",
- "description": "On an error condition, this field will contain the text of the exception"
- },
- "error_traceback": {
- "anyOf": [
+ "type": "number"
+ },
{
- "type": "string"
+ "type": "integer"
},
{
- "type": "null"
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "$ref": "#/components/schemas/VideoField"
+ },
+ {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "number"
+ },
+ {
+ "type": "integer"
+ },
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "$ref": "#/components/schemas/VideoField"
+ }
+ ]
+ },
+ "type": "array"
}
],
- "title": "Error Traceback",
- "description": "On an error condition, this field will contain the exception traceback"
+ "title": "Value",
+ "description": "The value to substitute into the node/field."
}
},
"type": "object",
- "required": ["id", "source", "local_path"],
- "title": "ModelInstallJob",
- "description": "Object that tracks the current status of an install request."
+ "required": ["node_path", "field_name", "value"],
+ "title": "NodeFieldValue"
},
- "ModelInstallStartedEvent": {
- "description": "Event model for model_install_started",
+ "NodePackInfo": {
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "The name of the node pack."
},
- "id": {
- "description": "The ID of the install job",
- "title": "Id",
- "type": "integer"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "The path to the node pack directory."
},
- "source": {
- "description": "Source of the model; local path, repo_id or url",
- "discriminator": {
- "mapping": {
- "external": "#/components/schemas/ExternalModelSource",
- "hf": "#/components/schemas/HFModelSource",
- "local": "#/components/schemas/LocalModelSource",
- "url": "#/components/schemas/URLModelSource"
- },
- "propertyName": "type"
+ "node_count": {
+ "type": "integer",
+ "title": "Node Count",
+ "description": "The number of nodes in the pack."
+ },
+ "node_types": {
+ "items": {
+ "type": "string"
},
- "oneOf": [
- {
- "$ref": "#/components/schemas/LocalModelSource"
- },
- {
- "$ref": "#/components/schemas/HFModelSource"
- },
- {
- "$ref": "#/components/schemas/URLModelSource"
- },
- {
- "$ref": "#/components/schemas/ExternalModelSource"
- }
- ],
- "title": "Source"
+ "type": "array",
+ "title": "Node Types",
+ "description": "The invocation types provided by this node pack."
}
},
- "required": ["timestamp", "id", "source"],
- "title": "ModelInstallStartedEvent",
- "type": "object"
+ "type": "object",
+ "required": ["name", "path", "node_count", "node_types"],
+ "title": "NodePackInfo",
+ "description": "Information about an installed node pack."
},
- "ModelLoadCompleteEvent": {
- "description": "Event model for model_load_complete",
+ "NodePackListResponse": {
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
+ "node_packs": {
+ "items": {
+ "$ref": "#/components/schemas/NodePackInfo"
+ },
+ "type": "array",
+ "title": "Node Packs",
+ "description": "List of installed node packs."
+ },
+ "custom_nodes_path": {
+ "type": "string",
+ "title": "Custom Nodes Path",
+ "description": "The configured custom nodes directory path."
+ }
+ },
+ "type": "object",
+ "required": ["node_packs", "custom_nodes_path"],
+ "title": "NodePackListResponse",
+ "description": "Response for listing installed node packs."
+ },
+ "NoiseInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generates latent noise for supported denoiser architectures.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "noise_type": {
+ "default": "SD",
+ "description": "Architecture-specific noise type.",
+ "enum": ["SD", "FLUX", "FLUX.2", "SD3", "CogView4", "Z-Image", "Anima"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "SD",
+ "orig_required": false,
+ "title": "Noise Type",
+ "type": "string"
+ },
+ "seed": {
+ "default": 0,
+ "description": "Seed for random number generation",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 4294967295,
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
"type": "integer"
},
- "config": {
- "description": "The model's config",
- "oneOf": [
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
- },
+ "width": {
+ "default": 512,
+ "description": "Width of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 8,
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 512,
+ "description": "Height of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 8,
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "use_cpu": {
+ "default": true,
+ "description": "Use CPU for noise generation (for reproducible results across platforms)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": true,
+ "orig_required": false,
+ "title": "Use Cpu",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "noise",
+ "default": "noise",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["latents", "noise"],
+ "title": "Create Latent Noise",
+ "type": "object",
+ "version": "1.1.0",
+ "output": {
+ "$ref": "#/components/schemas/NoiseOutput"
+ }
+ },
+ "NoiseOutput": {
+ "class": "output",
+ "description": "Invocation noise output",
+ "properties": {
+ "noise": {
+ "$ref": "#/components/schemas/LatentsField",
+ "description": "Noise tensor",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "width": {
+ "description": "Width of output (px)",
+ "field_kind": "output",
+ "title": "Width",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "height": {
+ "description": "Height of output (px)",
+ "field_kind": "output",
+ "title": "Height",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "noise_output",
+ "default": "noise_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "noise", "width", "height", "type", "type"],
+ "title": "NoiseOutput",
+ "type": "object"
+ },
+ "NormalMapInvocation": {
+ "category": "controlnet_preprocessors",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generates a normal map.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
+ "$ref": "#/components/schemas/BoardField"
},
{
- "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ExternalApiModelConfig"
+ "$ref": "#/components/schemas/MetadataField"
},
{
- "$ref": "#/components/schemas/Unknown_Config"
+ "type": "null"
}
],
- "title": "Config"
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "submodel_type": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/SubModelType"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The submodel type, if any"
+ "description": "The image to process",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "type": {
+ "const": "normal_map",
+ "default": "normal_map",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "required": ["timestamp", "config", "submodel_type"],
- "title": "ModelLoadCompleteEvent",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["controlnet", "normal"],
+ "title": "Normal Map",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "ModelLoadStartedEvent": {
- "description": "Event model for model_load_started",
+ "OffsetPaginatedResults_BoardDTO_": {
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
+ "limit": {
+ "type": "integer",
+ "title": "Limit",
+ "description": "Limit of items to get"
},
- "config": {
- "description": "The model's config",
- "oneOf": [
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
- },
+ "offset": {
+ "type": "integer",
+ "title": "Offset",
+ "description": "Offset from which to retrieve items"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of items in result"
+ },
+ "items": {
+ "items": {
+ "$ref": "#/components/schemas/BoardDTO"
+ },
+ "type": "array",
+ "title": "Items",
+ "description": "Items"
+ }
+ },
+ "type": "object",
+ "required": ["limit", "offset", "total", "items"],
+ "title": "OffsetPaginatedResults[BoardDTO]"
+ },
+ "OffsetPaginatedResults_GalleryItem_": {
+ "properties": {
+ "limit": {
+ "type": "integer",
+ "title": "Limit",
+ "description": "Limit of items to get"
+ },
+ "offset": {
+ "type": "integer",
+ "title": "Offset",
+ "description": "Offset from which to retrieve items"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of items in result"
+ },
+ "items": {
+ "items": {
+ "$ref": "#/components/schemas/GalleryItem"
+ },
+ "type": "array",
+ "title": "Items",
+ "description": "Items"
+ }
+ },
+ "type": "object",
+ "required": ["limit", "offset", "total", "items"],
+ "title": "OffsetPaginatedResults[GalleryItem]"
+ },
+ "OffsetPaginatedResults_ImageDTO_": {
+ "properties": {
+ "limit": {
+ "type": "integer",
+ "title": "Limit",
+ "description": "Limit of items to get"
+ },
+ "offset": {
+ "type": "integer",
+ "title": "Offset",
+ "description": "Offset from which to retrieve items"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of items in result"
+ },
+ "items": {
+ "items": {
+ "$ref": "#/components/schemas/ImageDTO"
+ },
+ "type": "array",
+ "title": "Items",
+ "description": "Items"
+ }
+ },
+ "type": "object",
+ "required": ["limit", "offset", "total", "items"],
+ "title": "OffsetPaginatedResults[ImageDTO]"
+ },
+ "OffsetPaginatedResults_VideoDTO_": {
+ "properties": {
+ "limit": {
+ "type": "integer",
+ "title": "Limit",
+ "description": "Limit of items to get"
+ },
+ "offset": {
+ "type": "integer",
+ "title": "Offset",
+ "description": "Offset from which to retrieve items"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of items in result"
+ },
+ "items": {
+ "items": {
+ "$ref": "#/components/schemas/VideoDTO"
+ },
+ "type": "array",
+ "title": "Items",
+ "description": "Items"
+ }
+ },
+ "type": "object",
+ "required": ["limit", "offset", "total", "items"],
+ "title": "OffsetPaginatedResults[VideoDTO]"
+ },
+ "OklabUnsharpMaskInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Applies an unsharp mask filter to an image in the Oklab color space",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
+ "$ref": "#/components/schemas/BoardField"
},
{
- "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
+ "$ref": "#/components/schemas/MetadataField"
},
{
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to use",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "radius": {
+ "default": 2,
+ "description": "Unsharp mask radius",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 2,
+ "orig_required": false,
+ "title": "Radius",
+ "type": "number"
+ },
+ "strength": {
+ "default": 50,
+ "description": "Unsharp mask strength",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 50,
+ "orig_required": false,
+ "title": "Strength",
+ "type": "number"
+ },
+ "type": {
+ "const": "unsharp_mask_oklab",
+ "default": "unsharp_mask_oklab",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "unsharp_mask", "oklab"],
+ "title": "Unsharp Mask (Oklab)",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "OklchImageHueAdjustmentInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Adjusts the hue of an image in Oklch space.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
+ "$ref": "#/components/schemas/BoardField"
},
{
- "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
+ "$ref": "#/components/schemas/MetadataField"
},
{
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to adjust",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "hue": {
+ "default": 0,
+ "description": "The degrees by which to rotate the hue, 0-360",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Hue",
+ "type": "integer"
+ },
+ "type": {
+ "const": "img_hue_adjust_oklch",
+ "default": "img_hue_adjust_oklch",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "hue", "oklch"],
+ "title": "Adjust Image Hue (Oklch)",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "OpenAIImageGenerationInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generate images using an OpenAI-hosted external model.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
+ "$ref": "#/components/schemas/BoardField"
},
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
+ "$ref": "#/components/schemas/MetadataField"
},
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "model": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Main model (UNet, VAE, CLIP) to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "ui_model_base": ["external"],
+ "ui_model_format": ["external_api"],
+ "ui_model_provider_id": ["openai"],
+ "ui_model_type": ["external_image_generator"]
+ },
+ "mode": {
+ "default": "txt2img",
+ "description": "Generation mode.",
+ "enum": ["txt2img", "img2img", "inpaint"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "txt2img",
+ "orig_required": false,
+ "title": "Mode",
+ "type": "string",
+ "ui_hidden": true
+ },
+ "prompt": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
+ "type": "string"
},
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Prompt",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt"
+ },
+ "seed": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
+ "type": "integer"
},
{
- "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Seed for random number generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Seed"
+ },
+ "num_images": {
+ "default": 1,
+ "description": "Number of images to generate",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Num Images",
+ "type": "integer"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Width of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "image_size": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
+ "type": "string"
},
{
- "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Image size preset (e.g. 1K, 2K, 4K)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Image Size"
+ },
+ "init_image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Init image (use reference_images instead)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_hidden": true
+ },
+ "mask_image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Mask image for inpaint",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_hidden": true
+ },
+ "reference_images": {
+ "default": [],
+ "description": "Reference images",
+ "field_kind": "input",
+ "input": "any",
+ "items": {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ "orig_default": [],
+ "orig_required": false,
+ "title": "Reference Images",
+ "type": "array"
+ },
+ "quality": {
+ "default": "auto",
+ "description": "Output image quality",
+ "enum": ["auto", "high", "medium", "low"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "auto",
+ "orig_required": false,
+ "title": "Quality",
+ "type": "string"
+ },
+ "background": {
+ "default": "auto",
+ "description": "Background transparency handling",
+ "enum": ["auto", "transparent", "opaque"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "auto",
+ "orig_required": false,
+ "title": "Background",
+ "type": "string"
+ },
+ "input_fidelity": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
+ "enum": ["low", "high"],
+ "type": "string"
},
{
- "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Fidelity to source images (edits only)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Input Fidelity"
+ },
+ "type": {
+ "const": "openai_image_generation",
+ "default": "openai_image_generation",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["external", "generation", "openai"],
+ "title": "OpenAI Image Generation",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageCollectionOutput"
+ }
+ },
+ "OrphanedModelInfo": {
+ "properties": {
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Relative path to the orphaned directory from models root"
+ },
+ "absolute_path": {
+ "type": "string",
+ "title": "Absolute Path",
+ "description": "Absolute path to the orphaned directory"
+ },
+ "files": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Files",
+ "description": "List of model files in this directory"
+ },
+ "size_bytes": {
+ "type": "integer",
+ "title": "Size Bytes",
+ "description": "Total size of all files in bytes"
+ }
+ },
+ "type": "object",
+ "required": ["path", "absolute_path", "files", "size_bytes"],
+ "title": "OrphanedModelInfo",
+ "description": "Information about an orphaned model directory."
+ },
+ "OutputFieldJSONSchemaExtra": {
+ "description": "Extra attributes to be added to input fields and their OpenAPI schema. Used by the workflow editor\nduring schema parsing and UI rendering.",
+ "properties": {
+ "field_kind": {
+ "$ref": "#/components/schemas/FieldKind"
+ },
+ "ui_hidden": {
+ "default": false,
+ "title": "Ui Hidden",
+ "type": "boolean"
+ },
+ "ui_order": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
+ "type": "integer"
},
{
- "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "title": "Ui Order"
+ },
+ "ui_type": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
+ "$ref": "#/components/schemas/UIType"
},
{
- "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null
+ }
+ },
+ "required": ["field_kind", "ui_hidden", "ui_order", "ui_type"],
+ "title": "OutputFieldJSONSchemaExtra",
+ "type": "object"
+ },
+ "PBRMapsInvocation": {
+ "category": "controlnet_preprocessors",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generate Normal, Displacement and Roughness Map from a given image",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
+ "$ref": "#/components/schemas/BoardField"
},
{
- "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
+ "$ref": "#/components/schemas/MetadataField"
},
{
- "$ref": "#/components/schemas/TI_File_SD1_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/TI_File_SD2_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/TI_File_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Input image",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "tile_size": {
+ "default": 512,
+ "description": "Tile size",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Tile Size",
+ "type": "integer"
+ },
+ "border_mode": {
+ "default": "none",
+ "description": "Border mode to apply to eliminate any artifacts or seams",
+ "enum": ["none", "seamless", "mirror", "replicate"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "none",
+ "orig_required": false,
+ "title": "Border Mode",
+ "type": "string"
+ },
+ "type": {
+ "const": "pbr_maps",
+ "default": "pbr_maps",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "material"],
+ "title": "PBR Maps",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/PBRMapsOutput"
+ }
+ },
+ "PBRMapsOutput": {
+ "class": "output",
+ "properties": {
+ "normal_map": {
+ "$ref": "#/components/schemas/ImageField",
+ "default": null,
+ "description": "The generated normal map",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "roughness_map": {
+ "$ref": "#/components/schemas/ImageField",
+ "default": null,
+ "description": "The generated roughness map",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "displacement_map": {
+ "$ref": "#/components/schemas/ImageField",
+ "default": null,
+ "description": "The generated displacement map",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "pbr_maps-output",
+ "default": "pbr_maps-output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "normal_map", "roughness_map", "displacement_map", "type", "type"],
+ "title": "PBRMapsOutput",
+ "type": "object"
+ },
+ "PaginatedResults_WorkflowRecordListItemWithThumbnailDTO_": {
+ "properties": {
+ "page": {
+ "type": "integer",
+ "title": "Page",
+ "description": "Current Page"
+ },
+ "pages": {
+ "type": "integer",
+ "title": "Pages",
+ "description": "Total number of pages"
+ },
+ "per_page": {
+ "type": "integer",
+ "title": "Per Page",
+ "description": "Number of items per page"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of items in result"
+ },
+ "items": {
+ "items": {
+ "$ref": "#/components/schemas/WorkflowRecordListItemWithThumbnailDTO"
+ },
+ "type": "array",
+ "title": "Items",
+ "description": "Items"
+ }
+ },
+ "type": "object",
+ "required": ["page", "pages", "per_page", "total", "items"],
+ "title": "PaginatedResults[WorkflowRecordListItemWithThumbnailDTO]"
+ },
+ "PairTileImageInvocation": {
+ "category": "tiles",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Pair an image with its tile properties.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/TI_Folder_SD1_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/TI_Folder_SD2_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The tile image.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "tile": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
+ "$ref": "#/components/schemas/Tile"
},
{
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The tile properties.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "type": {
+ "const": "pair_tile_image",
+ "default": "pair_tile_image",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["tiles"],
+ "title": "Pair Tile with Image",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/PairTileImageOutput"
+ }
+ },
+ "PairTileImageOutput": {
+ "class": "output",
+ "properties": {
+ "tile_with_image": {
+ "$ref": "#/components/schemas/TileWithImage",
+ "description": "A tile description with its corresponding image.",
+ "field_kind": "output",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "pair_tile_image_output",
+ "default": "pair_tile_image_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "tile_with_image", "type", "type"],
+ "title": "PairTileImageOutput",
+ "type": "object"
+ },
+ "PasteImageIntoBoundingBoxInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Paste the source image into the target image at the given bounding box.\n\nThe source image must be the same size as the bounding box, and the bounding box must fit within the target image.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
+ "$ref": "#/components/schemas/BoardField"
},
{
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
+ "$ref": "#/components/schemas/MetadataField"
},
{
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "source_image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to paste",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "target_image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to paste into",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "bounding_box": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
+ "$ref": "#/components/schemas/BoundingBoxField"
},
{
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The bounding box to paste the image into",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "type": {
+ "const": "paste_image_into_bounding_box",
+ "default": "paste_image_into_bounding_box",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "crop"],
+ "title": "Paste Image into Bounding Box",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "PiDiNetEdgeDetectionInvocation": {
+ "category": "controlnet_preprocessors",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generates an edge map using PiDiNet.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
+ "$ref": "#/components/schemas/BoardField"
},
{
- "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
+ "$ref": "#/components/schemas/MetadataField"
},
{
- "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
+ "$ref": "#/components/schemas/ImageField"
},
{
- "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
- },
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to process",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "quantize_edges": {
+ "default": false,
+ "description": "Whether or not to use safe mode",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Quantize Edges",
+ "type": "boolean"
+ },
+ "scribble": {
+ "default": false,
+ "description": "Whether or not to use scribble mode",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Scribble",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "pidi_edge_detection",
+ "default": "pidi_edge_detection",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["controlnet", "edge"],
+ "title": "PiDiNet Edge Detection",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "PresetData": {
+ "properties": {
+ "positive_prompt": {
+ "type": "string",
+ "title": "Positive Prompt",
+ "description": "Positive prompt"
+ },
+ "negative_prompt": {
+ "type": "string",
+ "title": "Negative Prompt",
+ "description": "Negative prompt"
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "required": ["positive_prompt", "negative_prompt"],
+ "title": "PresetData"
+ },
+ "PresetType": {
+ "type": "string",
+ "enum": ["user", "default"],
+ "title": "PresetType"
+ },
+ "ProgressImage": {
+ "description": "The progress image sent intermittently during processing",
+ "properties": {
+ "width": {
+ "description": "The effective width of the image in pixels",
+ "minimum": 1,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "description": "The effective height of the image in pixels",
+ "minimum": 1,
+ "title": "Height",
+ "type": "integer"
+ },
+ "dataURL": {
+ "description": "The image data as a b64 data URL",
+ "title": "Dataurl",
+ "type": "string"
+ }
+ },
+ "required": ["width", "height", "dataURL"],
+ "title": "ProgressImage",
+ "type": "object"
+ },
+ "PromptTemplateInvocation": {
+ "category": "prompt",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Applies a Style Preset template to positive and negative prompts.\n\nSelect a Style Preset and provide positive/negative prompts. The node replaces\n{prompt} placeholders in the template with your input prompts.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "style_preset": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ExternalApiModelConfig"
+ "$ref": "#/components/schemas/StylePresetField"
},
{
- "$ref": "#/components/schemas/Unknown_Config"
+ "type": "null"
}
],
- "title": "Config"
+ "default": null,
+ "description": "The Style Preset to use as a template",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "positive_prompt": {
+ "default": "",
+ "description": "The positive prompt to insert into the template's {prompt} placeholder",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Positive Prompt",
+ "type": "string",
+ "ui_component": "textarea"
},
- "submodel_type": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/SubModelType"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The submodel type, if any"
+ "negative_prompt": {
+ "default": "",
+ "description": "The negative prompt to insert into the template's {prompt} placeholder",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Negative Prompt",
+ "type": "string",
+ "ui_component": "textarea"
+ },
+ "type": {
+ "const": "prompt_template",
+ "default": "prompt_template",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "required": ["timestamp", "config", "submodel_type"],
- "title": "ModelLoadStartedEvent",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["prompt", "template", "style", "preset"],
+ "title": "Prompt Template",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/PromptTemplateOutput"
+ }
},
- "ModelLoaderOutput": {
+ "PromptTemplateOutput": {
"class": "output",
- "description": "Model loader output",
+ "description": "Output for the Prompt Template node",
"properties": {
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
+ "positive_prompt": {
+ "description": "The positive prompt with the template applied",
"field_kind": "output",
- "title": "VAE",
+ "title": "Positive Prompt",
+ "type": "string",
+ "ui_hidden": false
+ },
+ "negative_prompt": {
+ "description": "The negative prompt with the template applied",
+ "field_kind": "output",
+ "title": "Negative Prompt",
+ "type": "string",
"ui_hidden": false
},
"type": {
- "const": "model_loader_output",
- "default": "model_loader_output",
+ "const": "prompt_template_output",
+ "default": "prompt_template_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
- },
- "clip": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP",
- "ui_hidden": false
- },
- "unet": {
- "$ref": "#/components/schemas/UNetField",
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "output",
- "title": "UNet",
- "ui_hidden": false
}
},
- "required": ["output_meta", "vae", "type", "clip", "unet", "type"],
- "title": "ModelLoaderOutput",
+ "required": ["output_meta", "positive_prompt", "negative_prompt", "type", "type"],
+ "title": "PromptTemplateOutput",
"type": "object"
},
- "ModelRecordChanges": {
+ "PromptsFromFileInvocation": {
+ "category": "prompt",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Loads prompts from a text file",
+ "node_pack": "invokeai",
"properties": {
- "source": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source",
- "description": "original source of the model"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source_type": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelSourceType"
- },
- {
- "type": "null"
- }
- ],
- "description": "type of model source"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "source_api_response": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "file_path": {
"anyOf": [
{
"type": "string"
@@ -57499,10 +63136,14 @@
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "metadata from remote source"
+ "default": null,
+ "description": "Path to prompt text file",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "File Path"
},
- "source_url": {
+ "pre_prompt": {
"anyOf": [
{
"type": "string"
@@ -57511,10 +63152,16 @@
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page)"
+ "default": null,
+ "description": "String to prepend to each prompt",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Pre Prompt",
+ "ui_component": "textarea"
},
- "name": {
+ "post_prompt": {
"anyOf": [
{
"type": "string"
@@ -57523,10 +63170,81 @@
"type": "null"
}
],
- "title": "Name",
- "description": "Name of the model."
+ "default": null,
+ "description": "String to append to each prompt",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Post Prompt",
+ "ui_component": "textarea"
},
- "path": {
+ "start_line": {
+ "default": 1,
+ "description": "Line in the file to start start from",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Start Line",
+ "type": "integer"
+ },
+ "max_prompts": {
+ "default": 1,
+ "description": "Max lines to read from file (0=all)",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Max Prompts",
+ "type": "integer"
+ },
+ "type": {
+ "const": "prompt_from_file",
+ "default": "prompt_from_file",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "file"],
+ "title": "Prompts from File",
+ "type": "object",
+ "version": "1.0.2",
+ "output": {
+ "$ref": "#/components/schemas/StringCollectionOutput"
+ }
+ },
+ "PruneResult": {
+ "properties": {
+ "deleted": {
+ "type": "integer",
+ "title": "Deleted",
+ "description": "Number of queue items deleted"
+ }
+ },
+ "type": "object",
+ "required": ["deleted"],
+ "title": "PruneResult",
+ "description": "Result of pruning the session queue"
+ },
+ "QueueClearedEvent": {
+ "description": "Event model for queue_cleared",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "queue_id": {
+ "description": "The ID of the queue",
+ "title": "Queue Id",
+ "type": "string"
+ },
+ "user_id": {
"anyOf": [
{
"type": "string"
@@ -57535,10 +63253,39 @@
"type": "null"
}
],
- "title": "Path",
- "description": "Path to the model."
+ "default": null,
+ "description": "The ID of the user whose queue items were cleared, or None if all users' items were cleared",
+ "title": "User Id"
+ }
+ },
+ "required": ["timestamp", "queue_id", "user_id"],
+ "title": "QueueClearedEvent",
+ "type": "object"
+ },
+ "QueueItemStatusChangedEvent": {
+ "description": "Event model for queue_item_status_changed",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
- "description": {
+ "queue_id": {
+ "description": "The ID of the queue",
+ "title": "Queue Id",
+ "type": "string"
+ },
+ "item_id": {
+ "description": "The ID of the queue item",
+ "title": "Item Id",
+ "type": "integer"
+ },
+ "batch_id": {
+ "description": "The ID of the queue batch",
+ "title": "Batch Id",
+ "type": "string"
+ },
+ "origin": {
"anyOf": [
{
"type": "string"
@@ -57547,32 +63294,49 @@
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
+ "default": null,
+ "description": "The origin of the queue item",
+ "title": "Origin"
},
- "base": {
+ "destination": {
"anyOf": [
{
- "$ref": "#/components/schemas/BaseModelType"
+ "type": "string"
},
{
"type": "null"
}
],
- "description": "The base model."
+ "default": null,
+ "description": "The destination of the queue item",
+ "title": "Destination"
},
- "type": {
+ "user_id": {
+ "default": "system",
+ "description": "The ID of the user who created the queue item",
+ "title": "User Id",
+ "type": "string"
+ },
+ "status": {
+ "description": "The new status of the queue item",
+ "enum": ["pending", "in_progress", "waiting", "completed", "failed", "canceled"],
+ "title": "Status",
+ "type": "string"
+ },
+ "status_sequence": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelType"
+ "type": "integer"
},
{
"type": "null"
}
],
- "description": "Type of model"
+ "default": null,
+ "description": "A monotonically increasing version for this queue item's visible status lifecycle",
+ "title": "Status Sequence"
},
- "key": {
+ "error_type": {
"anyOf": [
{
"type": "string"
@@ -57581,10 +63345,11 @@
"type": "null"
}
],
- "title": "Key",
- "description": "Database ID for this model"
+ "default": null,
+ "description": "The error type, if any",
+ "title": "Error Type"
},
- "hash": {
+ "error_message": {
"anyOf": [
{
"type": "string"
@@ -57593,22 +63358,34 @@
"type": "null"
}
],
- "title": "Hash",
- "description": "hash of model file"
+ "default": null,
+ "description": "The error message, if any",
+ "title": "Error Message"
},
- "file_size": {
+ "error_traceback": {
"anyOf": [
{
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "File Size",
- "description": "Size of model file"
+ "default": null,
+ "description": "The error traceback, if any",
+ "title": "Error Traceback"
},
- "format": {
+ "created_at": {
+ "description": "The timestamp when the queue item was created",
+ "title": "Created At",
+ "type": "string"
+ },
+ "updated_at": {
+ "description": "The timestamp when the queue item was last updated",
+ "title": "Updated At",
+ "type": "string"
+ },
+ "started_at": {
"anyOf": [
{
"type": "string"
@@ -57617,59 +63394,158 @@
"type": "null"
}
],
- "title": "Format",
- "description": "format of model file"
+ "default": null,
+ "description": "The timestamp when the queue item was started",
+ "title": "Started At"
},
- "trigger_phrases": {
+ "completed_at": {
"anyOf": [
{
- "items": {
- "type": "string"
- },
- "type": "array",
- "uniqueItems": true
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Trigger Phrases",
- "description": "Set of trigger phrases for this model"
+ "default": null,
+ "description": "The timestamp when the queue item was completed",
+ "title": "Completed At"
},
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MainModelDefaultSettings"
- },
- {
- "$ref": "#/components/schemas/LoraModelDefaultSettings"
- },
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
- },
- {
- "type": "null"
- }
- ],
- "title": "Default Settings",
- "description": "Default settings for this model"
+ "batch_status": {
+ "$ref": "#/components/schemas/BatchStatus",
+ "description": "The status of the batch"
},
- "provider_id": {
- "anyOf": [
- {
- "type": "string"
+ "queue_status": {
+ "$ref": "#/components/schemas/SessionQueueStatus",
+ "description": "The status of the queue"
+ },
+ "session_id": {
+ "description": "The ID of the session (aka graph execution state)",
+ "title": "Session Id",
+ "type": "string"
+ }
+ },
+ "required": [
+ "timestamp",
+ "queue_id",
+ "item_id",
+ "batch_id",
+ "origin",
+ "destination",
+ "user_id",
+ "status",
+ "status_sequence",
+ "error_type",
+ "error_message",
+ "error_traceback",
+ "created_at",
+ "updated_at",
+ "started_at",
+ "completed_at",
+ "batch_status",
+ "queue_status",
+ "session_id"
+ ],
+ "title": "QueueItemStatusChangedEvent",
+ "type": "object"
+ },
+ "QueueItemsRetriedEvent": {
+ "description": "Event model for queue_items_retried",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
+ },
+ "queue_id": {
+ "description": "The ID of the queue",
+ "title": "Queue Id",
+ "type": "string"
+ },
+ "retried_item_ids": {
+ "description": "The IDs of the queue items that were retried",
+ "items": {
+ "type": "integer"
+ },
+ "title": "Retried Item Ids",
+ "type": "array"
+ },
+ "user_ids": {
+ "description": "The IDs of the users who own the retried root queue items",
+ "items": {
+ "type": "string"
+ },
+ "title": "User Ids",
+ "type": "array"
+ },
+ "retried_item_ids_by_user": {
+ "additionalProperties": {
+ "items": {
+ "type": "integer"
},
- {
- "type": "null"
- }
- ],
- "title": "Provider Id",
- "description": "External provider identifier"
+ "type": "array"
+ },
+ "description": "The retried root queue item IDs keyed by owner user ID.",
+ "title": "Retried Item Ids By User",
+ "type": "object"
+ }
+ },
+ "required": ["timestamp", "queue_id", "retried_item_ids", "user_ids", "retried_item_ids_by_user"],
+ "title": "QueueItemsRetriedEvent",
+ "type": "object"
+ },
+ "Qwen3EncoderField": {
+ "description": "Field for Qwen3 text encoder used by Z-Image models.",
+ "properties": {
+ "tokenizer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load tokenizer submodel"
+ },
+ "text_encoder": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load text_encoder submodel"
+ },
+ "loras": {
+ "description": "LoRAs to apply on model loading",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "Loras",
+ "type": "array"
+ }
+ },
+ "required": ["tokenizer", "text_encoder"],
+ "title": "Qwen3EncoderField",
+ "type": "object"
+ },
+ "Qwen3Encoder_Checkpoint_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
},
- "provider_model_id": {
+ "description": {
"anyOf": [
{
"type": "string"
@@ -57678,84 +63554,53 @@
"type": "null"
}
],
- "title": "Provider Model Id",
- "description": "External provider model identifier"
+ "title": "Description",
+ "description": "Model description"
},
- "capabilities": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ExternalModelCapabilities"
- },
- {
- "type": "null"
- }
- ],
- "description": "External model capabilities"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "cpu_only": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "variant": {
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelVariantType"
- },
- {
- "$ref": "#/components/schemas/ClipVariantType"
- },
- {
- "$ref": "#/components/schemas/FluxVariantType"
- },
- {
- "$ref": "#/components/schemas/Flux2VariantType"
- },
- {
- "$ref": "#/components/schemas/ZImageVariantType"
- },
- {
- "$ref": "#/components/schemas/QwenImageVariantType"
- },
- {
- "$ref": "#/components/schemas/Qwen3VariantType"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Variant",
- "description": "The variant of the model."
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "prediction_type": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/SchedulerPredictionType"
+ "type": "string"
},
{
"type": "null"
}
],
- "description": "The prediction type of the model."
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "upcast_attention": {
+ "cover_image": {
"anyOf": [
{
- "type": "boolean"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Upcast Attention",
- "description": "Whether to upcast attention."
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
"config_path": {
"anyOf": [
@@ -57767,737 +63612,407 @@
}
],
"title": "Config Path",
- "description": "Path to config file for model"
- }
- },
- "type": "object",
- "title": "ModelRecordChanges",
- "description": "A set of changes to apply to a model."
- },
- "ModelRecordOrderBy": {
- "type": "string",
- "enum": ["default", "type", "base", "name", "format", "size", "created_at", "updated_at", "path"],
- "title": "ModelRecordOrderBy",
- "description": "The order in which to return model summaries."
- },
- "ModelRelationshipBatchRequest": {
- "properties": {
- "model_keys": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Model Keys",
- "description": "List of model keys to fetch related models for",
- "examples": [
- ["aa3b247f-90c9-4416-bfcd-aeaa57a5339e", "ac32b914-10ab-496e-a24a-3068724b9c35"],
- [
- "b1c2d3e4-f5a6-7890-abcd-ef1234567890",
- "12345678-90ab-cdef-1234-567890abcdef",
- "fedcba98-7654-3210-fedc-ba9876543210"
- ],
- ["3bb7c0eb-b6c8-469c-ad8c-4d69c06075e4"]
- ]
- }
- },
- "type": "object",
- "required": ["model_keys"],
- "title": "ModelRelationshipBatchRequest"
- },
- "ModelRelationshipCreateRequest": {
- "properties": {
- "model_key_1": {
- "type": "string",
- "title": "Model Key 1",
- "description": "The key of the first model in the relationship",
- "examples": [
- "aa3b247f-90c9-4416-bfcd-aeaa57a5339e",
- "ac32b914-10ab-496e-a24a-3068724b9c35",
- "d944abfd-c7c3-42e2-a4ff-da640b29b8b4",
- "b1c2d3e4-f5a6-7890-abcd-ef1234567890",
- "12345678-90ab-cdef-1234-567890abcdef",
- "fedcba98-7654-3210-fedc-ba9876543210"
- ]
+ "description": "Path to the config for this model, if any."
},
- "model_key_2": {
+ "base": {
"type": "string",
- "title": "Model Key 2",
- "description": "The key of the second model in the relationship",
- "examples": [
- "3bb7c0eb-b6c8-469c-ad8c-4d69c06075e4",
- "f0c3da4e-d9ff-42b5-a45c-23be75c887c9",
- "38170dd8-f1e5-431e-866c-2c81f1277fcc",
- "c57fea2d-7646-424c-b9ad-c0ba60fc68be",
- "10f7807b-ab54-46a9-ab03-600e88c630a1",
- "f6c1d267-cf87-4ee0-bee0-37e791eacab7"
- ]
- }
- },
- "type": "object",
- "required": ["model_key_1", "model_key_2"],
- "title": "ModelRelationshipCreateRequest"
- },
- "ModelRepoVariant": {
- "type": "string",
- "enum": ["", "fp16", "fp32", "onnx", "openvino", "flax"],
- "title": "ModelRepoVariant",
- "description": "Various hugging face variants on the diffusers format."
- },
- "ModelSourceType": {
- "type": "string",
- "enum": ["path", "url", "hf_repo_id", "external"],
- "title": "ModelSourceType",
- "description": "Model source type."
- },
- "ModelType": {
- "type": "string",
- "enum": [
- "onnx",
- "main",
- "vae",
- "lora",
- "control_lora",
- "controlnet",
- "embedding",
- "ip_adapter",
- "clip_vision",
- "clip_embed",
- "t2i_adapter",
- "t5_encoder",
- "qwen3_encoder",
- "qwen_vl_encoder",
- "spandrel_image_to_image",
- "siglip",
- "flux_redux",
- "llava_onevision",
- "text_llm",
- "external_image_generator",
- "unknown"
- ],
- "title": "ModelType",
- "description": "Model type."
- },
- "ModelVariantType": {
- "type": "string",
- "enum": ["normal", "inpaint", "depth"],
- "title": "ModelVariantType",
- "description": "Variant type."
- },
- "ModelsList": {
- "properties": {
- "models": {
- "items": {
- "oneOf": [
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_SD3_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_CogView4_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_SDXLRefiner_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/Main_BnBNF4_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/Main_GGUF_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/VAE_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Checkpoint_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/ControlNet_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_LyCORIS_Anima_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_OMI_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_Flux2_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/LoRA_Diffusers_ZImage_Config"
- },
- {
- "$ref": "#/components/schemas/ControlLoRA_LyCORIS_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_T5Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/Qwen3Encoder_GGUF_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/QwenVLEncoder_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_File_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/TI_Folder_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_InvokeAI_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SD2_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/IPAdapter_Checkpoint_FLUX_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SD1_Config"
- },
- {
- "$ref": "#/components/schemas/T2IAdapter_Diffusers_SDXL_Config"
- },
- {
- "$ref": "#/components/schemas/Spandrel_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_G_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPEmbed_Diffusers_L_Config"
- },
- {
- "$ref": "#/components/schemas/CLIPVision_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/SigLIP_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/FLUXRedux_Checkpoint_Config"
- },
- {
- "$ref": "#/components/schemas/LlavaOnevision_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/TextLLM_Diffusers_Config"
- },
- {
- "$ref": "#/components/schemas/ExternalApiModelConfig"
- },
- {
- "$ref": "#/components/schemas/Unknown_Config"
- }
- ]
- },
- "type": "array",
- "title": "Models"
- }
- },
- "type": "object",
- "required": ["models"],
- "title": "ModelsList",
- "description": "Return list of configs."
- },
- "MultiplyInvocation": {
- "category": "math",
- "class": "invocation",
- "classification": "stable",
- "description": "Multiplies two numbers",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "type": {
+ "type": "string",
+ "const": "qwen3_encoder",
+ "title": "Type",
+ "default": "qwen3_encoder"
},
- "a": {
- "default": 0,
- "description": "The first number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "A",
- "type": "integer"
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
},
- "b": {
- "default": 0,
- "description": "The second number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "B",
- "type": "integer"
+ "cpu_only": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
},
- "type": {
- "const": "mul",
- "default": "mul",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "variant": {
+ "$ref": "#/components/schemas/Qwen3VariantType",
+ "description": "Qwen3 model size variant (4B or 8B)"
}
},
- "required": ["type", "id"],
- "tags": ["math", "multiply"],
- "title": "Multiply Integers",
"type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/IntegerOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "base",
+ "type",
+ "format",
+ "cpu_only",
+ "variant"
+ ],
+ "title": "Qwen3Encoder_Checkpoint_Config",
+ "description": "Configuration for single-file Qwen3 Encoder models (safetensors)."
},
- "NodeFieldValue": {
+ "Qwen3Encoder_GGUF_Config": {
"properties": {
- "node_path": {
+ "key": {
"type": "string",
- "title": "Node Path",
- "description": "The node into which this batch data item will be substituted."
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "field_name": {
+ "hash": {
"type": "string",
- "title": "Field Name",
- "description": "The field into which this batch data item will be substituted."
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "value": {
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
"type": "string"
},
{
- "type": "number"
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
- "type": "integer"
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
- "items": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "number"
- },
- {
- "type": "integer"
- },
- {
- "$ref": "#/components/schemas/ImageField"
- }
- ]
- },
- "type": "array"
+ "type": "null"
}
],
- "title": "Value",
- "description": "The value to substitute into the node/field."
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
+ },
+ "type": {
+ "type": "string",
+ "const": "qwen3_encoder",
+ "title": "Type",
+ "default": "qwen3_encoder"
+ },
+ "format": {
+ "type": "string",
+ "const": "gguf_quantized",
+ "title": "Format",
+ "default": "gguf_quantized"
+ },
+ "cpu_only": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/Qwen3VariantType",
+ "description": "Qwen3 model size variant (4B or 8B)"
}
},
"type": "object",
- "required": ["node_path", "field_name", "value"],
- "title": "NodeFieldValue"
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "base",
+ "type",
+ "format",
+ "cpu_only",
+ "variant"
+ ],
+ "title": "Qwen3Encoder_GGUF_Config",
+ "description": "Configuration for GGUF-quantized Qwen3 Encoder models."
},
- "NodePackInfo": {
+ "Qwen3Encoder_Qwen3Encoder_Config": {
"properties": {
- "name": {
+ "key": {
"type": "string",
- "title": "Name",
- "description": "The name of the node pack."
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
"path": {
"type": "string",
"title": "Path",
- "description": "The path to the node pack directory."
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "node_count": {
+ "file_size": {
"type": "integer",
- "title": "Node Count",
- "description": "The number of nodes in the pack."
- },
- "node_types": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Node Types",
- "description": "The invocation types provided by this node pack."
- }
- },
- "type": "object",
- "required": ["name", "path", "node_count", "node_types"],
- "title": "NodePackInfo",
- "description": "Information about an installed node pack."
- },
- "NodePackListResponse": {
- "properties": {
- "node_packs": {
- "items": {
- "$ref": "#/components/schemas/NodePackInfo"
- },
- "type": "array",
- "title": "Node Packs",
- "description": "List of installed node packs."
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "custom_nodes_path": {
+ "name": {
"type": "string",
- "title": "Custom Nodes Path",
- "description": "The configured custom nodes directory path."
- }
- },
- "type": "object",
- "required": ["node_packs", "custom_nodes_path"],
- "title": "NodePackListResponse",
- "description": "Response for listing installed node packs."
- },
- "NoiseInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "stable",
- "description": "Generates latent noise for supported denoiser architectures.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "title": "Name",
+ "description": "Name of the model."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "noise_type": {
- "default": "SD",
- "description": "Architecture-specific noise type.",
- "enum": ["SD", "FLUX", "FLUX.2", "SD3", "CogView4", "Z-Image", "Anima"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "SD",
- "orig_required": false,
- "title": "Noise Type",
- "type": "string"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "seed": {
- "default": 0,
- "description": "Seed for random number generation",
- "field_kind": "input",
- "input": "any",
- "maximum": 4294967295,
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "width": {
- "default": 512,
- "description": "Width of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "multipleOf": 8,
- "orig_default": 512,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "height": {
- "default": 512,
- "description": "Height of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "multipleOf": 8,
- "orig_default": 512,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "use_cpu": {
- "default": true,
- "description": "Use CPU for noise generation (for reproducible results across platforms)",
- "field_kind": "input",
- "input": "any",
- "orig_default": true,
- "orig_required": false,
- "title": "Use Cpu",
- "type": "boolean"
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
"type": {
- "const": "noise",
- "default": "noise",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "qwen3_encoder",
+ "title": "Type",
+ "default": "qwen3_encoder"
+ },
+ "format": {
+ "type": "string",
+ "const": "qwen3_encoder",
+ "title": "Format",
+ "default": "qwen3_encoder"
+ },
+ "cpu_only": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
+ },
+ "variant": {
+ "$ref": "#/components/schemas/Qwen3VariantType",
+ "description": "Qwen3 model size variant (4B or 8B)"
}
},
- "required": ["type", "id"],
- "tags": ["latents", "noise"],
- "title": "Create Latent Noise",
"type": "object",
- "version": "1.1.0",
- "output": {
- "$ref": "#/components/schemas/NoiseOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "base",
+ "type",
+ "format",
+ "cpu_only",
+ "variant"
+ ],
+ "title": "Qwen3Encoder_Qwen3Encoder_Config",
+ "description": "Configuration for Qwen3 Encoder models in a diffusers-like format.\n\nThe model weights are expected to be in a folder called text_encoder inside the model directory,\ncompatible with Qwen2VLForConditionalGeneration or similar architectures used by Z-Image."
+ },
+ "Qwen3VariantType": {
+ "type": "string",
+ "enum": ["qwen3_4b", "qwen3_8b", "qwen3_06b"],
+ "title": "Qwen3VariantType",
+ "description": "Qwen3 text encoder variants based on model size."
+ },
+ "QwenImageConditioningField": {
+ "description": "A Qwen Image Edit conditioning tensor primitive value",
+ "properties": {
+ "conditioning_name": {
+ "description": "The name of conditioning tensor",
+ "title": "Conditioning Name",
+ "type": "string"
+ }
+ },
+ "required": ["conditioning_name"],
+ "title": "QwenImageConditioningField",
+ "type": "object"
},
- "NoiseOutput": {
+ "QwenImageConditioningOutput": {
"class": "output",
- "description": "Invocation noise output",
+ "description": "Base class for nodes that output a Qwen Image Edit conditioning tensor.",
"properties": {
- "noise": {
- "$ref": "#/components/schemas/LatentsField",
- "description": "Noise tensor",
- "field_kind": "output",
- "ui_hidden": false
- },
- "width": {
- "description": "Width of output (px)",
- "field_kind": "output",
- "title": "Width",
- "type": "integer",
- "ui_hidden": false
- },
- "height": {
- "description": "Height of output (px)",
+ "conditioning": {
+ "$ref": "#/components/schemas/QwenImageConditioningField",
+ "description": "Conditioning tensor",
"field_kind": "output",
- "title": "Height",
- "type": "integer",
"ui_hidden": false
},
"type": {
- "const": "noise_output",
- "default": "noise_output",
+ "const": "qwen_image_conditioning_output",
+ "default": "qwen_image_conditioning_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "noise", "width", "height", "type", "type"],
- "title": "NoiseOutput",
+ "required": ["output_meta", "conditioning", "type", "type"],
+ "title": "QwenImageConditioningOutput",
"type": "object"
},
- "NormalMapInvocation": {
- "category": "controlnet_preprocessors",
+ "QwenImageDenoiseInvocation": {
+ "category": "image",
"class": "invocation",
- "classification": "stable",
- "description": "Generates a normal map.",
+ "classification": "prototype",
+ "description": "Run the denoising process with a Qwen Image model.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -58556,220 +64071,227 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "latents": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to process",
+ "description": "Latents tensor",
"field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "type": {
- "const": "normal_map",
- "default": "normal_map",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["controlnet", "normal"],
- "title": "Normal Map",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
- },
- "OffsetPaginatedResults_BoardDTO_": {
- "properties": {
- "limit": {
- "type": "integer",
- "title": "Limit",
- "description": "Limit of items to get"
- },
- "offset": {
- "type": "integer",
- "title": "Offset",
- "description": "Offset from which to retrieve items"
- },
- "total": {
- "type": "integer",
- "title": "Total",
- "description": "Total number of items in result"
- },
- "items": {
- "items": {
- "$ref": "#/components/schemas/BoardDTO"
- },
- "type": "array",
- "title": "Items",
- "description": "Items"
- }
- },
- "type": "object",
- "required": ["limit", "offset", "total", "items"],
- "title": "OffsetPaginatedResults[BoardDTO]"
- },
- "OffsetPaginatedResults_ImageDTO_": {
- "properties": {
- "limit": {
- "type": "integer",
- "title": "Limit",
- "description": "Limit of items to get"
- },
- "offset": {
- "type": "integer",
- "title": "Offset",
- "description": "Offset from which to retrieve items"
- },
- "total": {
- "type": "integer",
- "title": "Total",
- "description": "Total number of items in result"
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "items": {
- "items": {
- "$ref": "#/components/schemas/ImageDTO"
- },
- "type": "array",
- "title": "Items",
- "description": "Items"
- }
- },
- "type": "object",
- "required": ["limit", "offset", "total", "items"],
- "title": "OffsetPaginatedResults[ImageDTO]"
- },
- "OklabUnsharpMaskInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Applies an unsharp mask filter to an image in the Oklab color space",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "reference_latents": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "description": "Reference image latents to guide generation. Encoded through the VAE.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "metadata": {
+ "denoise_mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "$ref": "#/components/schemas/DenoiseMaskField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "input",
"input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
"orig_required": false,
- "ui_hidden": false
+ "title": "Denoising Start",
+ "type": "number"
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/TransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Qwen Image Edit model (Transformer) to load",
+ "field_kind": "input",
+ "input": "connection",
"orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "title": "Transformer"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "positive_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/QwenImageConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "image": {
+ "negative_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/QwenImageConditioningField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to use",
+ "description": "Negative conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "cfg_scale": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 4.0,
+ "description": "Classifier-Free Guidance scale",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": 4.0,
+ "orig_required": false,
+ "title": "CFG Scale"
},
- "radius": {
- "default": 2,
- "description": "Unsharp mask radius",
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "steps": {
+ "default": 40,
+ "description": "Number of steps to run",
"exclusiveMinimum": 0,
"field_kind": "input",
"input": "any",
- "orig_default": 2,
+ "orig_default": 40,
"orig_required": false,
- "title": "Radius",
- "type": "number"
+ "title": "Steps",
+ "type": "integer"
},
- "strength": {
- "default": 50,
- "description": "Unsharp mask strength",
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
"field_kind": "input",
"input": "any",
- "minimum": 0,
- "orig_default": 50,
+ "orig_default": 0,
"orig_required": false,
- "title": "Strength",
- "type": "number"
+ "title": "Seed",
+ "type": "integer"
+ },
+ "shift": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Override the sigma schedule shift. When set, uses a fixed shift (e.g. 3.0 for Lightning LoRAs) instead of the default dynamic shifting. Leave unset for the base model's default schedule.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Shift"
},
"type": {
- "const": "unsharp_mask_oklab",
- "default": "unsharp_mask_oklab",
+ "const": "qwen_image_denoise",
+ "default": "qwen_image_denoise",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "unsharp_mask", "oklab"],
- "title": "Unsharp Mask (Oklab)",
+ "tags": ["image", "qwen_image"],
+ "title": "Denoise - Qwen Image",
"type": "object",
"version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/LatentsOutput"
}
},
- "OklchImageHueAdjustmentInvocation": {
+ "QwenImageImageToLatentsInvocation": {
"category": "image",
"class": "invocation",
- "classification": "stable",
- "description": "Adjusts the hue of an image in Oklch space.",
+ "classification": "prototype",
+ "description": "Generates latents from an image using the Qwen Image VAE.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -58838,43 +64360,82 @@
}
],
"default": null,
- "description": "The image to adjust",
+ "description": "The image to encode.",
"field_kind": "input",
"input": "any",
"orig_required": true
},
- "hue": {
- "default": 0,
- "description": "The degrees by which to rotate the hue, 0-360",
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "width": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Resize the image to this width before encoding. If not set, encodes at the image's original size.",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
+ "orig_default": null,
"orig_required": false,
- "title": "Hue",
- "type": "integer"
+ "title": "Width"
+ },
+ "height": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Resize the image to this height before encoding. If not set, encodes at the image's original size.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Height"
},
"type": {
- "const": "img_hue_adjust_oklch",
- "default": "img_hue_adjust_oklch",
+ "const": "qwen_image_i2l",
+ "default": "qwen_image_i2l",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "hue", "oklch"],
- "title": "Adjust Image Hue (Oklch)",
+ "tags": ["image", "latents", "vae", "i2l", "qwen_image"],
+ "title": "Image to Latents - Qwen Image",
"type": "object",
"version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/LatentsOutput"
}
},
- "OpenAIImageGenerationInvocation": {
- "category": "image",
+ "QwenImageLatentsToImageInvocation": {
+ "category": "latents",
"class": "invocation",
- "classification": "stable",
- "description": "Generate images using an OpenAI-hosted external model.",
+ "classification": "prototype",
+ "description": "Generates an image from latents using the Qwen Image VAE.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -58923,343 +64484,284 @@
"orig_required": true,
"title": "Is Intermediate",
"type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Main model (UNet, VAE, CLIP) to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_model_base": ["external"],
- "ui_model_format": ["external_api"],
- "ui_model_provider_id": ["openai"],
- "ui_model_type": ["external_image_generator"]
- },
- "mode": {
- "default": "txt2img",
- "description": "Generation mode.",
- "enum": ["txt2img", "img2img", "inpaint"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "txt2img",
- "orig_required": false,
- "title": "Mode",
- "type": "string",
- "ui_hidden": true
- },
- "prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Prompt",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Prompt"
- },
- "seed": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Seed for random number generation",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Seed"
- },
- "num_images": {
- "default": 1,
- "description": "Number of images to generate",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "Num Images",
- "type": "integer"
- },
- "width": {
- "default": 1024,
- "description": "Width of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "height": {
- "default": 1024,
- "description": "Height of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "image_size": {
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Image size preset (e.g. 1K, 2K, 4K)",
+ "description": "Latents tensor",
"field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Image Size"
+ "input": "connection",
+ "orig_required": true
},
- "init_image": {
+ "vae": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Init image (use reference_images instead)",
+ "description": "VAE",
"field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "ui_hidden": true
+ "input": "connection",
+ "orig_required": true
},
- "mask_image": {
+ "type": {
+ "const": "qwen_image_l2i",
+ "default": "qwen_image_l2i",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "l2i", "qwen_image"],
+ "title": "Latents to Image - Qwen Image",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "QwenImageLoRACollectionLoader": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Applies a collection of LoRAs to a Qwen Image transformer.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "loras": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "Mask image for inpaint",
+ "description": "LoRA models and weights. May be a single LoRA or collection.",
"field_kind": "input",
"input": "any",
"orig_default": null,
"orig_required": false,
- "ui_hidden": true
- },
- "reference_images": {
- "default": [],
- "description": "Reference images",
- "field_kind": "input",
- "input": "any",
- "items": {
- "$ref": "#/components/schemas/ImageField"
- },
- "orig_default": [],
- "orig_required": false,
- "title": "Reference Images",
- "type": "array"
- },
- "quality": {
- "default": "auto",
- "description": "Output image quality",
- "enum": ["auto", "high", "medium", "low"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "auto",
- "orig_required": false,
- "title": "Quality",
- "type": "string"
- },
- "background": {
- "default": "auto",
- "description": "Background transparency handling",
- "enum": ["auto", "transparent", "opaque"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "auto",
- "orig_required": false,
- "title": "Background",
- "type": "string"
+ "title": "LoRAs",
+ "ui_model_base": ["qwen-image"],
+ "ui_model_type": ["lora"]
},
- "input_fidelity": {
+ "transformer": {
"anyOf": [
{
- "enum": ["low", "high"],
- "type": "string"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Fidelity to source images (edits only)",
+ "description": "Transformer",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_default": null,
"orig_required": false,
- "title": "Input Fidelity"
+ "title": "Transformer"
},
"type": {
- "const": "openai_image_generation",
- "default": "openai_image_generation",
+ "const": "qwen_image_lora_collection_loader",
+ "default": "qwen_image_lora_collection_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["external", "generation", "openai"],
- "title": "OpenAI Image Generation",
+ "tags": ["lora", "model", "qwen_image"],
+ "title": "Apply LoRA Collection - Qwen Image",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/ImageCollectionOutput"
+ "$ref": "#/components/schemas/QwenImageLoRALoaderOutput"
}
},
- "OrphanedModelInfo": {
- "properties": {
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Relative path to the orphaned directory from models root"
- },
- "absolute_path": {
- "type": "string",
- "title": "Absolute Path",
- "description": "Absolute path to the orphaned directory"
- },
- "files": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Files",
- "description": "List of model files in this directory"
- },
- "size_bytes": {
- "type": "integer",
- "title": "Size Bytes",
- "description": "Total size of all files in bytes"
- }
- },
- "type": "object",
- "required": ["path", "absolute_path", "files", "size_bytes"],
- "title": "OrphanedModelInfo",
- "description": "Information about an orphaned model directory."
- },
- "OutputFieldJSONSchemaExtra": {
- "description": "Extra attributes to be added to input fields and their OpenAPI schema. Used by the workflow editor\nduring schema parsing and UI rendering.",
+ "QwenImageLoRALoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Apply a LoRA model to a Qwen Image transformer.",
+ "node_pack": "invokeai",
"properties": {
- "field_kind": {
- "$ref": "#/components/schemas/FieldKind"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "ui_hidden": {
+ "is_intermediate": {
"default": false,
- "title": "Ui Hidden",
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
"type": "boolean"
},
- "ui_order": {
+ "lora": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "title": "Ui Order"
+ "description": "LoRA model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "LoRA",
+ "ui_model_base": ["qwen-image"],
+ "ui_model_type": ["lora"]
},
- "ui_type": {
+ "weight": {
+ "default": 1.0,
+ "description": "The weight at which the LoRA is applied to each model",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Weight",
+ "type": "number"
+ },
+ "transformer": {
"anyOf": [
{
- "$ref": "#/components/schemas/UIType"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
- "default": null
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Transformer"
+ },
+ "type": {
+ "const": "qwen_image_lora_loader",
+ "default": "qwen_image_lora_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "required": ["field_kind", "ui_hidden", "ui_order", "ui_type"],
- "title": "OutputFieldJSONSchemaExtra",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["lora", "model", "qwen_image"],
+ "title": "Apply LoRA - Qwen Image",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/QwenImageLoRALoaderOutput"
+ }
},
- "PBRMapsInvocation": {
- "category": "controlnet_preprocessors",
- "class": "invocation",
- "classification": "stable",
- "description": "Generate Normal, Displacement and Roughness Map from a given image",
- "node_pack": "invokeai",
+ "QwenImageLoRALoaderOutput": {
+ "class": "output",
+ "description": "Qwen Image LoRA Loader Output",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
+ "transformer": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
+ "description": "Transformer",
+ "field_kind": "output",
+ "title": "Transformer",
"ui_hidden": false
},
+ "type": {
+ "const": "qwen_image_lora_loader_output",
+ "default": "qwen_image_lora_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "transformer", "type", "type"],
+ "title": "QwenImageLoRALoaderOutput",
+ "type": "object"
+ },
+ "QwenImageModelLoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Loads a Qwen Image model, outputting its submodels.\n\nThe transformer is always loaded from the main model (Diffusers or GGUF).\n\nComponents can be mixed and matched:\n- VAE: standalone Qwen Image VAE checkpoint, the Component Source (Diffusers),\n or the main model if it's Diffusers.\n- Qwen VL Encoder: standalone Qwen2.5-VL encoder, the Component Source\n (Diffusers), or the main model if it's Diffusers.\n\nTogether, the standalone VAE and standalone encoder allow running a GGUF\ntransformer without ever downloading the full ~40 GB Diffusers pipeline.",
+ "node_pack": "invokeai",
+ "properties": {
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -59284,135 +64786,132 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Qwen Image Edit model (Transformer) to load",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Transformer",
+ "ui_model_base": ["qwen-image"],
+ "ui_model_type": ["main"]
+ },
+ "vae_model": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Input image",
+ "description": "Standalone Qwen Image VAE model. If not provided, VAE will be loaded from the Component Source (or from the main model if it is Diffusers).",
"field_kind": "input",
- "input": "any",
- "orig_required": true
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "VAE",
+ "ui_model_base": ["qwen-image"],
+ "ui_model_type": ["vae"]
},
- "tile_size": {
- "default": 512,
- "description": "Tile size",
+ "qwen_vl_encoder_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Standalone Qwen2.5-VL encoder model. If not provided, the encoder will be loaded from the Component Source (or from the main model if it is Diffusers).",
"field_kind": "input",
- "input": "any",
- "orig_default": 512,
+ "input": "direct",
+ "orig_default": null,
"orig_required": false,
- "title": "Tile Size",
- "type": "integer"
+ "title": "Qwen VL Encoder",
+ "ui_model_type": ["qwen_vl_encoder"]
},
- "border_mode": {
- "default": "none",
- "description": "Border mode to apply to eliminate any artifacts or seams",
- "enum": ["none", "seamless", "mirror", "replicate"],
+ "component_source": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Diffusers Qwen Image model to extract VAE and/or Qwen VL encoder from. Use this if you don't have separate VAE/encoder models. Ignored for any submodel that is provided separately.",
"field_kind": "input",
- "input": "any",
- "orig_default": "none",
+ "input": "direct",
+ "orig_default": null,
"orig_required": false,
- "title": "Border Mode",
- "type": "string"
+ "title": "Component Source (Diffusers)",
+ "ui_model_base": ["qwen-image"],
+ "ui_model_format": ["diffusers"],
+ "ui_model_type": ["main"]
},
"type": {
- "const": "pbr_maps",
- "default": "pbr_maps",
+ "const": "qwen_image_model_loader",
+ "default": "qwen_image_model_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["image", "material"],
- "title": "PBR Maps",
+ "required": ["model", "type", "id"],
+ "tags": ["model", "qwen_image"],
+ "title": "Main Model - Qwen Image",
"type": "object",
- "version": "1.0.0",
+ "version": "1.2.0",
"output": {
- "$ref": "#/components/schemas/PBRMapsOutput"
+ "$ref": "#/components/schemas/QwenImageModelLoaderOutput"
}
},
- "PBRMapsOutput": {
+ "QwenImageModelLoaderOutput": {
"class": "output",
+ "description": "Qwen Image model loader output.",
"properties": {
- "normal_map": {
- "$ref": "#/components/schemas/ImageField",
- "default": null,
- "description": "The generated normal map",
+ "transformer": {
+ "$ref": "#/components/schemas/TransformerField",
+ "description": "Transformer",
"field_kind": "output",
+ "title": "Transformer",
"ui_hidden": false
},
- "roughness_map": {
- "$ref": "#/components/schemas/ImageField",
- "default": null,
- "description": "The generated roughness map",
+ "qwen_vl_encoder": {
+ "$ref": "#/components/schemas/QwenVLEncoderField",
+ "description": "Qwen2.5-VL tokenizer, processor and text/vision encoder",
"field_kind": "output",
+ "title": "Qwen VL Encoder",
"ui_hidden": false
},
- "displacement_map": {
- "$ref": "#/components/schemas/ImageField",
- "default": null,
- "description": "The generated displacement map",
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
"field_kind": "output",
+ "title": "VAE",
"ui_hidden": false
},
"type": {
- "const": "pbr_maps-output",
- "default": "pbr_maps-output",
+ "const": "qwen_image_model_loader_output",
+ "default": "qwen_image_model_loader_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "normal_map", "roughness_map", "displacement_map", "type", "type"],
- "title": "PBRMapsOutput",
+ "required": ["output_meta", "transformer", "qwen_vl_encoder", "vae", "type", "type"],
+ "title": "QwenImageModelLoaderOutput",
"type": "object"
},
- "PaginatedResults_WorkflowRecordListItemWithThumbnailDTO_": {
- "properties": {
- "page": {
- "type": "integer",
- "title": "Page",
- "description": "Current Page"
- },
- "pages": {
- "type": "integer",
- "title": "Pages",
- "description": "Total number of pages"
- },
- "per_page": {
- "type": "integer",
- "title": "Per Page",
- "description": "Number of items per page"
- },
- "total": {
- "type": "integer",
- "title": "Total",
- "description": "Total number of items in result"
- },
- "items": {
- "items": {
- "$ref": "#/components/schemas/WorkflowRecordListItemWithThumbnailDTO"
- },
- "type": "array",
- "title": "Items",
- "description": "Items"
- }
- },
- "type": "object",
- "required": ["page", "pages", "per_page", "total", "items"],
- "title": "PaginatedResults[WorkflowRecordListItemWithThumbnailDTO]"
- },
- "PairTileImageInvocation": {
- "category": "tiles",
+ "QwenImageTextEncoderInvocation": {
+ "category": "conditioning",
"class": "invocation",
- "classification": "stable",
- "description": "Pair an image with its tile properties.",
+ "classification": "prototype",
+ "description": "Encodes text and reference images for Qwen Image using Qwen2.5-VL.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -59439,238 +64938,368 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "prompt": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "The tile image.",
+ "description": "Text prompt describing the desired edit.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt",
+ "ui_component": "textarea"
+ },
+ "reference_images": {
+ "default": [],
+ "description": "Reference images to guide the edit. The model can use multiple reference images.",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "items": {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ "orig_default": [],
+ "orig_required": false,
+ "title": "Reference Images",
+ "type": "array"
},
- "tile": {
+ "qwen_vl_encoder": {
"anyOf": [
{
- "$ref": "#/components/schemas/Tile"
+ "$ref": "#/components/schemas/QwenVLEncoderField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The tile properties.",
+ "description": "Qwen2.5-VL tokenizer, processor and text/vision encoder",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Qwen VL Encoder"
+ },
+ "quantization": {
+ "default": "none",
+ "description": "Quantize the Qwen VL encoder to reduce VRAM usage. 'nf4' (4-bit) saves the most memory, 'int8' (8-bit) is a middle ground.",
+ "enum": ["none", "int8", "nf4"],
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": "none",
+ "orig_required": false,
+ "title": "Quantization",
+ "type": "string"
},
"type": {
- "const": "pair_tile_image",
- "default": "pair_tile_image",
+ "const": "qwen_image_text_encoder",
+ "default": "qwen_image_text_encoder",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["tiles"],
- "title": "Pair Tile with Image",
+ "tags": ["prompt", "conditioning", "qwen_image"],
+ "title": "Prompt - Qwen Image",
"type": "object",
- "version": "1.0.1",
+ "version": "1.2.0",
"output": {
- "$ref": "#/components/schemas/PairTileImageOutput"
+ "$ref": "#/components/schemas/QwenImageConditioningOutput"
}
},
- "PairTileImageOutput": {
- "class": "output",
+ "QwenImageVariantType": {
+ "type": "string",
+ "enum": ["generate", "edit"],
+ "title": "QwenImageVariantType",
+ "description": "Qwen Image model variants."
+ },
+ "QwenVLEncoderField": {
+ "description": "Field for Qwen2.5-VL encoder used by Qwen Image Edit models.",
"properties": {
- "tile_with_image": {
- "$ref": "#/components/schemas/TileWithImage",
- "description": "A tile description with its corresponding image.",
- "field_kind": "output",
- "ui_hidden": false
+ "tokenizer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load tokenizer submodel"
},
- "type": {
- "const": "pair_tile_image_output",
- "default": "pair_tile_image_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "text_encoder": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load text_encoder submodel"
}
},
- "required": ["output_meta", "tile_with_image", "type", "type"],
- "title": "PairTileImageOutput",
+ "required": ["tokenizer", "text_encoder"],
+ "title": "QwenVLEncoderField",
"type": "object"
},
- "PasteImageIntoBoundingBoxInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Paste the source image into the target image at the given bounding box.\n\nThe source image must be the same size as the bounding box, and the bounding box must fit within the target image.",
- "node_pack": "invokeai",
+ "QwenVLEncoder_Checkpoint_Config": {
"properties": {
- "board": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
},
- "metadata": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "source_image": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to paste",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "target_image": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to paste into",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "bounding_box": {
+ "config_path": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoundingBoxField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The bounding box to paste the image into",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
"type": {
- "const": "paste_image_into_bounding_box",
- "default": "paste_image_into_bounding_box",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "qwen_vl_encoder",
+ "title": "Type",
+ "default": "qwen_vl_encoder"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
}
},
- "required": ["type", "id"],
- "tags": ["image", "crop"],
- "title": "Paste Image into Bounding Box",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "base",
+ "type",
+ "format"
+ ],
+ "title": "QwenVLEncoder_Checkpoint_Config",
+ "description": "Configuration for single-file Qwen2.5-VL encoder checkpoints (safetensors).\n\nThis matches ComfyUI-style consolidated single-file encoders such as\n`qwen_2.5_vl_7b_fp8_scaled.safetensors`, which bundle the language model\nand the visual tower into one file (typically with FP8 + per-tensor\n`weight_scale` ComfyUI quantization).\n\nThe matching tokenizer + processor are pulled from HuggingFace\n(`Qwen/Qwen2.5-VL-7B-Instruct`) on first use and cached for offline use."
},
- "PiDiNetEdgeDetectionInvocation": {
- "category": "controlnet_preprocessors",
- "class": "invocation",
- "classification": "stable",
- "description": "Generates an edge map using PiDiNet.",
- "node_pack": "invokeai",
+ "QwenVLEncoder_Diffusers_Config": {
"properties": {
- "board": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
},
- "metadata": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
+ },
+ "type": {
+ "type": "string",
+ "const": "qwen_vl_encoder",
+ "title": "Type",
+ "default": "qwen_vl_encoder"
},
+ "format": {
+ "type": "string",
+ "const": "qwen_vl_encoder",
+ "title": "Format",
+ "default": "qwen_vl_encoder"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "base",
+ "type",
+ "format"
+ ],
+ "title": "QwenVLEncoder_Diffusers_Config",
+ "description": "Configuration for standalone Qwen2.5-VL encoder models in diffusers-style folder layout.\n\nExpected structure:\n /\n text_encoder/\n config.json (with `_class_name` or `architectures` listing\n `Qwen2_5_VLForConditionalGeneration`)\n model.safetensors\n tokenizer/\n tokenizer_config.json\n ...\n processor/ (optional, for vision preprocessing)\n preprocessor_config.json\n\nThis lets users avoid downloading the full ~40 GB Qwen Image diffusers pipeline\nwhen they only need the Qwen2.5-VL encoder for use with a GGUF transformer."
+ },
+ "RandomFloatInvocation": {
+ "category": "math",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Outputs a single random float",
+ "node_pack": "invokeai",
+ "properties": {
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -59689,117 +65318,132 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": true,
+ "default": false,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
"type": "boolean"
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to process",
+ "low": {
+ "default": 0.0,
+ "description": "The inclusive low value",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Low",
+ "type": "number"
},
- "quantize_edges": {
- "default": false,
- "description": "Whether or not to use safe mode",
+ "high": {
+ "default": 1.0,
+ "description": "The exclusive high value",
"field_kind": "input",
"input": "any",
- "orig_default": false,
+ "orig_default": 1.0,
"orig_required": false,
- "title": "Quantize Edges",
- "type": "boolean"
+ "title": "High",
+ "type": "number"
},
- "scribble": {
- "default": false,
- "description": "Whether or not to use scribble mode",
+ "decimals": {
+ "default": 2,
+ "description": "The number of decimal places to round to",
"field_kind": "input",
"input": "any",
- "orig_default": false,
+ "orig_default": 2,
"orig_required": false,
- "title": "Scribble",
- "type": "boolean"
+ "title": "Decimals",
+ "type": "integer"
},
"type": {
- "const": "pidi_edge_detection",
- "default": "pidi_edge_detection",
+ "const": "rand_float",
+ "default": "rand_float",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["controlnet", "edge"],
- "title": "PiDiNet Edge Detection",
+ "tags": ["math", "float", "random"],
+ "title": "Random Float",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/FloatOutput"
}
},
- "PresetData": {
+ "RandomIntInvocation": {
+ "category": "math",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Outputs a single random integer.",
+ "node_pack": "invokeai",
"properties": {
- "positive_prompt": {
- "type": "string",
- "title": "Positive Prompt",
- "description": "Positive prompt"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "negative_prompt": {
- "type": "string",
- "title": "Negative Prompt",
- "description": "Negative prompt"
- }
- },
- "additionalProperties": false,
- "type": "object",
- "required": ["positive_prompt", "negative_prompt"],
- "title": "PresetData"
- },
- "PresetType": {
- "type": "string",
- "enum": ["user", "default"],
- "title": "PresetType"
- },
- "ProgressImage": {
- "description": "The progress image sent intermittently during processing",
- "properties": {
- "width": {
- "description": "The effective width of the image in pixels",
- "minimum": 1,
- "title": "Width",
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": false,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "low": {
+ "default": 0,
+ "description": "The inclusive low value",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Low",
"type": "integer"
},
- "height": {
- "description": "The effective height of the image in pixels",
- "minimum": 1,
- "title": "Height",
+ "high": {
+ "default": 2147483647,
+ "description": "The exclusive high value",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 2147483647,
+ "orig_required": false,
+ "title": "High",
"type": "integer"
},
- "dataURL": {
- "description": "The image data as a b64 data URL",
- "title": "Dataurl",
+ "type": {
+ "const": "rand_int",
+ "default": "rand_int",
+ "field_kind": "node_attribute",
+ "title": "type",
"type": "string"
}
},
- "required": ["width", "height", "dataURL"],
- "title": "ProgressImage",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["math", "random"],
+ "title": "Random Integer",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/IntegerOutput"
+ }
},
- "PromptTemplateInvocation": {
- "category": "prompt",
+ "RandomRangeInvocation": {
+ "category": "batch",
"class": "invocation",
"classification": "stable",
- "description": "Applies a Style Preset template to positive and negative prompts.\n\nSelect a Style Preset and provide positive/negative prompts. The node replaces\n{prompt} placeholders in the template with your input prompts.",
+ "description": "Creates a collection of random numbers",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -59820,101 +65464,76 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": true,
+ "default": false,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
"type": "boolean"
},
- "style_preset": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/StylePresetField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The Style Preset to use as a template",
+ "low": {
+ "default": 0,
+ "description": "The inclusive low value",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Low",
+ "type": "integer"
},
- "positive_prompt": {
- "default": "",
- "description": "The positive prompt to insert into the template's {prompt} placeholder",
+ "high": {
+ "default": 2147483647,
+ "description": "The exclusive high value",
"field_kind": "input",
"input": "any",
- "orig_default": "",
+ "orig_default": 2147483647,
"orig_required": false,
- "title": "Positive Prompt",
- "type": "string",
- "ui_component": "textarea"
+ "title": "High",
+ "type": "integer"
},
- "negative_prompt": {
- "default": "",
- "description": "The negative prompt to insert into the template's {prompt} placeholder",
+ "size": {
+ "default": 1,
+ "description": "The number of values to generate",
"field_kind": "input",
"input": "any",
- "orig_default": "",
+ "orig_default": 1,
"orig_required": false,
- "title": "Negative Prompt",
- "type": "string",
- "ui_component": "textarea"
+ "title": "Size",
+ "type": "integer"
+ },
+ "seed": {
+ "default": 0,
+ "description": "The seed for the RNG (omit for random)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 4294967295,
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
},
"type": {
- "const": "prompt_template",
- "default": "prompt_template",
+ "const": "random_range",
+ "default": "random_range",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["prompt", "template", "style", "preset"],
- "title": "Prompt Template",
+ "tags": ["range", "integer", "random", "collection"],
+ "title": "Random Range",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/PromptTemplateOutput"
+ "$ref": "#/components/schemas/IntegerCollectionOutput"
}
},
- "PromptTemplateOutput": {
- "class": "output",
- "description": "Output for the Prompt Template node",
- "properties": {
- "positive_prompt": {
- "description": "The positive prompt with the template applied",
- "field_kind": "output",
- "title": "Positive Prompt",
- "type": "string",
- "ui_hidden": false
- },
- "negative_prompt": {
- "description": "The negative prompt with the template applied",
- "field_kind": "output",
- "title": "Negative Prompt",
- "type": "string",
- "ui_hidden": false
- },
- "type": {
- "const": "prompt_template_output",
- "default": "prompt_template_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "positive_prompt", "negative_prompt", "type", "type"],
- "title": "PromptTemplateOutput",
- "type": "object"
- },
- "PromptsFromFileInvocation": {
- "category": "prompt",
+ "RangeInvocation": {
+ "category": "batch",
"class": "invocation",
"classification": "stable",
- "description": "Loads prompts from a text file",
+ "description": "Creates a range of numbers from start to stop with step",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -59941,446 +65560,135 @@
"title": "Use Cache",
"type": "boolean"
},
- "file_path": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Path to prompt text file",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "File Path"
- },
- "pre_prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "String to prepend to each prompt",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Pre Prompt",
- "ui_component": "textarea"
- },
- "post_prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "String to append to each prompt",
+ "start": {
+ "default": 0,
+ "description": "The start of the range",
"field_kind": "input",
"input": "any",
- "orig_default": null,
+ "orig_default": 0,
"orig_required": false,
- "title": "Post Prompt",
- "ui_component": "textarea"
+ "title": "Start",
+ "type": "integer"
},
- "start_line": {
- "default": 1,
- "description": "Line in the file to start start from",
+ "stop": {
+ "default": 10,
+ "description": "The stop of the range",
"field_kind": "input",
"input": "any",
- "minimum": 1,
- "orig_default": 1,
+ "orig_default": 10,
"orig_required": false,
- "title": "Start Line",
+ "title": "Stop",
"type": "integer"
},
- "max_prompts": {
+ "step": {
"default": 1,
- "description": "Max lines to read from file (0=all)",
+ "description": "The step of the range",
"field_kind": "input",
"input": "any",
- "minimum": 0,
"orig_default": 1,
"orig_required": false,
- "title": "Max Prompts",
+ "title": "Step",
"type": "integer"
},
"type": {
- "const": "prompt_from_file",
- "default": "prompt_from_file",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["prompt", "file"],
- "title": "Prompts from File",
- "type": "object",
- "version": "1.0.2",
- "output": {
- "$ref": "#/components/schemas/StringCollectionOutput"
- }
- },
- "PruneResult": {
- "properties": {
- "deleted": {
- "type": "integer",
- "title": "Deleted",
- "description": "Number of queue items deleted"
- }
- },
- "type": "object",
- "required": ["deleted"],
- "title": "PruneResult",
- "description": "Result of pruning the session queue"
- },
- "QueueClearedEvent": {
- "description": "Event model for queue_cleared",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "queue_id": {
- "description": "The ID of the queue",
- "title": "Queue Id",
- "type": "string"
- },
- "user_id": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The ID of the user whose queue items were cleared, or None if all users' items were cleared",
- "title": "User Id"
- }
- },
- "required": ["timestamp", "queue_id", "user_id"],
- "title": "QueueClearedEvent",
- "type": "object"
- },
- "QueueItemStatusChangedEvent": {
- "description": "Event model for queue_item_status_changed",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "queue_id": {
- "description": "The ID of the queue",
- "title": "Queue Id",
- "type": "string"
- },
- "item_id": {
- "description": "The ID of the queue item",
- "title": "Item Id",
- "type": "integer"
- },
- "batch_id": {
- "description": "The ID of the queue batch",
- "title": "Batch Id",
- "type": "string"
- },
- "origin": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The origin of the queue item",
- "title": "Origin"
- },
- "destination": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The destination of the queue item",
- "title": "Destination"
- },
- "user_id": {
- "default": "system",
- "description": "The ID of the user who created the queue item",
- "title": "User Id",
- "type": "string"
- },
- "status": {
- "description": "The new status of the queue item",
- "enum": ["pending", "in_progress", "waiting", "completed", "failed", "canceled"],
- "title": "Status",
- "type": "string"
- },
- "status_sequence": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "A monotonically increasing version for this queue item's visible status lifecycle",
- "title": "Status Sequence"
- },
- "error_type": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The error type, if any",
- "title": "Error Type"
- },
- "error_message": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The error message, if any",
- "title": "Error Message"
- },
- "error_traceback": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The error traceback, if any",
- "title": "Error Traceback"
- },
- "created_at": {
- "description": "The timestamp when the queue item was created",
- "title": "Created At",
- "type": "string"
- },
- "updated_at": {
- "description": "The timestamp when the queue item was last updated",
- "title": "Updated At",
- "type": "string"
- },
- "started_at": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The timestamp when the queue item was started",
- "title": "Started At"
- },
- "completed_at": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The timestamp when the queue item was completed",
- "title": "Completed At"
- },
- "batch_status": {
- "$ref": "#/components/schemas/BatchStatus",
- "description": "The status of the batch"
- },
- "queue_status": {
- "$ref": "#/components/schemas/SessionQueueStatus",
- "description": "The status of the queue"
- },
- "session_id": {
- "description": "The ID of the session (aka graph execution state)",
- "title": "Session Id",
- "type": "string"
- }
- },
- "required": [
- "timestamp",
- "queue_id",
- "item_id",
- "batch_id",
- "origin",
- "destination",
- "user_id",
- "status",
- "status_sequence",
- "error_type",
- "error_message",
- "error_traceback",
- "created_at",
- "updated_at",
- "started_at",
- "completed_at",
- "batch_status",
- "queue_status",
- "session_id"
- ],
- "title": "QueueItemStatusChangedEvent",
- "type": "object"
- },
- "QueueItemsRetriedEvent": {
- "description": "Event model for queue_items_retried",
- "properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "queue_id": {
- "description": "The ID of the queue",
- "title": "Queue Id",
- "type": "string"
- },
- "retried_item_ids": {
- "description": "The IDs of the queue items that were retried",
- "items": {
- "type": "integer"
- },
- "title": "Retried Item Ids",
- "type": "array"
- },
- "user_ids": {
- "description": "The IDs of the users who own the retried root queue items",
- "items": {
- "type": "string"
- },
- "title": "User Ids",
- "type": "array"
- },
- "retried_item_ids_by_user": {
- "additionalProperties": {
- "items": {
- "type": "integer"
- },
- "type": "array"
- },
- "description": "The retried root queue item IDs keyed by owner user ID.",
- "title": "Retried Item Ids By User",
- "type": "object"
- }
- },
- "required": ["timestamp", "queue_id", "retried_item_ids", "user_ids", "retried_item_ids_by_user"],
- "title": "QueueItemsRetriedEvent",
- "type": "object"
- },
- "Qwen3EncoderField": {
- "description": "Field for Qwen3 text encoder used by Z-Image models.",
- "properties": {
- "tokenizer": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load tokenizer submodel"
- },
- "text_encoder": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load text_encoder submodel"
- },
- "loras": {
- "description": "LoRAs to apply on model loading",
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "title": "Loras",
- "type": "array"
+ "const": "range",
+ "default": "range",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "required": ["tokenizer", "text_encoder"],
- "title": "Qwen3EncoderField",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["collection", "integer", "range"],
+ "title": "Integer Range",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/IntegerCollectionOutput"
+ }
},
- "Qwen3Encoder_Checkpoint_Config": {
+ "RangeOfSizeInvocation": {
+ "category": "batch",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Creates a range from start to start + (size * step) incremented by step",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "start": {
+ "default": 0,
+ "description": "The start of the range",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Start",
+ "type": "integer"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "size": {
+ "default": 1,
+ "description": "The number of values",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Size",
+ "type": "integer"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "step": {
+ "default": 1,
+ "description": "The step of the range",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Step",
+ "type": "integer"
},
- "source_api_response": {
+ "type": {
+ "const": "range_of_size",
+ "default": "range_of_size",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["collection", "integer", "size", "range"],
+ "title": "Integer Range of Size",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/IntegerCollectionOutput"
+ }
+ },
+ "RecallParameter": {
+ "properties": {
+ "positive_prompt": {
"anyOf": [
{
"type": "string"
@@ -60389,10 +65697,10 @@
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "title": "Positive Prompt",
+ "description": "Positive prompt text"
},
- "source_url": {
+ "negative_prompt": {
"anyOf": [
{
"type": "string"
@@ -60401,10 +65709,10 @@
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "title": "Negative Prompt",
+ "description": "Negative prompt text"
},
- "cover_image": {
+ "model": {
"anyOf": [
{
"type": "string"
@@ -60413,10 +65721,10 @@
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "title": "Model",
+ "description": "Main model name/identifier"
},
- "config_path": {
+ "refiner_model": {
"anyOf": [
{
"type": "string"
@@ -60425,95 +65733,22 @@
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
- },
- "type": {
- "type": "string",
- "const": "qwen3_encoder",
- "title": "Type",
- "default": "qwen3_encoder"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "title": "Refiner Model",
+ "description": "Refiner model name/identifier"
},
- "cpu_only": {
+ "vae_model": {
"anyOf": [
{
- "type": "boolean"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
- },
- "variant": {
- "$ref": "#/components/schemas/Qwen3VariantType",
- "description": "Qwen3 model size variant (4B or 8B)"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "base",
- "type",
- "format",
- "cpu_only",
- "variant"
- ],
- "title": "Qwen3Encoder_Checkpoint_Config",
- "description": "Configuration for single-file Qwen3 Encoder models (safetensors)."
- },
- "Qwen3Encoder_GGUF_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "title": "Vae Model",
+ "description": "VAE model name/identifier"
},
- "description": {
+ "scheduler": {
"anyOf": [
{
"type": "string"
@@ -60522,608 +65757,313 @@
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "title": "Scheduler",
+ "description": "Scheduler name"
},
- "source_api_response": {
+ "steps": {
"anyOf": [
{
- "type": "string"
+ "type": "integer",
+ "minimum": 1.0
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "title": "Steps",
+ "description": "Number of generation steps"
},
- "source_url": {
+ "refiner_steps": {
"anyOf": [
{
- "type": "string"
+ "type": "integer",
+ "minimum": 0.0
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "title": "Refiner Steps",
+ "description": "Number of refiner steps"
},
- "cover_image": {
+ "cfg_scale": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "title": "Cfg Scale",
+ "description": "CFG scale for guidance"
},
- "config_path": {
+ "cfg_rescale_multiplier": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
- },
- "type": {
- "type": "string",
- "const": "qwen3_encoder",
- "title": "Type",
- "default": "qwen3_encoder"
- },
- "format": {
- "type": "string",
- "const": "gguf_quantized",
- "title": "Format",
- "default": "gguf_quantized"
+ "title": "Cfg Rescale Multiplier",
+ "description": "CFG rescale multiplier"
},
- "cpu_only": {
+ "refiner_cfg_scale": {
"anyOf": [
{
- "type": "boolean"
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
- },
- "variant": {
- "$ref": "#/components/schemas/Qwen3VariantType",
- "description": "Qwen3 model size variant (4B or 8B)"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "base",
- "type",
- "format",
- "cpu_only",
- "variant"
- ],
- "title": "Qwen3Encoder_GGUF_Config",
- "description": "Configuration for GGUF-quantized Qwen3 Encoder models."
- },
- "Qwen3Encoder_Qwen3Encoder_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "title": "Refiner Cfg Scale",
+ "description": "Refiner CFG scale"
},
- "description": {
+ "guidance": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "title": "Guidance",
+ "description": "Guidance scale"
},
- "source_api_response": {
+ "width": {
"anyOf": [
{
- "type": "string"
+ "type": "integer",
+ "minimum": 64.0
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "title": "Width",
+ "description": "Image width in pixels"
},
- "source_url": {
+ "height": {
"anyOf": [
{
- "type": "string"
+ "type": "integer",
+ "minimum": 64.0
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "title": "Height",
+ "description": "Image height in pixels"
},
- "cover_image": {
+ "seed": {
"anyOf": [
{
- "type": "string"
+ "type": "integer",
+ "minimum": 0.0
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
- },
- "type": {
- "type": "string",
- "const": "qwen3_encoder",
- "title": "Type",
- "default": "qwen3_encoder"
- },
- "format": {
- "type": "string",
- "const": "qwen3_encoder",
- "title": "Format",
- "default": "qwen3_encoder"
+ "title": "Seed",
+ "description": "Random seed"
},
- "cpu_only": {
+ "denoise_strength": {
"anyOf": [
{
- "type": "boolean"
+ "type": "number",
+ "maximum": 1.0,
+ "minimum": 0.0
},
{
"type": "null"
}
],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
- },
- "variant": {
- "$ref": "#/components/schemas/Qwen3VariantType",
- "description": "Qwen3 model size variant (4B or 8B)"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "base",
- "type",
- "format",
- "cpu_only",
- "variant"
- ],
- "title": "Qwen3Encoder_Qwen3Encoder_Config",
- "description": "Configuration for Qwen3 Encoder models in a diffusers-like format.\n\nThe model weights are expected to be in a folder called text_encoder inside the model directory,\ncompatible with Qwen2VLForConditionalGeneration or similar architectures used by Z-Image."
- },
- "Qwen3VariantType": {
- "type": "string",
- "enum": ["qwen3_4b", "qwen3_8b", "qwen3_06b"],
- "title": "Qwen3VariantType",
- "description": "Qwen3 text encoder variants based on model size."
- },
- "QwenImageConditioningField": {
- "description": "A Qwen Image Edit conditioning tensor primitive value",
- "properties": {
- "conditioning_name": {
- "description": "The name of conditioning tensor",
- "title": "Conditioning Name",
- "type": "string"
- }
- },
- "required": ["conditioning_name"],
- "title": "QwenImageConditioningField",
- "type": "object"
- },
- "QwenImageConditioningOutput": {
- "class": "output",
- "description": "Base class for nodes that output a Qwen Image Edit conditioning tensor.",
- "properties": {
- "conditioning": {
- "$ref": "#/components/schemas/QwenImageConditioningField",
- "description": "Conditioning tensor",
- "field_kind": "output",
- "ui_hidden": false
- },
- "type": {
- "const": "qwen_image_conditioning_output",
- "default": "qwen_image_conditioning_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "conditioning", "type", "type"],
- "title": "QwenImageConditioningOutput",
- "type": "object"
- },
- "QwenImageDenoiseInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "prototype",
- "description": "Run the denoising process with a Qwen Image model.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "title": "Denoise Strength",
+ "description": "Denoising strength"
+ },
+ "refiner_denoise_start": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "number",
+ "maximum": 1.0,
+ "minimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Refiner Denoise Start",
+ "description": "Refiner denoising start"
},
- "metadata": {
+ "clip_skip": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "integer",
+ "minimum": 0.0
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Clip Skip",
+ "description": "CLIP skip layers"
},
- "latents": {
+ "seamless_x": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Seamless X",
+ "description": "Enable seamless X tiling"
},
- "reference_latents": {
+ "seamless_y": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Reference image latents to guide generation. Encoded through the VAE.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Seamless Y",
+ "description": "Enable seamless Y tiling"
},
- "denoise_mask": {
+ "refiner_positive_aesthetic_score": {
"anyOf": [
{
- "$ref": "#/components/schemas/DenoiseMaskField"
+ "type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
- },
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
- },
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
+ "title": "Refiner Positive Aesthetic Score",
+ "description": "Refiner positive aesthetic score"
},
- "transformer": {
+ "refiner_negative_aesthetic_score": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "type": "number"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Qwen Image Edit model (Transformer) to load",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Transformer"
+ "title": "Refiner Negative Aesthetic Score",
+ "description": "Refiner negative aesthetic score"
},
- "positive_conditioning": {
+ "loras": {
"anyOf": [
{
- "$ref": "#/components/schemas/QwenImageConditioningField"
+ "items": {
+ "$ref": "#/components/schemas/LoRARecallParameter"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Loras",
+ "description": "List of LoRAs with their weights"
},
- "negative_conditioning": {
+ "control_layers": {
"anyOf": [
{
- "$ref": "#/components/schemas/QwenImageConditioningField"
+ "items": {
+ "$ref": "#/components/schemas/ControlNetRecallParameter"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Negative conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "title": "Control Layers",
+ "description": "List of control adapters (ControlNet, T2I Adapter, Control LoRA) with their settings"
},
- "cfg_scale": {
+ "ip_adapters": {
"anyOf": [
- {
- "type": "number"
- },
{
"items": {
- "type": "number"
+ "$ref": "#/components/schemas/IPAdapterRecallParameter"
},
"type": "array"
+ },
+ {
+ "type": "null"
}
],
- "default": 4.0,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 4.0,
- "orig_required": false,
- "title": "CFG Scale"
- },
- "width": {
- "default": 1024,
- "description": "Width of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
- },
- "steps": {
- "default": 40,
- "description": "Number of steps to run",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 40,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
- },
- "seed": {
- "default": 0,
- "description": "Randomness seed for reproducibility.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "title": "Ip Adapters",
+ "description": "List of IP Adapters with their settings"
},
- "shift": {
+ "reference_images": {
"anyOf": [
{
- "type": "number"
+ "items": {
+ "$ref": "#/components/schemas/ReferenceImageRecallParameter"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Override the sigma schedule shift. When set, uses a fixed shift (e.g. 3.0 for Lightning LoRAs) instead of the default dynamic shifting. Leave unset for the base model's default schedule.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Shift"
+ "title": "Reference Images",
+ "description": "List of model-free reference images for architectures that consume reference images directly (FLUX.2 Klein, FLUX Kontext, Qwen Image Edit). The frontend picks the correct config type based on the currently-selected main model."
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "title": "RecallParameter",
+ "description": "Request model for updating recallable parameters."
+ },
+ "RecallParametersUpdatedEvent": {
+ "description": "Event model for recall_parameters_updated",
+ "properties": {
+ "timestamp": {
+ "description": "The timestamp of the event",
+ "title": "Timestamp",
+ "type": "integer"
},
- "type": {
- "const": "qwen_image_denoise",
- "default": "qwen_image_denoise",
- "field_kind": "node_attribute",
- "title": "type",
+ "queue_id": {
+ "description": "The ID of the queue",
+ "title": "Queue Id",
+ "type": "string"
+ },
+ "user_id": {
+ "description": "The ID of the user whose recall parameters were updated",
+ "title": "User Id",
"type": "string"
+ },
+ "parameters": {
+ "additionalProperties": true,
+ "description": "The recall parameters that were updated",
+ "title": "Parameters",
+ "type": "object"
}
},
- "required": ["type", "id"],
- "tags": ["image", "qwen_image"],
- "title": "Denoise - Qwen Image",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
+ "required": ["timestamp", "queue_id", "user_id", "parameters"],
+ "title": "RecallParametersUpdatedEvent",
+ "type": "object"
},
- "QwenImageImageToLatentsInvocation": {
- "category": "image",
+ "RectangleMaskInvocation": {
+ "category": "mask",
"class": "invocation",
- "classification": "prototype",
- "description": "Generates latents from an image using the Qwen Image VAE.",
+ "classification": "stable",
+ "description": "Create a rectangular mask.",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
"metadata": {
"anyOf": [
{
@@ -61164,37 +66104,39 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "width": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to encode.",
+ "description": "The width of the entire mask.",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_required": true,
+ "title": "Width"
},
- "vae": {
+ "height": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "VAE",
+ "description": "The height of the entire mask.",
"field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "input": "any",
+ "orig_required": true,
+ "title": "Height"
},
- "width": {
+ "x_left": {
"anyOf": [
{
"type": "integer"
@@ -61204,14 +66146,13 @@
}
],
"default": null,
- "description": "Resize the image to this width before encoding. If not set, encodes at the image's original size.",
+ "description": "The left x-coordinate of the rectangular masked region (inclusive).",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Width"
+ "orig_required": true,
+ "title": "X Left"
},
- "height": {
+ "y_top": {
"anyOf": [
{
"type": "integer"
@@ -61221,145 +66162,171 @@
}
],
"default": null,
- "description": "Resize the image to this height before encoding. If not set, encodes at the image's original size.",
+ "description": "The top y-coordinate of the rectangular masked region (inclusive).",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Height"
+ "orig_required": true,
+ "title": "Y Top"
},
- "type": {
- "const": "qwen_image_i2l",
- "default": "qwen_image_i2l",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "latents", "vae", "i2l", "qwen_image"],
- "title": "Image to Latents - Qwen Image",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
- },
- "QwenImageLatentsToImageInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "prototype",
- "description": "Generates an image from latents using the Qwen Image VAE.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "rectangle_width": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "description": "The width of the rectangular masked region.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Rectangle Width"
},
- "metadata": {
+ "rectangle_height": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "description": "The height of the rectangular masked region.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Rectangle Height"
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "type": {
+ "const": "rectangle_mask",
+ "default": "rectangle_mask",
"field_kind": "node_attribute",
- "title": "Id",
+ "title": "type",
"type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["conditioning"],
+ "title": "Create Rectangle Mask",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/MaskOutput"
+ }
+ },
+ "ReferenceImageRecallParameter": {
+ "properties": {
+ "image_name": {
+ "type": "string",
+ "title": "Image Name",
+ "description": "The filename of the reference image in outputs/images"
+ }
+ },
+ "type": "object",
+ "required": ["image_name"],
+ "title": "ReferenceImageRecallParameter",
+ "description": "Global reference-image configuration for recall.\n\nUsed for reference images that feed directly into the main model rather\nthan through a separate IP-Adapter / ControlNet model \u2014 for example\nFLUX.2 Klein, FLUX Kontext, and Qwen Image Edit. The receiving frontend\npicks the correct config type (``flux2_reference_image`` /\n``qwen_image_reference_image`` / ``flux_kontext_reference_image``) based\non the currently-selected main model."
+ },
+ "RemoteModelFile": {
+ "properties": {
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "format": "uri",
+ "title": "Url",
+ "description": "The url to download this model file"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "path": {
+ "type": "string",
+ "format": "path",
+ "title": "Path",
+ "description": "The path to the file, relative to the model root"
},
- "latents": {
+ "size": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Size",
+ "description": "The size of this file, in bytes",
+ "default": 0
},
- "vae": {
+ "sha256": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Sha256",
+ "description": "SHA256 hash of this model (not always available)"
+ }
+ },
+ "type": "object",
+ "required": ["url", "path"],
+ "title": "RemoteModelFile",
+ "description": "Information about a downloadable file that forms part of a model."
+ },
+ "RemoveImagesFromBoardResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the delete operation"
},
- "type": {
- "const": "qwen_image_l2i",
- "default": "qwen_image_l2i",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "removed_images": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Removed Images",
+ "description": "The image names that were removed from their board"
+ }
+ },
+ "type": "object",
+ "required": ["affected_boards", "removed_images"],
+ "title": "RemoveImagesFromBoardResult"
+ },
+ "RemoveVideosFromBoardResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the operation"
+ },
+ "removed_videos": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Removed Videos",
+ "description": "The video names that were removed from their board"
}
},
- "required": ["type", "id"],
- "tags": ["latents", "image", "vae", "l2i", "qwen_image"],
- "title": "Latents to Image - Qwen Image",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": ["affected_boards", "removed_videos"],
+ "title": "RemoveVideosFromBoardResult"
},
- "QwenImageLoRACollectionLoader": {
- "category": "model",
+ "ResizeLatentsInvocation": {
+ "category": "latents",
"class": "invocation",
- "classification": "prototype",
- "description": "Applies a collection of LoRAs to a Qwen Image transformer.",
+ "classification": "stable",
+ "description": "Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -61386,70 +66353,126 @@
"title": "Use Cache",
"type": "boolean"
},
- "loras": {
+ "latents": {
"anyOf": [
{
- "$ref": "#/components/schemas/LoRAField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "type": "array"
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "width": {
+ "anyOf": [
+ {
+ "minimum": 64,
+ "multipleOf": 8,
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "LoRA models and weights. May be a single LoRA or collection.",
+ "description": "Width of output (px)",
"field_kind": "input",
"input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "LoRAs",
- "ui_model_base": ["qwen-image"],
- "ui_model_type": ["lora"]
+ "orig_required": true,
+ "title": "Width"
},
- "transformer": {
+ "height": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "minimum": 64,
+ "multipleOf": 8,
+ "type": "integer"
},
{
"type": "null"
}
],
"default": null,
- "description": "Transformer",
+ "description": "Width of output (px)",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
+ "input": "any",
+ "orig_required": true,
+ "title": "Height"
+ },
+ "mode": {
+ "default": "bilinear",
+ "description": "Interpolation mode",
+ "enum": ["nearest", "linear", "bilinear", "bicubic", "trilinear", "area", "nearest-exact"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "bilinear",
"orig_required": false,
- "title": "Transformer"
+ "title": "Mode",
+ "type": "string"
+ },
+ "antialias": {
+ "default": false,
+ "description": "Whether or not to apply antialiasing (bilinear or bicubic only)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Antialias",
+ "type": "boolean"
},
"type": {
- "const": "qwen_image_lora_collection_loader",
- "default": "qwen_image_lora_collection_loader",
+ "const": "lresize",
+ "default": "lresize",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["lora", "model", "qwen_image"],
- "title": "Apply LoRA Collection - Qwen Image",
+ "tags": ["latents", "resize"],
+ "title": "Resize Latents",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.2",
"output": {
- "$ref": "#/components/schemas/QwenImageLoRALoaderOutput"
+ "$ref": "#/components/schemas/LatentsOutput"
}
},
- "QwenImageLoRALoaderInvocation": {
- "category": "model",
+ "ResourceOrigin": {
+ "type": "string",
+ "enum": ["internal", "external"],
+ "title": "ResourceOrigin",
+ "description": "The origin of a resource (eg image).\n\n- INTERNAL: The resource was created by the application.\n- EXTERNAL: The resource was not created by the application.\nThis may be a user-initiated upload, or an internal application upload (eg Canvas init image)."
+ },
+ "RetryItemsResult": {
+ "properties": {
+ "queue_id": {
+ "type": "string",
+ "title": "Queue Id",
+ "description": "The ID of the queue"
+ },
+ "retried_item_ids": {
+ "items": {
+ "type": "integer"
+ },
+ "type": "array",
+ "title": "Retried Item Ids",
+ "description": "The IDs of the queue items that were retried"
+ }
+ },
+ "type": "object",
+ "required": ["queue_id", "retried_item_ids"],
+ "title": "RetryItemsResult"
+ },
+ "RoundInvocation": {
+ "category": "math",
"class": "invocation",
- "classification": "prototype",
- "description": "Apply a LoRA model to a Qwen Image transformer.",
+ "classification": "stable",
+ "description": "Rounds a float to a specified number of decimal places.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -61476,106 +66499,159 @@
"title": "Use Cache",
"type": "boolean"
},
- "lora": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "LoRA model to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "LoRA",
- "ui_model_base": ["qwen-image"],
- "ui_model_type": ["lora"]
- },
- "weight": {
- "default": 1.0,
- "description": "The weight at which the LoRA is applied to each model",
+ "value": {
+ "default": 0,
+ "description": "The float value",
"field_kind": "input",
"input": "any",
- "orig_default": 1.0,
+ "orig_default": 0,
"orig_required": false,
- "title": "Weight",
+ "title": "Value",
"type": "number"
},
- "transformer": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TransformerField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Transformer",
+ "decimals": {
+ "default": 0,
+ "description": "The number of decimal places",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
+ "input": "any",
+ "orig_default": 0,
"orig_required": false,
- "title": "Transformer"
+ "title": "Decimals",
+ "type": "integer"
},
"type": {
- "const": "qwen_image_lora_loader",
- "default": "qwen_image_lora_loader",
+ "const": "round_float",
+ "default": "round_float",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["lora", "model", "qwen_image"],
- "title": "Apply LoRA - Qwen Image",
+ "tags": ["math", "round"],
+ "title": "Round Float",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/QwenImageLoRALoaderOutput"
+ "$ref": "#/components/schemas/FloatOutput"
}
},
- "QwenImageLoRALoaderOutput": {
+ "SAMPoint": {
+ "properties": {
+ "x": {
+ "description": "The x-coordinate of the point",
+ "title": "X",
+ "type": "integer"
+ },
+ "y": {
+ "description": "The y-coordinate of the point",
+ "title": "Y",
+ "type": "integer"
+ },
+ "label": {
+ "$ref": "#/components/schemas/SAMPointLabel",
+ "description": "The label of the point"
+ }
+ },
+ "required": ["x", "y", "label"],
+ "title": "SAMPoint",
+ "type": "object"
+ },
+ "SAMPointLabel": {
+ "enum": [-1, 0, 1],
+ "title": "SAMPointLabel",
+ "type": "integer"
+ },
+ "SAMPointsField": {
+ "properties": {
+ "points": {
+ "description": "The points of the object",
+ "items": {
+ "$ref": "#/components/schemas/SAMPoint"
+ },
+ "minItems": 1,
+ "title": "Points",
+ "type": "array"
+ }
+ },
+ "required": ["points"],
+ "title": "SAMPointsField",
+ "type": "object"
+ },
+ "SD3ConditioningField": {
+ "description": "A conditioning tensor primitive value",
+ "properties": {
+ "conditioning_name": {
+ "description": "The name of conditioning tensor",
+ "title": "Conditioning Name",
+ "type": "string"
+ }
+ },
+ "required": ["conditioning_name"],
+ "title": "SD3ConditioningField",
+ "type": "object"
+ },
+ "SD3ConditioningOutput": {
"class": "output",
- "description": "Qwen Image LoRA Loader Output",
+ "description": "Base class for nodes that output a single SD3 conditioning tensor",
"properties": {
- "transformer": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/TransformerField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Transformer",
+ "conditioning": {
+ "$ref": "#/components/schemas/SD3ConditioningField",
+ "description": "Conditioning tensor",
"field_kind": "output",
- "title": "Transformer",
"ui_hidden": false
},
"type": {
- "const": "qwen_image_lora_loader_output",
- "default": "qwen_image_lora_loader_output",
+ "const": "sd3_conditioning_output",
+ "default": "sd3_conditioning_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "transformer", "type", "type"],
- "title": "QwenImageLoRALoaderOutput",
+ "required": ["output_meta", "conditioning", "type", "type"],
+ "title": "SD3ConditioningOutput",
"type": "object"
},
- "QwenImageModelLoaderInvocation": {
- "category": "model",
+ "SD3DenoiseInvocation": {
+ "category": "latents",
"class": "invocation",
- "classification": "prototype",
- "description": "Loads a Qwen Image model, outputting its submodels.\n\nThe transformer is always loaded from the main model (Diffusers or GGUF).\n\nComponents can be mixed and matched:\n- VAE: standalone Qwen Image VAE checkpoint, the Component Source (Diffusers),\n or the main model if it's Diffusers.\n- Qwen VL Encoder: standalone Qwen2.5-VL encoder, the Component Source\n (Diffusers), or the main model if it's Diffusers.\n\nTogether, the standalone VAE and standalone encoder allow running a GGUF\ntransformer without ever downloading the full ~40 GB Diffusers pipeline.",
+ "classification": "stable",
+ "description": "Run denoising process with a SD3 model.",
"node_pack": "invokeai",
"properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -61600,134 +66676,243 @@
"title": "Use Cache",
"type": "boolean"
},
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Qwen Image Edit model (Transformer) to load",
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
"field_kind": "input",
- "input": "direct",
- "orig_required": true,
- "title": "Transformer",
- "ui_model_base": ["qwen-image"],
- "ui_model_type": ["main"]
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "vae_model": {
+ "noise": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Standalone Qwen Image VAE model. If not provided, VAE will be loaded from the Component Source (or from the main model if it is Diffusers).",
+ "description": "Noise tensor",
"field_kind": "input",
- "input": "direct",
+ "input": "connection",
"orig_default": null,
- "orig_required": false,
- "title": "VAE",
- "ui_model_base": ["qwen-image"],
- "ui_model_type": ["vae"]
+ "orig_required": false
},
- "qwen_vl_encoder_model": {
+ "denoise_mask": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/DenoiseMaskField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Standalone Qwen2.5-VL encoder model. If not provided, the encoder will be loaded from the Component Source (or from the main model if it is Diffusers).",
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
"field_kind": "input",
- "input": "direct",
+ "input": "connection",
"orig_default": null,
+ "orig_required": false
+ },
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
"orig_required": false,
- "title": "Qwen VL Encoder",
- "ui_model_type": ["qwen_vl_encoder"]
+ "title": "Denoising Start",
+ "type": "number"
},
- "component_source": {
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
+ },
+ "transformer": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/TransformerField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Diffusers Qwen Image model to extract VAE and/or Qwen VL encoder from. Use this if you don't have separate VAE/encoder models. Ignored for any submodel that is provided separately.",
+ "description": "SD3 model (MMDiTX) to load",
"field_kind": "input",
- "input": "direct",
- "orig_default": null,
+ "input": "connection",
+ "orig_required": true,
+ "title": "Transformer"
+ },
+ "positive_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/SD3ConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "negative_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/SD3ConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Negative conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
+ },
+ "cfg_scale": {
+ "anyOf": [
+ {
+ "type": "number"
+ },
+ {
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
+ }
+ ],
+ "default": 3.5,
+ "description": "Classifier-Free Guidance scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 3.5,
"orig_required": false,
- "title": "Component Source (Diffusers)",
- "ui_model_base": ["qwen-image"],
- "ui_model_format": ["diffusers"],
- "ui_model_type": ["main"]
+ "title": "CFG Scale"
},
- "type": {
- "const": "qwen_image_model_loader",
- "default": "qwen_image_model_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["model", "type", "id"],
- "tags": ["model", "qwen_image"],
- "title": "Main Model - Qwen Image",
- "type": "object",
- "version": "1.2.0",
- "output": {
- "$ref": "#/components/schemas/QwenImageModelLoaderOutput"
- }
- },
- "QwenImageModelLoaderOutput": {
- "class": "output",
- "description": "Qwen Image model loader output.",
- "properties": {
- "transformer": {
- "$ref": "#/components/schemas/TransformerField",
- "description": "Transformer",
- "field_kind": "output",
- "title": "Transformer",
- "ui_hidden": false
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
},
- "qwen_vl_encoder": {
- "$ref": "#/components/schemas/QwenVLEncoderField",
- "description": "Qwen2.5-VL tokenizer, processor and text/vision encoder",
- "field_kind": "output",
- "title": "Qwen VL Encoder",
- "ui_hidden": false
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "steps": {
+ "default": 10,
+ "description": "Number of steps to run",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 10,
+ "orig_required": false,
+ "title": "Steps",
+ "type": "integer"
},
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
},
"type": {
- "const": "qwen_image_model_loader_output",
- "default": "qwen_image_model_loader_output",
+ "const": "sd3_denoise",
+ "default": "sd3_denoise",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "transformer", "qwen_vl_encoder", "vae", "type", "type"],
- "title": "QwenImageModelLoaderOutput",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["image", "sd3"],
+ "title": "Denoise - SD3",
+ "type": "object",
+ "version": "1.2.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
- "QwenImageTextEncoderInvocation": {
- "category": "conditioning",
+ "SD3ImageToLatentsInvocation": {
+ "category": "latents",
"class": "invocation",
- "classification": "prototype",
- "description": "Encodes text and reference images for Qwen Image using Qwen2.5-VL.",
+ "classification": "stable",
+ "description": "Generates latents from an image.",
"node_pack": "invokeai",
"properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -61752,366 +66937,346 @@
"title": "Use Cache",
"type": "boolean"
},
- "prompt": {
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Text prompt describing the desired edit.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Prompt",
- "ui_component": "textarea"
- },
- "reference_images": {
- "default": [],
- "description": "Reference images to guide the edit. The model can use multiple reference images.",
+ "description": "The image to encode",
"field_kind": "input",
"input": "any",
- "items": {
- "$ref": "#/components/schemas/ImageField"
- },
- "orig_default": [],
- "orig_required": false,
- "title": "Reference Images",
- "type": "array"
+ "orig_required": true
},
- "qwen_vl_encoder": {
+ "vae": {
"anyOf": [
{
- "$ref": "#/components/schemas/QwenVLEncoderField"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Qwen2.5-VL tokenizer, processor and text/vision encoder",
+ "description": "VAE",
"field_kind": "input",
"input": "connection",
- "orig_required": true,
- "title": "Qwen VL Encoder"
- },
- "quantization": {
- "default": "none",
- "description": "Quantize the Qwen VL encoder to reduce VRAM usage. 'nf4' (4-bit) saves the most memory, 'int8' (8-bit) is a middle ground.",
- "enum": ["none", "int8", "nf4"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "none",
- "orig_required": false,
- "title": "Quantization",
- "type": "string"
+ "orig_required": true
},
"type": {
- "const": "qwen_image_text_encoder",
- "default": "qwen_image_text_encoder",
+ "const": "sd3_i2l",
+ "default": "sd3_i2l",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["prompt", "conditioning", "qwen_image"],
- "title": "Prompt - Qwen Image",
+ "tags": ["image", "latents", "vae", "i2l", "sd3"],
+ "title": "Image to Latents - SD3",
"type": "object",
- "version": "1.2.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/QwenImageConditioningOutput"
+ "$ref": "#/components/schemas/LatentsOutput"
}
},
- "QwenImageVariantType": {
- "type": "string",
- "enum": ["generate", "edit"],
- "title": "QwenImageVariantType",
- "description": "Qwen Image model variants."
- },
- "QwenVLEncoderField": {
- "description": "Field for Qwen2.5-VL encoder used by Qwen Image Edit models.",
- "properties": {
- "tokenizer": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load tokenizer submodel"
- },
- "text_encoder": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load text_encoder submodel"
- }
- },
- "required": ["tokenizer", "text_encoder"],
- "title": "QwenVLEncoderField",
- "type": "object"
- },
- "QwenVLEncoder_Checkpoint_Config": {
+ "SD3LatentsToImageInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Generates an image from latents.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "cover_image": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "config_path": {
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
"type": {
- "type": "string",
- "const": "qwen_vl_encoder",
- "title": "Type",
- "default": "qwen_vl_encoder"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "const": "sd3_l2i",
+ "default": "sd3_l2i",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "l2i", "sd3"],
+ "title": "Latents to Image - SD3",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "base",
- "type",
- "format"
- ],
- "title": "QwenVLEncoder_Checkpoint_Config",
- "description": "Configuration for single-file Qwen2.5-VL encoder checkpoints (safetensors).\n\nThis matches ComfyUI-style consolidated single-file encoders such as\n`qwen_2.5_vl_7b_fp8_scaled.safetensors`, which bundle the language model\nand the visual tower into one file (typically with FP8 + per-tensor\n`weight_scale` ComfyUI quantization).\n\nThe matching tokenizer + processor are pulled from HuggingFace\n(`Qwen/Qwen2.5-VL-7B-Instruct`) on first use and cached for offline use."
+ "version": "1.3.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "QwenVLEncoder_Diffusers_Config": {
+ "SDXLCompelPromptInvocation": {
+ "category": "prompt",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Parse prompt using compel package to conditioning.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "hash": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "prompt": {
+ "default": "",
+ "description": "Prompt to be parsed by Compel to create a conditioning tensor",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Prompt",
"type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "ui_component": "textarea"
},
- "path": {
+ "style": {
+ "default": "",
+ "description": "Prompt to be parsed by Compel to create a conditioning tensor",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Style",
"type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "ui_component": "textarea"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "original_width": {
+ "default": 1024,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Original Width",
+ "type": "integer"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "original_height": {
+ "default": 1024,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Original Height",
+ "type": "integer"
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "crop_top": {
+ "default": 0,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Crop Top",
+ "type": "integer"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "crop_left": {
+ "default": 0,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Crop Left",
+ "type": "integer"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "target_width": {
+ "default": 1024,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Target Width",
+ "type": "integer"
},
- "source_api_response": {
+ "target_height": {
+ "default": 1024,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Target Height",
+ "type": "integer"
+ },
+ "clip": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "CLIP 1"
},
- "source_url": {
+ "clip2": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "CLIP 2"
},
- "cover_image": {
+ "mask": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/TensorField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
+ "default": null,
+ "description": "A mask defining the region that this conditioning prompt applies to.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
},
"type": {
- "type": "string",
- "const": "qwen_vl_encoder",
- "title": "Type",
- "default": "qwen_vl_encoder"
- },
- "format": {
- "type": "string",
- "const": "qwen_vl_encoder",
- "title": "Format",
- "default": "qwen_vl_encoder"
+ "const": "sdxl_compel_prompt",
+ "default": "sdxl_compel_prompt",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["sdxl", "compel", "prompt"],
+ "title": "Prompt - SDXL",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "base",
- "type",
- "format"
- ],
- "title": "QwenVLEncoder_Diffusers_Config",
- "description": "Configuration for standalone Qwen2.5-VL encoder models in diffusers-style folder layout.\n\nExpected structure:\n /\n text_encoder/\n config.json (with `_class_name` or `architectures` listing\n `Qwen2_5_VLForConditionalGeneration`)\n model.safetensors\n tokenizer/\n tokenizer_config.json\n ...\n processor/ (optional, for vision preprocessing)\n preprocessor_config.json\n\nThis lets users avoid downloading the full ~40 GB Qwen Image diffusers pipeline\nwhen they only need the Qwen2.5-VL encoder for use with a GGUF transformer."
+ "version": "1.2.1",
+ "output": {
+ "$ref": "#/components/schemas/ConditioningOutput"
+ }
},
- "RandomFloatInvocation": {
- "category": "math",
+ "SDXLLoRACollectionLoader": {
+ "category": "model",
"class": "invocation",
"classification": "stable",
- "description": "Outputs a single random float",
+ "description": "Applies a collection of SDXL LoRAs to the provided UNet and CLIP models.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -62132,64 +67297,110 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": false,
+ "default": true,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
"type": "boolean"
},
- "low": {
- "default": 0.0,
- "description": "The inclusive low value",
+ "loras": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "LoRA models and weights. May be a single LoRA or collection.",
"field_kind": "input",
"input": "any",
- "orig_default": 0.0,
+ "orig_default": null,
"orig_required": false,
- "title": "Low",
- "type": "number"
+ "title": "LoRAs",
+ "ui_model_base": ["sdxl"],
+ "ui_model_type": ["lora"]
},
- "high": {
- "default": 1.0,
- "description": "The exclusive high value",
+ "unet": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/UNetField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
"field_kind": "input",
- "input": "any",
- "orig_default": 1.0,
+ "input": "connection",
+ "orig_default": null,
"orig_required": false,
- "title": "High",
- "type": "number"
+ "title": "UNet"
},
- "decimals": {
- "default": 2,
- "description": "The number of decimal places to round to",
+ "clip": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
- "input": "any",
- "orig_default": 2,
+ "input": "connection",
+ "orig_default": null,
"orig_required": false,
- "title": "Decimals",
- "type": "integer"
+ "title": "CLIP"
+ },
+ "clip2": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "CLIP 2"
},
"type": {
- "const": "rand_float",
- "default": "rand_float",
+ "const": "sdxl_lora_collection_loader",
+ "default": "sdxl_lora_collection_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["math", "float", "random"],
- "title": "Random Float",
+ "tags": ["model"],
+ "title": "Apply LoRA Collection - SDXL",
"type": "object",
- "version": "1.0.1",
+ "version": "1.1.3",
"output": {
- "$ref": "#/components/schemas/FloatOutput"
+ "$ref": "#/components/schemas/SDXLLoRALoaderOutput"
}
},
- "RandomIntInvocation": {
- "category": "math",
+ "SDXLLoRALoaderInvocation": {
+ "category": "model",
"class": "invocation",
"classification": "stable",
- "description": "Outputs a single random integer.",
+ "description": "Apply selected lora to unet and text_encoder.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -62210,54 +67421,174 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": false,
+ "default": true,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
"type": "boolean"
},
- "low": {
- "default": 0,
- "description": "The inclusive low value",
+ "lora": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "LoRA model to load",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Low",
- "type": "integer"
+ "orig_required": true,
+ "title": "LoRA",
+ "ui_model_base": ["sdxl"],
+ "ui_model_type": ["lora"]
},
- "high": {
- "default": 2147483647,
- "description": "The exclusive high value",
+ "weight": {
+ "default": 0.75,
+ "description": "The weight at which the LoRA is applied to each model",
"field_kind": "input",
"input": "any",
- "orig_default": 2147483647,
+ "orig_default": 0.75,
"orig_required": false,
- "title": "High",
- "type": "integer"
+ "title": "Weight",
+ "type": "number"
+ },
+ "unet": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/UNetField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "UNet"
+ },
+ "clip": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "CLIP 1"
+ },
+ "clip2": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "CLIP 2"
},
"type": {
- "const": "rand_int",
- "default": "rand_int",
+ "const": "sdxl_lora_loader",
+ "default": "sdxl_lora_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["math", "random"],
- "title": "Random Integer",
+ "tags": ["lora", "model"],
+ "title": "Apply LoRA - SDXL",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.5",
"output": {
- "$ref": "#/components/schemas/IntegerOutput"
+ "$ref": "#/components/schemas/SDXLLoRALoaderOutput"
}
},
- "RandomRangeInvocation": {
- "category": "batch",
+ "SDXLLoRALoaderOutput": {
+ "class": "output",
+ "description": "SDXL LoRA Loader Output",
+ "properties": {
+ "unet": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/UNetField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
+ },
+ "clip": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP 1",
+ "ui_hidden": false
+ },
+ "clip2": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP 2",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "sdxl_lora_loader_output",
+ "default": "sdxl_lora_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "unet", "clip", "clip2", "type", "type"],
+ "title": "SDXLLoRALoaderOutput",
+ "type": "object"
+ },
+ "SDXLModelLoaderInvocation": {
+ "category": "model",
"class": "invocation",
"classification": "stable",
- "description": "Creates a collection of random numbers",
+ "description": "Loads an sdxl base model, outputting its submodels.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -62278,76 +67609,95 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": false,
+ "default": true,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
"type": "boolean"
},
- "low": {
- "default": 0,
- "description": "The inclusive low value",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Low",
- "type": "integer"
- },
- "high": {
- "default": 2147483647,
- "description": "The exclusive high value",
- "field_kind": "input",
- "input": "any",
- "orig_default": 2147483647,
- "orig_required": false,
- "title": "High",
- "type": "integer"
- },
- "size": {
- "default": 1,
- "description": "The number of values to generate",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "Size",
- "type": "integer"
- },
- "seed": {
- "default": 0,
- "description": "The seed for the RNG (omit for random)",
+ "model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "SDXL Main model (UNet, VAE, CLIP1, CLIP2) to load",
"field_kind": "input",
"input": "any",
- "maximum": 4294967295,
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "orig_required": true,
+ "ui_model_base": ["sdxl"],
+ "ui_model_type": ["main"]
},
"type": {
- "const": "random_range",
- "default": "random_range",
+ "const": "sdxl_model_loader",
+ "default": "sdxl_model_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["range", "integer", "random", "collection"],
- "title": "Random Range",
+ "tags": ["model", "sdxl"],
+ "title": "Main Model - SDXL",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.4",
"output": {
- "$ref": "#/components/schemas/IntegerCollectionOutput"
+ "$ref": "#/components/schemas/SDXLModelLoaderOutput"
}
},
- "RangeInvocation": {
- "category": "batch",
+ "SDXLModelLoaderOutput": {
+ "class": "output",
+ "description": "SDXL base model loader output",
+ "properties": {
+ "unet": {
+ "$ref": "#/components/schemas/UNetField",
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
+ },
+ "clip": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP 1",
+ "ui_hidden": false
+ },
+ "clip2": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP 2",
+ "ui_hidden": false
+ },
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "sdxl_model_loader_output",
+ "default": "sdxl_model_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "unet", "clip", "clip2", "vae", "type", "type"],
+ "title": "SDXLModelLoaderOutput",
+ "type": "object"
+ },
+ "SDXLRefinerCompelPromptInvocation": {
+ "category": "prompt",
"class": "invocation",
"classification": "stable",
- "description": "Creates a range of numbers from start to stop with step",
+ "description": "Parse prompt using compel package to conditioning.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -62374,58 +67724,104 @@
"title": "Use Cache",
"type": "boolean"
},
- "start": {
+ "style": {
+ "default": "",
+ "description": "Prompt to be parsed by Compel to create a conditioning tensor",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Style",
+ "type": "string",
+ "ui_component": "textarea"
+ },
+ "original_width": {
+ "default": 1024,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Original Width",
+ "type": "integer"
+ },
+ "original_height": {
+ "default": 1024,
+ "description": "",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Original Height",
+ "type": "integer"
+ },
+ "crop_top": {
"default": 0,
- "description": "The start of the range",
+ "description": "",
"field_kind": "input",
"input": "any",
"orig_default": 0,
"orig_required": false,
- "title": "Start",
+ "title": "Crop Top",
"type": "integer"
},
- "stop": {
- "default": 10,
- "description": "The stop of the range",
+ "crop_left": {
+ "default": 0,
+ "description": "",
"field_kind": "input",
"input": "any",
- "orig_default": 10,
+ "orig_default": 0,
"orig_required": false,
- "title": "Stop",
+ "title": "Crop Left",
"type": "integer"
},
- "step": {
- "default": 1,
- "description": "The step of the range",
+ "aesthetic_score": {
+ "default": 6.0,
+ "description": "The aesthetic score to apply to the conditioning tensor",
"field_kind": "input",
"input": "any",
- "orig_default": 1,
+ "orig_default": 6.0,
"orig_required": false,
- "title": "Step",
- "type": "integer"
+ "title": "Aesthetic Score",
+ "type": "number"
+ },
+ "clip2": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/CLIPField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
"type": {
- "const": "range",
- "default": "range",
+ "const": "sdxl_refiner_compel_prompt",
+ "default": "sdxl_refiner_compel_prompt",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["collection", "integer", "range"],
- "title": "Integer Range",
+ "tags": ["sdxl", "compel", "prompt"],
+ "title": "Prompt - SDXL Refiner",
"type": "object",
- "version": "1.0.0",
+ "version": "1.1.2",
"output": {
- "$ref": "#/components/schemas/IntegerCollectionOutput"
+ "$ref": "#/components/schemas/ConditioningOutput"
}
},
- "RangeOfSizeInvocation": {
- "category": "batch",
+ "SDXLRefinerModelLoaderInvocation": {
+ "category": "model",
"class": "invocation",
"classification": "stable",
- "description": "Creates a range from start to start + (size * step) incremented by step",
+ "description": "Loads an sdxl refiner model, outputting its submodels.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -62452,448 +67848,587 @@
"title": "Use Cache",
"type": "boolean"
},
- "start": {
- "default": 0,
- "description": "The start of the range",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Start",
- "type": "integer"
- },
- "size": {
- "default": 1,
- "description": "The number of values",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "Size",
- "type": "integer"
- },
- "step": {
- "default": 1,
- "description": "The step of the range",
+ "model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "SDXL Refiner Main Modde (UNet, VAE, CLIP2) to load",
"field_kind": "input",
"input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "Step",
- "type": "integer"
+ "orig_required": true,
+ "ui_model_base": ["sdxl-refiner"],
+ "ui_model_type": ["main"]
},
"type": {
- "const": "range_of_size",
- "default": "range_of_size",
+ "const": "sdxl_refiner_model_loader",
+ "default": "sdxl_refiner_model_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["collection", "integer", "size", "range"],
- "title": "Integer Range of Size",
+ "tags": ["model", "sdxl", "refiner"],
+ "title": "Refiner Model - SDXL",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.4",
"output": {
- "$ref": "#/components/schemas/IntegerCollectionOutput"
+ "$ref": "#/components/schemas/SDXLRefinerModelLoaderOutput"
}
},
- "RecallParameter": {
+ "SDXLRefinerModelLoaderOutput": {
+ "class": "output",
+ "description": "SDXL refiner model loader output",
"properties": {
- "positive_prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Positive Prompt",
- "description": "Positive prompt text"
+ "unet": {
+ "$ref": "#/components/schemas/UNetField",
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
},
- "negative_prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Negative Prompt",
- "description": "Negative prompt text"
+ "clip2": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP 2",
+ "ui_hidden": false
},
- "model": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Model",
- "description": "Main model name/identifier"
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
},
- "refiner_model": {
+ "type": {
+ "const": "sdxl_refiner_model_loader_output",
+ "default": "sdxl_refiner_model_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "unet", "clip2", "vae", "type", "type"],
+ "title": "SDXLRefinerModelLoaderOutput",
+ "type": "object"
+ },
+ "SQLiteDirection": {
+ "type": "string",
+ "enum": ["ASC", "DESC"],
+ "title": "SQLiteDirection"
+ },
+ "SaveImageInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Saves an image. Unlike an image primitive, this invocation stores a copy of the image.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Refiner Model",
- "description": "Refiner model name/identifier"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "vae_model": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Vae Model",
- "description": "VAE model name/identifier"
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "scheduler": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Scheduler",
- "description": "Scheduler name"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "steps": {
- "anyOf": [
- {
- "type": "integer",
- "minimum": 1.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Steps",
- "description": "Number of generation steps"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "refiner_steps": {
- "anyOf": [
- {
- "type": "integer",
- "minimum": 0.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Refiner Steps",
- "description": "Number of refiner steps"
+ "use_cache": {
+ "default": false,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "cfg_scale": {
+ "image": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Cfg Scale",
- "description": "CFG scale for guidance"
+ "default": null,
+ "description": "The image to process",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "cfg_rescale_multiplier": {
+ "type": {
+ "const": "save_image",
+ "default": "save_image",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["primitives", "image"],
+ "title": "Save Image",
+ "type": "object",
+ "version": "1.2.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "SaveImageToFileInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Saves an image to the gallery (like the standard Save Image node) AND additionally exports a copy\nto the filesystem with a custom filename.\n\nFilename pattern: {prefix}{uuid}{suffix}.{file_format}\n- The UUID is the same UUID used for the gallery entry, so the exported file can be matched to the gallery item.\n- The gallery entry itself always uses the plain UUID (prefix/suffix apply only to the exported file on disk).\n- Board and Metadata inputs behave exactly like the standard Save Image node.\n- The export target is restricted to (subfolders of) the InvokeAI outputs folder \u2014 absolute paths are rejected.\n\nExample: prefix=\"hero_\", suffix=\"_final\", file_format=\"png\" \u2192 \"hero__final.png\"",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Cfg Rescale Multiplier",
- "description": "CFG rescale multiplier"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "refiner_cfg_scale": {
+ "metadata": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Refiner Cfg Scale",
- "description": "Refiner CFG scale"
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "guidance": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ],
- "title": "Guidance",
- "description": "Guidance scale"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "width": {
- "anyOf": [
- {
- "type": "integer",
- "minimum": 64.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Width",
- "description": "Image width in pixels"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "height": {
- "anyOf": [
- {
- "type": "integer",
- "minimum": 64.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Height",
- "description": "Image height in pixels"
+ "use_cache": {
+ "default": false,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "seed": {
+ "image": {
"anyOf": [
{
- "type": "integer",
- "minimum": 0.0
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Seed",
- "description": "Random seed"
+ "default": null,
+ "description": "The image to save and export",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "denoise_strength": {
- "anyOf": [
- {
- "type": "number",
- "maximum": 1.0,
- "minimum": 0.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Denoise Strength",
- "description": "Denoising strength"
+ "output_directory": {
+ "default": "",
+ "description": "Target subdirectory (relative to the configured InvokeAI outputs folder) for the exported file. Leave empty to use the outputs folder directly. Example: 'my-exports' \u2192 /my-exports/. Nested paths like 'exports/2026' are allowed. Absolute paths and path traversal ('..') are not allowed for security reasons. The directory is created automatically if it doesn't exist.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Output Directory",
+ "type": "string"
},
- "refiner_denoise_start": {
- "anyOf": [
- {
- "type": "number",
- "maximum": 1.0,
- "minimum": 0.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Refiner Denoise Start",
- "description": "Refiner denoising start"
+ "prefix": {
+ "default": "",
+ "description": "Text prepended to the UUID in the exported filename. Example: 'portrait_' \u2192 'portrait_.png'",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Prefix",
+ "type": "string"
},
- "clip_skip": {
- "anyOf": [
- {
- "type": "integer",
- "minimum": 0.0
- },
- {
- "type": "null"
- }
- ],
- "title": "Clip Skip",
- "description": "CLIP skip layers"
+ "suffix": {
+ "default": "",
+ "description": "Text appended to the UUID (before the extension). Example: '_v2' \u2192 '_v2.png'",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Suffix",
+ "type": "string"
},
- "seamless_x": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Seamless X",
- "description": "Enable seamless X tiling"
+ "file_format": {
+ "default": "png",
+ "description": "File format for the exported file. PNG is lossless; JPG/WEBP are lossy and respect 'quality'.",
+ "enum": ["png", "jpg", "webp"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "png",
+ "orig_required": false,
+ "title": "File Format",
+ "type": "string"
},
- "seamless_y": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Seamless Y",
- "description": "Enable seamless Y tiling"
+ "quality": {
+ "default": 95,
+ "description": "Compression quality for JPG and WEBP (1-100, higher = better quality, larger file). Ignored for PNG.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 100,
+ "minimum": 1,
+ "orig_default": 95,
+ "orig_required": false,
+ "title": "Quality",
+ "type": "integer"
},
- "refiner_positive_aesthetic_score": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ],
- "title": "Refiner Positive Aesthetic Score",
- "description": "Refiner positive aesthetic score"
+ "type": {
+ "const": "save_image_to_file",
+ "default": "save_image_to_file",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "export", "file", "save"],
+ "title": "Save Image (Gallery + File Export)",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "ScaleLatentsInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Scales latents by a given factor.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "refiner_negative_aesthetic_score": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "type": "null"
- }
- ],
- "title": "Refiner Negative Aesthetic Score",
- "description": "Refiner negative aesthetic score"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "loras": {
- "anyOf": [
- {
- "items": {
- "$ref": "#/components/schemas/LoRARecallParameter"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "title": "Loras",
- "description": "List of LoRAs with their weights"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "control_layers": {
+ "latents": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/ControlNetRecallParameter"
- },
- "type": "array"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Control Layers",
- "description": "List of control adapters (ControlNet, T2I Adapter, Control LoRA) with their settings"
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "ip_adapters": {
+ "scale_factor": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/IPAdapterRecallParameter"
- },
- "type": "array"
+ "exclusiveMinimum": 0,
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Ip Adapters",
- "description": "List of IP Adapters with their settings"
+ "default": null,
+ "description": "The factor by which to scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Scale Factor"
},
- "reference_images": {
- "anyOf": [
- {
- "items": {
- "$ref": "#/components/schemas/ReferenceImageRecallParameter"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "title": "Reference Images",
- "description": "List of model-free reference images for architectures that consume reference images directly (FLUX.2 Klein, FLUX Kontext, Qwen Image Edit). The frontend picks the correct config type based on the currently-selected main model."
+ "mode": {
+ "default": "bilinear",
+ "description": "Interpolation mode",
+ "enum": ["nearest", "linear", "bilinear", "bicubic", "trilinear", "area", "nearest-exact"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "bilinear",
+ "orig_required": false,
+ "title": "Mode",
+ "type": "string"
+ },
+ "antialias": {
+ "default": false,
+ "description": "Whether or not to apply antialiasing (bilinear or bicubic only)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Antialias",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "lscale",
+ "default": "lscale",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "additionalProperties": false,
+ "required": ["type", "id"],
+ "tags": ["latents", "resize"],
+ "title": "Scale Latents",
"type": "object",
- "title": "RecallParameter",
- "description": "Request model for updating recallable parameters."
+ "version": "1.0.2",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
- "RecallParametersUpdatedEvent": {
- "description": "Event model for recall_parameters_updated",
+ "SchedulerInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Selects a scheduler.",
+ "node_pack": "invokeai",
"properties": {
- "timestamp": {
- "description": "The timestamp of the event",
- "title": "Timestamp",
- "type": "integer"
- },
- "queue_id": {
- "description": "The ID of the queue",
- "title": "Queue Id",
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
"type": "string"
},
- "user_id": {
- "description": "The ID of the user whose recall parameters were updated",
- "title": "User Id",
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler to use during inference",
+ "enum": [
+ "ddim",
+ "ddpm",
+ "deis",
+ "deis_k",
+ "lms",
+ "lms_k",
+ "pndm",
+ "heun",
+ "heun_k",
+ "euler",
+ "euler_k",
+ "euler_a",
+ "kdpm_2",
+ "kdpm_2_k",
+ "kdpm_2_a",
+ "kdpm_2_a_k",
+ "dpmpp_2s",
+ "dpmpp_2s_k",
+ "dpmpp_2m",
+ "dpmpp_2m_k",
+ "dpmpp_2m_sde",
+ "dpmpp_2m_sde_k",
+ "dpmpp_3m",
+ "dpmpp_3m_k",
+ "dpmpp_sde",
+ "dpmpp_sde_k",
+ "er_sde",
+ "unipc",
+ "unipc_k",
+ "lcm",
+ "tcd"
+ ],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Scheduler",
+ "type": "string",
+ "ui_type": "SchedulerField"
+ },
+ "type": {
+ "const": "scheduler",
+ "default": "scheduler",
+ "field_kind": "node_attribute",
+ "title": "type",
"type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["scheduler"],
+ "title": "Scheduler",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/SchedulerOutput"
+ }
+ },
+ "SchedulerOutput": {
+ "class": "output",
+ "properties": {
+ "scheduler": {
+ "description": "Scheduler to use during inference",
+ "enum": [
+ "ddim",
+ "ddpm",
+ "deis",
+ "deis_k",
+ "lms",
+ "lms_k",
+ "pndm",
+ "heun",
+ "heun_k",
+ "euler",
+ "euler_k",
+ "euler_a",
+ "kdpm_2",
+ "kdpm_2_k",
+ "kdpm_2_a",
+ "kdpm_2_a_k",
+ "dpmpp_2s",
+ "dpmpp_2s_k",
+ "dpmpp_2m",
+ "dpmpp_2m_k",
+ "dpmpp_2m_sde",
+ "dpmpp_2m_sde_k",
+ "dpmpp_3m",
+ "dpmpp_3m_k",
+ "dpmpp_sde",
+ "dpmpp_sde_k",
+ "er_sde",
+ "unipc",
+ "unipc_k",
+ "lcm",
+ "tcd"
+ ],
+ "field_kind": "output",
+ "title": "Scheduler",
+ "type": "string",
+ "ui_hidden": false,
+ "ui_type": "SchedulerField"
},
- "parameters": {
- "additionalProperties": true,
- "description": "The recall parameters that were updated",
- "title": "Parameters",
- "type": "object"
+ "type": {
+ "const": "scheduler_output",
+ "default": "scheduler_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "required": ["timestamp", "queue_id", "user_id", "parameters"],
- "title": "RecallParametersUpdatedEvent",
+ "required": ["output_meta", "scheduler", "type", "type"],
+ "title": "SchedulerOutput",
"type": "object"
},
- "RectangleMaskInvocation": {
- "category": "mask",
+ "SchedulerPredictionType": {
+ "type": "string",
+ "enum": ["epsilon", "v_prediction", "sample"],
+ "title": "SchedulerPredictionType",
+ "description": "Scheduler prediction type."
+ },
+ "Sd3ModelLoaderInvocation": {
+ "category": "model",
"class": "invocation",
"classification": "stable",
- "description": "Create a rectangular mask.",
+ "description": "Loads a SD3 base model, outputting its submodels.",
"node_pack": "invokeai",
"properties": {
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -62918,206 +68453,163 @@
"title": "Use Cache",
"type": "boolean"
},
- "width": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The width of the entire mask.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Width"
- },
- "height": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The height of the entire mask.",
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "SD3 model (MMDiTX) to load",
"field_kind": "input",
- "input": "any",
+ "input": "direct",
"orig_required": true,
- "title": "Height"
+ "ui_model_base": ["sd-3"],
+ "ui_model_type": ["main"]
},
- "x_left": {
+ "t5_encoder_model": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The left x-coordinate of the rectangular masked region (inclusive).",
+ "description": "T5 tokenizer and text encoder",
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "X Left"
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "T5 Encoder",
+ "ui_model_type": ["t5_encoder"]
},
- "y_top": {
+ "clip_l_model": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The top y-coordinate of the rectangular masked region (inclusive).",
+ "description": "CLIP Embed loader",
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Y Top"
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "CLIP L Encoder",
+ "ui_model_type": ["clip_embed"],
+ "ui_model_variant": ["large"]
},
- "rectangle_width": {
+ "clip_g_model": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The width of the rectangular masked region.",
+ "description": "CLIP-G Embed loader",
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Rectangle Width"
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "CLIP G Encoder",
+ "ui_model_type": ["clip_embed"],
+ "ui_model_variant": ["gigantic"]
},
- "rectangle_height": {
+ "vae_model": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The height of the rectangular masked region.",
+ "description": "VAE model to load",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Rectangle Height"
+ "orig_default": null,
+ "orig_required": false,
+ "title": "VAE",
+ "ui_model_base": ["sd-3"],
+ "ui_model_type": ["vae"]
},
"type": {
- "const": "rectangle_mask",
- "default": "rectangle_mask",
+ "const": "sd3_model_loader",
+ "default": "sd3_model_loader",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["conditioning"],
- "title": "Create Rectangle Mask",
+ "required": ["model", "type", "id"],
+ "tags": ["model", "sd3"],
+ "title": "Main Model - SD3",
"type": "object",
"version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/MaskOutput"
+ "$ref": "#/components/schemas/Sd3ModelLoaderOutput"
}
},
- "ReferenceImageRecallParameter": {
- "properties": {
- "image_name": {
- "type": "string",
- "title": "Image Name",
- "description": "The filename of the reference image in outputs/images"
- }
- },
- "type": "object",
- "required": ["image_name"],
- "title": "ReferenceImageRecallParameter",
- "description": "Global reference-image configuration for recall.\n\nUsed for reference images that feed directly into the main model rather\nthan through a separate IP-Adapter / ControlNet model \u2014 for example\nFLUX.2 Klein, FLUX Kontext, and Qwen Image Edit. The receiving frontend\npicks the correct config type (``flux2_reference_image`` /\n``qwen_image_reference_image`` / ``flux_kontext_reference_image``) based\non the currently-selected main model."
- },
- "RemoteModelFile": {
+ "Sd3ModelLoaderOutput": {
+ "class": "output",
+ "description": "SD3 base model loader output.",
"properties": {
- "url": {
- "type": "string",
- "minLength": 1,
- "format": "uri",
- "title": "Url",
- "description": "The url to download this model file"
+ "transformer": {
+ "$ref": "#/components/schemas/TransformerField",
+ "description": "Transformer",
+ "field_kind": "output",
+ "title": "Transformer",
+ "ui_hidden": false
},
- "path": {
- "type": "string",
- "format": "path",
- "title": "Path",
- "description": "The path to the file, relative to the model root"
+ "clip_l": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP L",
+ "ui_hidden": false
},
- "size": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "title": "Size",
- "description": "The size of this file, in bytes",
- "default": 0
+ "clip_g": {
+ "$ref": "#/components/schemas/CLIPField",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "field_kind": "output",
+ "title": "CLIP G",
+ "ui_hidden": false
},
- "sha256": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Sha256",
- "description": "SHA256 hash of this model (not always available)"
- }
- },
- "type": "object",
- "required": ["url", "path"],
- "title": "RemoteModelFile",
- "description": "Information about a downloadable file that forms part of a model."
- },
- "RemoveImagesFromBoardResult": {
- "properties": {
- "affected_boards": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Affected Boards",
- "description": "The ids of boards affected by the delete operation"
+ "t5_encoder": {
+ "$ref": "#/components/schemas/T5EncoderField",
+ "description": "T5 tokenizer and text encoder",
+ "field_kind": "output",
+ "title": "T5 Encoder",
+ "ui_hidden": false
},
- "removed_images": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Removed Images",
- "description": "The image names that were removed from their board"
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "sd3_model_loader_output",
+ "default": "sd3_model_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
- "type": "object",
- "required": ["affected_boards", "removed_images"],
- "title": "RemoveImagesFromBoardResult"
+ "required": ["output_meta", "transformer", "clip_l", "clip_g", "t5_encoder", "vae", "type", "type"],
+ "title": "Sd3ModelLoaderOutput",
+ "type": "object"
},
- "ResizeLatentsInvocation": {
- "category": "latents",
+ "Sd3TextEncoderInvocation": {
+ "category": "prompt",
"class": "invocation",
"classification": "stable",
- "description": "Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8.",
+ "description": "Encodes and preps a prompt for a SD3 image.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -63144,126 +68636,93 @@
"title": "Use Cache",
"type": "boolean"
},
- "latents": {
+ "clip_l": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Latents tensor",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
"input": "connection",
- "orig_required": true
+ "orig_required": true,
+ "title": "CLIP L"
},
- "width": {
+ "clip_g": {
"anyOf": [
{
- "minimum": 64,
- "multipleOf": 8,
- "type": "integer"
+ "$ref": "#/components/schemas/CLIPField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Width of output (px)",
+ "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_required": true,
- "title": "Width"
+ "title": "CLIP G"
},
- "height": {
+ "t5_encoder": {
"anyOf": [
{
- "minimum": 64,
- "multipleOf": 8,
- "type": "integer"
+ "$ref": "#/components/schemas/T5EncoderField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Width of output (px)",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Height"
- },
- "mode": {
- "default": "bilinear",
- "description": "Interpolation mode",
- "enum": ["nearest", "linear", "bilinear", "bicubic", "trilinear", "area", "nearest-exact"],
+ "description": "T5 tokenizer and text encoder",
"field_kind": "input",
- "input": "any",
- "orig_default": "bilinear",
+ "input": "connection",
+ "orig_default": null,
"orig_required": false,
- "title": "Mode",
- "type": "string"
+ "title": "T5Encoder"
},
- "antialias": {
- "default": false,
- "description": "Whether or not to apply antialiasing (bilinear or bicubic only)",
+ "prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Text prompt to encode.",
"field_kind": "input",
"input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Antialias",
- "type": "boolean"
+ "orig_required": true,
+ "title": "Prompt"
},
"type": {
- "const": "lresize",
- "default": "lresize",
+ "const": "sd3_text_encoder",
+ "default": "sd3_text_encoder",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["latents", "resize"],
- "title": "Resize Latents",
+ "tags": ["prompt", "conditioning", "sd3"],
+ "title": "Prompt - SD3",
"type": "object",
- "version": "1.0.2",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/LatentsOutput"
+ "$ref": "#/components/schemas/SD3ConditioningOutput"
}
},
- "ResourceOrigin": {
- "type": "string",
- "enum": ["internal", "external"],
- "title": "ResourceOrigin",
- "description": "The origin of a resource (eg image).\n\n- INTERNAL: The resource was created by the application.\n- EXTERNAL: The resource was not created by the application.\nThis may be a user-initiated upload, or an internal application upload (eg Canvas init image)."
- },
- "RetryItemsResult": {
- "properties": {
- "queue_id": {
- "type": "string",
- "title": "Queue Id",
- "description": "The ID of the queue"
- },
- "retried_item_ids": {
- "items": {
- "type": "integer"
- },
- "type": "array",
- "title": "Retried Item Ids",
- "description": "The IDs of the queue items that were retried"
- }
- },
- "type": "object",
- "required": ["queue_id", "retried_item_ids"],
- "title": "RetryItemsResult"
- },
- "RoundInvocation": {
- "category": "math",
+ "SeamlessModeInvocation": {
+ "category": "model",
"class": "invocation",
"classification": "stable",
- "description": "Rounds a float to a specified number of decimal places.",
+ "description": "Applies the seamless transformation to the Model UNet and VAE.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -63290,125 +68749,128 @@
"title": "Use Cache",
"type": "boolean"
},
- "value": {
- "default": 0,
- "description": "The float value",
+ "unet": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/UNetField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "UNet"
+ },
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE model to load",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "VAE"
+ },
+ "seamless_y": {
+ "default": true,
+ "description": "Specify whether Y axis is seamless",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
+ "orig_default": true,
"orig_required": false,
- "title": "Value",
- "type": "number"
+ "title": "Seamless Y",
+ "type": "boolean"
},
- "decimals": {
- "default": 0,
- "description": "The number of decimal places",
+ "seamless_x": {
+ "default": true,
+ "description": "Specify whether X axis is seamless",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
+ "orig_default": true,
"orig_required": false,
- "title": "Decimals",
- "type": "integer"
+ "title": "Seamless X",
+ "type": "boolean"
},
"type": {
- "const": "round_float",
- "default": "round_float",
+ "const": "seamless",
+ "default": "seamless",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["math", "round"],
- "title": "Round Float",
+ "tags": ["seamless", "model"],
+ "title": "Apply Seamless - SD1.5, SDXL",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.2",
"output": {
- "$ref": "#/components/schemas/FloatOutput"
+ "$ref": "#/components/schemas/SeamlessModeOutput"
}
},
- "SAMPoint": {
- "properties": {
- "x": {
- "description": "The x-coordinate of the point",
- "title": "X",
- "type": "integer"
- },
- "y": {
- "description": "The y-coordinate of the point",
- "title": "Y",
- "type": "integer"
- },
- "label": {
- "$ref": "#/components/schemas/SAMPointLabel",
- "description": "The label of the point"
- }
- },
- "required": ["x", "y", "label"],
- "title": "SAMPoint",
- "type": "object"
- },
- "SAMPointLabel": {
- "enum": [-1, 0, 1],
- "title": "SAMPointLabel",
- "type": "integer"
- },
- "SAMPointsField": {
- "properties": {
- "points": {
- "description": "The points of the object",
- "items": {
- "$ref": "#/components/schemas/SAMPoint"
- },
- "minItems": 1,
- "title": "Points",
- "type": "array"
- }
- },
- "required": ["points"],
- "title": "SAMPointsField",
- "type": "object"
- },
- "SD3ConditioningField": {
- "description": "A conditioning tensor primitive value",
- "properties": {
- "conditioning_name": {
- "description": "The name of conditioning tensor",
- "title": "Conditioning Name",
- "type": "string"
- }
- },
- "required": ["conditioning_name"],
- "title": "SD3ConditioningField",
- "type": "object"
- },
- "SD3ConditioningOutput": {
+ "SeamlessModeOutput": {
"class": "output",
- "description": "Base class for nodes that output a single SD3 conditioning tensor",
+ "description": "Modified Seamless Model output",
"properties": {
- "conditioning": {
- "$ref": "#/components/schemas/SD3ConditioningField",
- "description": "Conditioning tensor",
+ "unet": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/UNetField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
+ },
+ "vae": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VAEField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE",
"field_kind": "output",
+ "title": "VAE",
"ui_hidden": false
},
"type": {
- "const": "sd3_conditioning_output",
- "default": "sd3_conditioning_output",
+ "const": "seamless_output",
+ "default": "seamless_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "conditioning", "type", "type"],
- "title": "SD3ConditioningOutput",
+ "required": ["output_meta", "unet", "vae", "type", "type"],
+ "title": "SeamlessModeOutput",
"type": "object"
},
- "SD3DenoiseInvocation": {
- "category": "latents",
+ "SeedreamImageGenerationInvocation": {
+ "category": "image",
"class": "invocation",
"classification": "stable",
- "description": "Run denoising process with a SD3 model.",
+ "description": "Generate images using a BytePlus Seedream model.",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -63467,731 +68929,965 @@
"title": "Use Cache",
"type": "boolean"
},
- "latents": {
+ "model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Main model (UNet, VAE, CLIP) to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "ui_model_base": ["external"],
+ "ui_model_format": ["external_api"],
+ "ui_model_provider_id": ["seedream"],
+ "ui_model_type": ["external_image_generator"]
+ },
+ "mode": {
+ "default": "txt2img",
+ "description": "Generation mode.",
+ "enum": ["txt2img", "img2img", "inpaint"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "txt2img",
+ "orig_required": false,
+ "title": "Mode",
+ "type": "string",
+ "ui_hidden": true
+ },
+ "prompt": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Prompt",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt"
+ },
+ "seed": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Seed for random number generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Seed"
+ },
+ "num_images": {
+ "default": 1,
+ "description": "Number of images to generate",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Num Images",
+ "type": "integer"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Width of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of output (px)",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "image_size": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Image size preset (e.g. 1K, 2K, 4K)",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Image Size"
+ },
+ "init_image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Init image for img2img/inpaint",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "mask_image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Mask image for inpaint",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "ui_hidden": true
+ },
+ "reference_images": {
+ "default": [],
+ "description": "Reference images",
+ "field_kind": "input",
+ "input": "any",
+ "items": {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ "orig_default": [],
+ "orig_required": false,
+ "title": "Reference Images",
+ "type": "array"
+ },
+ "watermark": {
+ "default": false,
+ "description": "Add watermark to generated images",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Watermark",
+ "type": "boolean"
+ },
+ "optimize_prompt": {
+ "default": false,
+ "description": "Let the model optimize the prompt before generation",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Optimize Prompt",
+ "type": "boolean"
+ },
+ "type": {
+ "const": "seedream_image_generation",
+ "default": "seedream_image_generation",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["external", "generation", "seedream"],
+ "title": "Seedream Image Generation",
+ "type": "object",
+ "version": "1.1.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageCollectionOutput"
+ }
+ },
+ "SegmentAnythingInvocation": {
+ "category": "segmentation",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Runs a Segment Anything Model (SAM or SAM2).",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "model": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "enum": [
+ "segment-anything-base",
+ "segment-anything-large",
+ "segment-anything-huge",
+ "segment-anything-2-tiny",
+ "segment-anything-2-small",
+ "segment-anything-2-base",
+ "segment-anything-2-large"
+ ],
+ "type": "string"
},
{
"type": "null"
}
],
"default": null,
- "description": "Latents tensor",
+ "description": "The Segment Anything model to use (SAM or SAM2).",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "input": "any",
+ "orig_required": true,
+ "title": "Model"
},
- "noise": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "Noise tensor",
+ "description": "The image to segment.",
"field_kind": "input",
- "input": "connection",
+ "input": "any",
+ "orig_required": true
+ },
+ "bounding_boxes": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/components/schemas/BoundingBoxField"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The bounding boxes to prompt the model with.",
+ "field_kind": "input",
+ "input": "any",
"orig_default": null,
- "orig_required": false
+ "orig_required": false,
+ "title": "Bounding Boxes"
},
- "denoise_mask": {
+ "point_lists": {
"anyOf": [
{
- "$ref": "#/components/schemas/DenoiseMaskField"
+ "items": {
+ "$ref": "#/components/schemas/SAMPointsField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "description": "The list of point lists to prompt the model with. Each list of points represents a single object.",
"field_kind": "input",
- "input": "connection",
+ "input": "any",
"orig_default": null,
- "orig_required": false
+ "orig_required": false,
+ "title": "Point Lists"
},
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
+ "apply_polygon_refinement": {
+ "default": true,
+ "description": "Whether to apply polygon refinement to the masks. This will smooth the edges of the masks slightly and ensure that each mask consists of a single closed polygon (before merging).",
"field_kind": "input",
"input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
+ "orig_default": true,
"orig_required": false,
- "title": "Denoising Start",
- "type": "number"
+ "title": "Apply Polygon Refinement",
+ "type": "boolean"
},
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
+ "mask_filter": {
+ "default": "all",
+ "description": "The filtering to apply to the detected masks before merging them into a final output.",
+ "enum": ["all", "largest", "highest_box_score"],
"field_kind": "input",
"input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
+ "orig_default": "all",
"orig_required": false,
- "title": "Denoising End",
- "type": "number"
+ "title": "Mask Filter",
+ "type": "string"
},
- "transformer": {
+ "type": {
+ "const": "segment_anything",
+ "default": "segment_anything",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "segmentation", "sam", "sam2"],
+ "title": "Segment Anything",
+ "type": "object",
+ "version": "1.3.0",
+ "output": {
+ "$ref": "#/components/schemas/MaskOutput"
+ }
+ },
+ "SessionProcessorStatus": {
+ "properties": {
+ "is_started": {
+ "type": "boolean",
+ "title": "Is Started",
+ "description": "Whether the session processor is started"
+ },
+ "is_processing": {
+ "type": "boolean",
+ "title": "Is Processing",
+ "description": "Whether a session is being processed"
+ }
+ },
+ "type": "object",
+ "required": ["is_started", "is_processing"],
+ "title": "SessionProcessorStatus"
+ },
+ "SessionQueueAndProcessorStatus": {
+ "properties": {
+ "queue": {
+ "$ref": "#/components/schemas/SessionQueueStatus"
+ },
+ "processor": {
+ "$ref": "#/components/schemas/SessionProcessorStatus"
+ }
+ },
+ "type": "object",
+ "required": ["queue", "processor"],
+ "title": "SessionQueueAndProcessorStatus",
+ "description": "The overall status of session queue and processor"
+ },
+ "SessionQueueCountsByDestination": {
+ "properties": {
+ "queue_id": {
+ "type": "string",
+ "title": "Queue Id",
+ "description": "The ID of the queue"
+ },
+ "destination": {
+ "type": "string",
+ "title": "Destination",
+ "description": "The destination of queue items included in this status"
+ },
+ "pending": {
+ "type": "integer",
+ "title": "Pending",
+ "description": "Number of queue items with status 'pending' for the destination"
+ },
+ "in_progress": {
+ "type": "integer",
+ "title": "In Progress",
+ "description": "Number of queue items with status 'in_progress' for the destination"
+ },
+ "waiting": {
+ "type": "integer",
+ "title": "Waiting",
+ "description": "Number of queue items with status 'waiting' for the destination"
+ },
+ "completed": {
+ "type": "integer",
+ "title": "Completed",
+ "description": "Number of queue items with status 'complete' for the destination"
+ },
+ "failed": {
+ "type": "integer",
+ "title": "Failed",
+ "description": "Number of queue items with status 'error' for the destination"
+ },
+ "canceled": {
+ "type": "integer",
+ "title": "Canceled",
+ "description": "Number of queue items with status 'canceled' for the destination"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of queue items for the destination"
+ }
+ },
+ "type": "object",
+ "required": [
+ "queue_id",
+ "destination",
+ "pending",
+ "in_progress",
+ "waiting",
+ "completed",
+ "failed",
+ "canceled",
+ "total"
+ ],
+ "title": "SessionQueueCountsByDestination"
+ },
+ "SessionQueueItem": {
+ "properties": {
+ "item_id": {
+ "type": "integer",
+ "title": "Item Id",
+ "description": "The identifier of the session queue item"
+ },
+ "status": {
+ "type": "string",
+ "enum": ["pending", "in_progress", "waiting", "completed", "failed", "canceled"],
+ "title": "Status",
+ "description": "The status of this queue item",
+ "default": "pending"
+ },
+ "status_sequence": {
"anyOf": [
{
- "$ref": "#/components/schemas/TransformerField"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "SD3 model (MMDiTX) to load",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "Transformer"
+ "title": "Status Sequence",
+ "description": "A monotonically increasing version for this queue item's visible status lifecycle"
},
- "positive_conditioning": {
+ "priority": {
+ "type": "integer",
+ "title": "Priority",
+ "description": "The priority of this queue item",
+ "default": 0
+ },
+ "batch_id": {
+ "type": "string",
+ "title": "Batch Id",
+ "description": "The ID of the batch associated with this queue item"
+ },
+ "origin": {
"anyOf": [
{
- "$ref": "#/components/schemas/SD3ConditioningField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Origin",
+ "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results."
},
- "negative_conditioning": {
+ "destination": {
"anyOf": [
{
- "$ref": "#/components/schemas/SD3ConditioningField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Negative conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Destination",
+ "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results"
},
- "cfg_scale": {
+ "session_id": {
+ "type": "string",
+ "title": "Session Id",
+ "description": "The ID of the session associated with this queue item. The session doesn't exist in graph_executions until the queue item is executed."
+ },
+ "error_type": {
"anyOf": [
{
- "type": "number"
+ "type": "string"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
],
- "default": 3.5,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 3.5,
- "orig_required": false,
- "title": "CFG Scale"
+ "title": "Error Type",
+ "description": "The error type if this queue item errored"
},
- "width": {
- "default": 1024,
- "description": "Width of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
+ "error_message": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Error Message",
+ "description": "The error message if this queue item errored"
},
- "height": {
- "default": 1024,
- "description": "Height of the generated image.",
- "field_kind": "input",
- "input": "any",
- "multipleOf": 16,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
+ "error_traceback": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Error Traceback",
+ "description": "The error traceback if this queue item errored"
},
- "steps": {
- "default": 10,
- "description": "Number of steps to run",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 10,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
+ "created_at": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "string"
+ }
+ ],
+ "title": "Created At",
+ "description": "When this queue item was created"
},
- "seed": {
- "default": 0,
- "description": "Randomness seed for reproducibility.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Seed",
- "type": "integer"
+ "updated_at": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "string"
+ }
+ ],
+ "title": "Updated At",
+ "description": "When this queue item was updated"
},
- "type": {
- "const": "sd3_denoise",
- "default": "sd3_denoise",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "sd3"],
- "title": "Denoise - SD3",
- "type": "object",
- "version": "1.2.0",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
- },
- "SD3ImageToLatentsInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "stable",
- "description": "Generates latents from an image.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "started_at": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Started At",
+ "description": "When this queue item was started"
},
- "metadata": {
+ "completed_at": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Completed At",
+ "description": "When this queue item was completed"
},
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "queue_id": {
+ "type": "string",
+ "title": "Queue Id",
+ "description": "The id of the queue with which this item is associated"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "user_id": {
+ "type": "string",
+ "title": "User Id",
+ "description": "The id of the user who created this queue item",
+ "default": "system"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "user_display_name": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "User Display Name",
+ "description": "The display name of the user who created this queue item, if available"
},
- "image": {
+ "user_email": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The image to encode",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "User Email",
+ "description": "The email of the user who created this queue item, if available"
+ },
+ "field_values": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/components/schemas/NodeFieldValue"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Field Values",
+ "description": "The field values that were used for this queue item"
+ },
+ "retried_from_item_id": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Retried From Item Id",
+ "description": "The item_id of the queue item that this item was retried from"
+ },
+ "workflow_call_id": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Workflow Call Id",
+ "description": "The active workflow-call relationship id when this queue item is a child execution."
},
- "vae": {
+ "parent_item_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Parent Item Id",
+ "description": "The parent queue item id when this queue item is a child workflow execution."
},
- "type": {
- "const": "sd3_i2l",
- "default": "sd3_i2l",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["image", "latents", "vae", "i2l", "sd3"],
- "title": "Image to Latents - SD3",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
- },
- "SD3LatentsToImageInvocation": {
- "category": "latents",
- "class": "invocation",
- "classification": "stable",
- "description": "Generates an image from latents.",
- "node_pack": "invokeai",
- "properties": {
- "board": {
+ "parent_session_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Parent Session Id",
+ "description": "The parent session id when this queue item is a child workflow execution."
},
- "metadata": {
+ "root_item_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Root Item Id",
+ "description": "The root queue item id for this workflow call chain, if any."
},
- "latents": {
+ "workflow_call_depth": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "Workflow Call Depth",
+ "description": "The 1-based workflow-call depth for this queue item when it is a child execution."
},
- "vae": {
+ "session": {
+ "$ref": "#/components/schemas/GraphExecutionState",
+ "description": "The fully-populated session to be executed"
+ },
+ "workflow": {
"anyOf": [
{
- "$ref": "#/components/schemas/VAEField"
+ "$ref": "#/components/schemas/WorkflowWithoutID"
},
{
"type": "null"
}
],
- "default": null,
- "description": "VAE",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "type": {
- "const": "sd3_l2i",
- "default": "sd3_l2i",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "description": "The workflow associated with this queue item"
}
},
- "required": ["type", "id"],
- "tags": ["latents", "image", "vae", "l2i", "sd3"],
- "title": "Latents to Image - SD3",
"type": "object",
- "version": "1.3.2",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": [
+ "item_id",
+ "status",
+ "batch_id",
+ "queue_id",
+ "session_id",
+ "session",
+ "priority",
+ "session_id",
+ "created_at",
+ "updated_at"
+ ],
+ "title": "SessionQueueItem",
+ "description": "Session queue item without the full graph. Used for serialization."
},
- "SDXLCompelPromptInvocation": {
- "category": "prompt",
- "class": "invocation",
- "classification": "stable",
- "description": "Parse prompt using compel package to conditioning.",
- "node_pack": "invokeai",
+ "SessionQueueStatus": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "prompt": {
- "default": "",
- "description": "Prompt to be parsed by Compel to create a conditioning tensor",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Prompt",
- "type": "string",
- "ui_component": "textarea"
- },
- "style": {
- "default": "",
- "description": "Prompt to be parsed by Compel to create a conditioning tensor",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Style",
+ "queue_id": {
"type": "string",
- "ui_component": "textarea"
- },
- "original_width": {
- "default": 1024,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Original Width",
- "type": "integer"
- },
- "original_height": {
- "default": 1024,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Original Height",
- "type": "integer"
- },
- "crop_top": {
- "default": 0,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Crop Top",
- "type": "integer"
- },
- "crop_left": {
- "default": 0,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Crop Left",
- "type": "integer"
- },
- "target_width": {
- "default": 1024,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Target Width",
- "type": "integer"
- },
- "target_height": {
- "default": 1024,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Target Height",
- "type": "integer"
+ "title": "Queue Id",
+ "description": "The ID of the queue"
},
- "clip": {
+ "item_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "CLIP 1"
+ "title": "Item Id",
+ "description": "The current queue item id"
},
- "clip2": {
+ "batch_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "CLIP 2"
+ "title": "Batch Id",
+ "description": "The current queue item's batch id"
},
- "mask": {
+ "session_id": {
"anyOf": [
{
- "$ref": "#/components/schemas/TensorField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "A mask defining the region that this conditioning prompt applies to.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
+ "title": "Session Id",
+ "description": "The current queue item's session id"
},
- "type": {
- "const": "sdxl_compel_prompt",
- "default": "sdxl_compel_prompt",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["sdxl", "compel", "prompt"],
- "title": "Prompt - SDXL",
- "type": "object",
- "version": "1.2.1",
- "output": {
- "$ref": "#/components/schemas/ConditioningOutput"
- }
- },
- "SDXLLoRACollectionLoader": {
- "category": "model",
- "class": "invocation",
- "classification": "stable",
- "description": "Applies a collection of SDXL LoRAs to the provided UNet and CLIP models.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "pending": {
+ "type": "integer",
+ "title": "Pending",
+ "description": "Number of queue items with status 'pending'"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "in_progress": {
+ "type": "integer",
+ "title": "In Progress",
+ "description": "Number of queue items with status 'in_progress'"
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "waiting": {
+ "type": "integer",
+ "title": "Waiting",
+ "description": "Number of queue items with status 'waiting'"
},
- "loras": {
+ "completed": {
+ "type": "integer",
+ "title": "Completed",
+ "description": "Number of queue items with status 'complete'"
+ },
+ "failed": {
+ "type": "integer",
+ "title": "Failed",
+ "description": "Number of queue items with status 'error'"
+ },
+ "canceled": {
+ "type": "integer",
+ "title": "Canceled",
+ "description": "Number of queue items with status 'canceled'"
+ },
+ "total": {
+ "type": "integer",
+ "title": "Total",
+ "description": "Total number of queue items"
+ },
+ "user_pending": {
"anyOf": [
{
- "$ref": "#/components/schemas/LoRAField"
- },
- {
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "type": "array"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "LoRA models and weights. May be a single LoRA or collection.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "LoRAs",
- "ui_model_base": ["sdxl"],
- "ui_model_type": ["lora"]
+ "title": "User Pending",
+ "description": "Number of the requesting user's queue items with status 'pending' (None for admins/global callers)"
},
- "unet": {
+ "user_in_progress": {
"anyOf": [
{
- "$ref": "#/components/schemas/UNetField"
+ "type": "integer"
},
{
"type": "null"
}
],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "UNet"
+ "title": "User In Progress",
+ "description": "Number of the requesting user's queue items with status 'in_progress' (None for admins/global callers)"
+ }
+ },
+ "type": "object",
+ "required": [
+ "queue_id",
+ "item_id",
+ "batch_id",
+ "session_id",
+ "pending",
+ "in_progress",
+ "waiting",
+ "completed",
+ "failed",
+ "canceled",
+ "total"
+ ],
+ "title": "SessionQueueStatus"
+ },
+ "SetupRequest": {
+ "properties": {
+ "email": {
+ "type": "string",
+ "title": "Email",
+ "description": "Admin email address"
},
- "clip": {
+ "display_name": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP"
+ "title": "Display Name",
+ "description": "Admin display name"
},
- "clip2": {
+ "password": {
+ "type": "string",
+ "title": "Password",
+ "description": "Admin password"
+ }
+ },
+ "type": "object",
+ "required": ["email", "password"],
+ "title": "SetupRequest",
+ "description": "Request body for initial admin setup."
+ },
+ "SetupResponse": {
+ "properties": {
+ "success": {
+ "type": "boolean",
+ "title": "Success",
+ "description": "Whether setup was successful"
+ },
+ "user": {
+ "$ref": "#/components/schemas/UserDTO",
+ "description": "Created admin user information"
+ }
+ },
+ "type": "object",
+ "required": ["success", "user"],
+ "title": "SetupResponse",
+ "description": "Response from successful admin setup."
+ },
+ "SetupStatusResponse": {
+ "properties": {
+ "setup_required": {
+ "type": "boolean",
+ "title": "Setup Required",
+ "description": "Whether initial setup is required"
+ },
+ "multiuser_enabled": {
+ "type": "boolean",
+ "title": "Multiuser Enabled",
+ "description": "Whether multiuser mode is enabled"
+ },
+ "strict_password_checking": {
+ "type": "boolean",
+ "title": "Strict Password Checking",
+ "description": "Whether strict password requirements are enforced"
+ },
+ "admin_email": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP 2"
- },
- "type": {
- "const": "sdxl_lora_collection_loader",
- "default": "sdxl_lora_collection_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Admin Email",
+ "description": "Email of the first active admin user, if any"
}
},
- "required": ["type", "id"],
- "tags": ["model"],
- "title": "Apply LoRA Collection - SDXL",
"type": "object",
- "version": "1.1.3",
- "output": {
- "$ref": "#/components/schemas/SDXLLoRALoaderOutput"
- }
+ "required": ["setup_required", "multiuser_enabled", "strict_password_checking"],
+ "title": "SetupStatusResponse",
+ "description": "Response for setup status check."
},
- "SDXLLoRALoaderInvocation": {
- "category": "model",
+ "ShowImageInvocation": {
+ "category": "image",
"class": "invocation",
"classification": "stable",
- "description": "Apply selected lora to unet and text_encoder.",
+ "description": "Displays a provided image using the OS image viewer, and passes it forward in the pipeline.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -64218,279 +69914,218 @@
"title": "Use Cache",
"type": "boolean"
},
- "lora": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "LoRA model to load",
+ "description": "The image to show",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "LoRA",
- "ui_model_base": ["sdxl"],
- "ui_model_type": ["lora"]
+ "orig_required": true
},
- "weight": {
- "default": 0.75,
- "description": "The weight at which the LoRA is applied to each model",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0.75,
- "orig_required": false,
- "title": "Weight",
- "type": "number"
+ "type": {
+ "const": "show_image",
+ "default": "show_image",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image"],
+ "title": "Show Image",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "SigLIP_Diffusers_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "unet": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/UNetField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "UNet"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "clip": {
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP 1"
+ "title": "Description",
+ "description": "Model description"
},
- "clip2": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP 2"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "type": {
- "const": "sdxl_lora_loader",
- "default": "sdxl_lora_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["lora", "model"],
- "title": "Apply LoRA - SDXL",
- "type": "object",
- "version": "1.0.5",
- "output": {
- "$ref": "#/components/schemas/SDXLLoRALoaderOutput"
- }
- },
- "SDXLLoRALoaderOutput": {
- "class": "output",
- "description": "SDXL LoRA Loader Output",
- "properties": {
- "unet": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/UNetField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "output",
- "title": "UNet",
- "ui_hidden": false
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "clip": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP 1",
- "ui_hidden": false
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "clip2": {
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "const": "siglip",
+ "title": "Type",
+ "default": "siglip"
+ },
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
+ },
+ "cpu_only": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP 2",
- "ui_hidden": false
- },
- "type": {
- "const": "sdxl_lora_loader_output",
- "default": "sdxl_lora_loader_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
}
},
- "required": ["output_meta", "unet", "clip", "clip2", "type", "type"],
- "title": "SDXLLoRALoaderOutput",
- "type": "object"
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "base",
+ "cpu_only"
+ ],
+ "title": "SigLIP_Diffusers_Config",
+ "description": "Model config for SigLIP."
},
- "SDXLModelLoaderInvocation": {
- "category": "model",
+ "SpandrelImageToImageAutoscaleInvocation": {
+ "category": "upscale",
"class": "invocation",
"classification": "stable",
- "description": "Loads an sdxl base model, outputting its submodels.",
+ "description": "Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel) until the target scale is reached.",
"node_pack": "invokeai",
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "model": {
+ "board": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
"default": null,
- "description": "SDXL Main model (UNet, VAE, CLIP1, CLIP2) to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_model_base": ["sdxl"],
- "ui_model_type": ["main"]
- },
- "type": {
- "const": "sdxl_model_loader",
- "default": "sdxl_model_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["model", "sdxl"],
- "title": "Main Model - SDXL",
- "type": "object",
- "version": "1.0.4",
- "output": {
- "$ref": "#/components/schemas/SDXLModelLoaderOutput"
- }
- },
- "SDXLModelLoaderOutput": {
- "class": "output",
- "description": "SDXL base model loader output",
- "properties": {
- "unet": {
- "$ref": "#/components/schemas/UNetField",
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "output",
- "title": "UNet",
- "ui_hidden": false
- },
- "clip": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP 1",
- "ui_hidden": false
- },
- "clip2": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP 2",
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
"ui_hidden": false
},
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
"ui_hidden": false
},
- "type": {
- "const": "sdxl_model_loader_output",
- "default": "sdxl_model_loader_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "unet", "clip", "clip2", "vae", "type", "type"],
- "title": "SDXLModelLoaderOutput",
- "type": "object"
- },
- "SDXLRefinerCompelPromptInvocation": {
- "category": "prompt",
- "class": "invocation",
- "classification": "stable",
- "description": "Parse prompt using compel package to conditioning.",
- "node_pack": "invokeai",
- "properties": {
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -64515,131 +70150,22 @@
"title": "Use Cache",
"type": "boolean"
},
- "style": {
- "default": "",
- "description": "Prompt to be parsed by Compel to create a conditioning tensor",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Style",
- "type": "string",
- "ui_component": "textarea"
- },
- "original_width": {
- "default": 1024,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Original Width",
- "type": "integer"
- },
- "original_height": {
- "default": 1024,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Original Height",
- "type": "integer"
- },
- "crop_top": {
- "default": 0,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Crop Top",
- "type": "integer"
- },
- "crop_left": {
- "default": 0,
- "description": "",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "Crop Left",
- "type": "integer"
- },
- "aesthetic_score": {
- "default": 6.0,
- "description": "The aesthetic score to apply to the conditioning tensor",
- "field_kind": "input",
- "input": "any",
- "orig_default": 6.0,
- "orig_required": false,
- "title": "Aesthetic Score",
- "type": "number"
- },
- "clip2": {
+ "image": {
"anyOf": [
{
- "$ref": "#/components/schemas/CLIPField"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
"default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
+ "description": "The input image",
"field_kind": "input",
- "input": "connection",
+ "input": "any",
"orig_required": true
},
- "type": {
- "const": "sdxl_refiner_compel_prompt",
- "default": "sdxl_refiner_compel_prompt",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["sdxl", "compel", "prompt"],
- "title": "Prompt - SDXL Refiner",
- "type": "object",
- "version": "1.1.2",
- "output": {
- "$ref": "#/components/schemas/ConditioningOutput"
- }
- },
- "SDXLRefinerModelLoaderInvocation": {
- "category": "model",
- "class": "invocation",
- "classification": "stable",
- "description": "Loads an sdxl refiner model, outputting its submodels.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "model": {
+ "image_to_image_model": {
"anyOf": [
{
"$ref": "#/components/schemas/ModelIdentifierField"
@@ -64649,77 +70175,67 @@
}
],
"default": null,
- "description": "SDXL Refiner Main Modde (UNet, VAE, CLIP2) to load",
+ "description": "Image-to-Image model",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "ui_model_base": ["sdxl-refiner"],
- "ui_model_type": ["main"]
- },
- "type": {
- "const": "sdxl_refiner_model_loader",
- "default": "sdxl_refiner_model_loader",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["model", "sdxl", "refiner"],
- "title": "Refiner Model - SDXL",
- "type": "object",
- "version": "1.0.4",
- "output": {
- "$ref": "#/components/schemas/SDXLRefinerModelLoaderOutput"
- }
- },
- "SDXLRefinerModelLoaderOutput": {
- "class": "output",
- "description": "SDXL refiner model loader output",
- "properties": {
- "unet": {
- "$ref": "#/components/schemas/UNetField",
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "output",
- "title": "UNet",
- "ui_hidden": false
- },
- "clip2": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP 2",
- "ui_hidden": false
+ "title": "Image-to-Image Model",
+ "ui_model_type": ["spandrel_image_to_image"]
},
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
+ "tile_size": {
+ "default": 512,
+ "description": "The tile size for tiled image-to-image. Set to 0 to disable tiling.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Tile Size",
+ "type": "integer"
},
"type": {
- "const": "sdxl_refiner_model_loader_output",
- "default": "sdxl_refiner_model_loader_output",
+ "const": "spandrel_image_to_image_autoscale",
+ "default": "spandrel_image_to_image_autoscale",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
+ },
+ "scale": {
+ "default": 4.0,
+ "description": "The final scale of the output image. If the model does not upscale the image, this will be ignored.",
+ "exclusiveMinimum": 0.0,
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 16.0,
+ "orig_default": 4.0,
+ "orig_required": false,
+ "title": "Scale",
+ "type": "number"
+ },
+ "fit_to_multiple_of_8": {
+ "default": false,
+ "description": "If true, the output image will be resized to the nearest multiple of 8 in both dimensions.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": false,
+ "orig_required": false,
+ "title": "Fit To Multiple Of 8",
+ "type": "boolean"
}
},
- "required": ["output_meta", "unet", "clip2", "vae", "type", "type"],
- "title": "SDXLRefinerModelLoaderOutput",
- "type": "object"
- },
- "SQLiteDirection": {
- "type": "string",
- "enum": ["ASC", "DESC"],
- "title": "SQLiteDirection"
+ "required": ["type", "id"],
+ "tags": ["upscale"],
+ "title": "Image-to-Image (Autoscale)",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "SaveImageInvocation": {
- "category": "image",
+ "SpandrelImageToImageInvocation": {
+ "category": "upscale",
"class": "invocation",
"classification": "stable",
- "description": "Saves an image. Unlike an image primitive, this invocation stores a copy of the image.",
+ "description": "Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel).",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -64772,7 +70288,7 @@
"ui_type": "IsIntermediate"
},
"use_cache": {
- "default": false,
+ "default": true,
"description": "Whether or not to use the cache",
"field_kind": "node_attribute",
"title": "Use Cache",
@@ -64788,181 +70304,538 @@
}
],
"default": null,
- "description": "The image to process",
+ "description": "The input image",
"field_kind": "input",
"input": "any",
"orig_required": true
},
+ "image_to_image_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Image-to-Image model",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Image-to-Image Model",
+ "ui_model_type": ["spandrel_image_to_image"]
+ },
+ "tile_size": {
+ "default": 512,
+ "description": "The tile size for tiled image-to-image. Set to 0 to disable tiling.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 512,
+ "orig_required": false,
+ "title": "Tile Size",
+ "type": "integer"
+ },
"type": {
- "const": "save_image",
- "default": "save_image",
+ "const": "spandrel_image_to_image",
+ "default": "spandrel_image_to_image",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["primitives", "image"],
- "title": "Save Image",
+ "tags": ["upscale"],
+ "title": "Image-to-Image",
"type": "object",
- "version": "1.2.2",
+ "version": "1.3.0",
"output": {
"$ref": "#/components/schemas/ImageOutput"
}
},
- "SaveImageToFileInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Saves an image to the gallery (like the standard Save Image node) AND additionally exports a copy\nto the filesystem with a custom filename.\n\nFilename pattern: {prefix}{uuid}{suffix}.{file_format}\n- The UUID is the same UUID used for the gallery entry, so the exported file can be matched to the gallery item.\n- The gallery entry itself always uses the plain UUID (prefix/suffix apply only to the exported file on disk).\n- Board and Metadata inputs behave exactly like the standard Save Image node.\n- The export target is restricted to (subfolders of) the InvokeAI outputs folder \u2014 absolute paths are rejected.\n\nExample: prefix=\"hero_\", suffix=\"_final\", file_format=\"png\" \u2192 \"hero__final.png\"",
- "node_pack": "invokeai",
+ "Spandrel_Checkpoint_Config": {
"properties": {
- "board": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
+ },
+ "type": {
+ "type": "string",
+ "const": "spandrel_image_to_image",
+ "title": "Type",
+ "default": "spandrel_image_to_image"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "base",
+ "type",
+ "format"
+ ],
+ "title": "Spandrel_Checkpoint_Config",
+ "description": "Model config for Spandrel Image to Image models."
+ },
+ "StarredImagesResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the delete operation"
+ },
+ "starred_images": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Starred Images",
+ "description": "The names of the images that were starred"
+ }
+ },
+ "type": "object",
+ "required": ["affected_boards", "starred_images"],
+ "title": "StarredImagesResult"
+ },
+ "StarredVideosResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the operation"
+ },
+ "starred_videos": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Starred Videos",
+ "description": "The names of the videos that were starred"
+ }
+ },
+ "type": "object",
+ "required": ["affected_boards", "starred_videos"],
+ "title": "StarredVideosResult"
+ },
+ "StarterModel": {
+ "properties": {
+ "description": {
+ "type": "string",
+ "title": "Description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source"
+ },
+ "name": {
+ "type": "string",
+ "title": "Name"
+ },
+ "base": {
+ "$ref": "#/components/schemas/BaseModelType"
+ },
+ "type": {
+ "$ref": "#/components/schemas/ModelType"
+ },
+ "format": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelFormat"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "variant": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ClipVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/FluxVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/Flux2VariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ZImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/QwenImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRAVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3VariantType"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Variant"
+ },
+ "is_installed": {
+ "type": "boolean",
+ "title": "Is Installed",
+ "default": false
+ },
+ "capabilities": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ExternalModelCapabilities"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "default_settings": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "panel_schema": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ExternalModelPanelSchema"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "previous_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Previous Names",
+ "default": []
+ },
+ "dependencies": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/components/schemas/StarterModelWithoutDependencies"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Dependencies"
+ }
+ },
+ "type": "object",
+ "required": ["description", "source", "name", "base", "type"],
+ "title": "StarterModel"
+ },
+ "StarterModelBundle": {
+ "properties": {
+ "name": {
+ "type": "string",
+ "title": "Name"
+ },
+ "models": {
+ "items": {
+ "$ref": "#/components/schemas/StarterModel"
+ },
+ "type": "array",
+ "title": "Models"
+ }
+ },
+ "type": "object",
+ "required": ["name", "models"],
+ "title": "StarterModelBundle"
+ },
+ "StarterModelResponse": {
+ "properties": {
+ "starter_models": {
+ "items": {
+ "$ref": "#/components/schemas/StarterModel"
+ },
+ "type": "array",
+ "title": "Starter Models"
+ },
+ "starter_bundles": {
+ "additionalProperties": {
+ "$ref": "#/components/schemas/StarterModelBundle"
+ },
+ "type": "object",
+ "title": "Starter Bundles"
+ }
+ },
+ "type": "object",
+ "required": ["starter_models", "starter_bundles"],
+ "title": "StarterModelResponse"
+ },
+ "StarterModelWithoutDependencies": {
+ "properties": {
+ "description": {
+ "type": "string",
+ "title": "Description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source"
+ },
+ "name": {
+ "type": "string",
+ "title": "Name"
+ },
+ "base": {
+ "$ref": "#/components/schemas/BaseModelType"
+ },
+ "type": {
+ "$ref": "#/components/schemas/ModelType"
+ },
+ "format": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelFormat"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "variant": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ClipVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/FluxVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/Flux2VariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ZImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/QwenImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRAVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3VariantType"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Variant"
+ },
+ "is_installed": {
+ "type": "boolean",
+ "title": "Is Installed",
+ "default": false
+ },
+ "capabilities": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ExternalModelCapabilities"
+ },
+ {
+ "type": "null"
+ }
+ ]
},
- "metadata": {
+ "default_settings": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": false,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ ]
},
- "image": {
+ "panel_schema": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/ExternalModelPanelSchema"
},
{
"type": "null"
}
- ],
- "default": null,
- "description": "The image to save and export",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "output_directory": {
- "default": "",
- "description": "Target subdirectory (relative to the configured InvokeAI outputs folder) for the exported file. Leave empty to use the outputs folder directly. Example: 'my-exports' \u2192 /my-exports/. Nested paths like 'exports/2026' are allowed. Absolute paths and path traversal ('..') are not allowed for security reasons. The directory is created automatically if it doesn't exist.",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Output Directory",
- "type": "string"
- },
- "prefix": {
- "default": "",
- "description": "Text prepended to the UUID in the exported filename. Example: 'portrait_' \u2192 'portrait_.png'",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Prefix",
- "type": "string"
- },
- "suffix": {
- "default": "",
- "description": "Text appended to the UUID (before the extension). Example: '_v2' \u2192 '_v2.png'",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Suffix",
- "type": "string"
+ ]
},
- "file_format": {
- "default": "png",
- "description": "File format for the exported file. PNG is lossless; JPG/WEBP are lossy and respect 'quality'.",
- "enum": ["png", "jpg", "webp"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "png",
- "orig_required": false,
- "title": "File Format",
- "type": "string"
+ "previous_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Previous Names",
+ "default": []
+ }
+ },
+ "type": "object",
+ "required": ["description", "source", "name", "base", "type"],
+ "title": "StarterModelWithoutDependencies"
+ },
+ "String2Output": {
+ "class": "output",
+ "description": "Base class for invocations that output two strings",
+ "properties": {
+ "string_1": {
+ "description": "string 1",
+ "field_kind": "output",
+ "title": "String 1",
+ "type": "string",
+ "ui_hidden": false
},
- "quality": {
- "default": 95,
- "description": "Compression quality for JPG and WEBP (1-100, higher = better quality, larger file). Ignored for PNG.",
- "field_kind": "input",
- "input": "any",
- "maximum": 100,
- "minimum": 1,
- "orig_default": 95,
- "orig_required": false,
- "title": "Quality",
- "type": "integer"
+ "string_2": {
+ "description": "string 2",
+ "field_kind": "output",
+ "title": "String 2",
+ "type": "string",
+ "ui_hidden": false
},
"type": {
- "const": "save_image_to_file",
- "default": "save_image_to_file",
+ "const": "string_2_output",
+ "default": "string_2_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["type", "id"],
- "tags": ["image", "export", "file", "save"],
- "title": "Save Image (Gallery + File Export)",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": ["output_meta", "string_1", "string_2", "type", "type"],
+ "title": "String2Output",
+ "type": "object"
},
- "ScaleLatentsInvocation": {
- "category": "latents",
+ "StringBatchInvocation": {
+ "category": "batch",
"class": "invocation",
- "classification": "stable",
- "description": "Scales latents by a given factor.",
+ "classification": "special",
+ "description": "Create a batched generation, where the workflow is executed once for each string in the batch.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -64989,81 +70862,59 @@
"title": "Use Cache",
"type": "boolean"
},
- "latents": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LatentsField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Latents tensor",
+ "batch_group_id": {
+ "default": "None",
+ "description": "The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size.",
+ "enum": ["None", "Group 1", "Group 2", "Group 3", "Group 4", "Group 5"],
"field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "input": "direct",
+ "orig_default": "None",
+ "orig_required": false,
+ "title": "Batch Group",
+ "type": "string"
},
- "scale_factor": {
+ "strings": {
"anyOf": [
{
- "exclusiveMinimum": 0,
- "type": "number"
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1,
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "The factor by which to scale",
+ "description": "The strings to batch over",
"field_kind": "input",
"input": "any",
"orig_required": true,
- "title": "Scale Factor"
- },
- "mode": {
- "default": "bilinear",
- "description": "Interpolation mode",
- "enum": ["nearest", "linear", "bilinear", "bicubic", "trilinear", "area", "nearest-exact"],
- "field_kind": "input",
- "input": "any",
- "orig_default": "bilinear",
- "orig_required": false,
- "title": "Mode",
- "type": "string"
- },
- "antialias": {
- "default": false,
- "description": "Whether or not to apply antialiasing (bilinear or bicubic only)",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Antialias",
- "type": "boolean"
+ "title": "Strings"
},
"type": {
- "const": "lscale",
- "default": "lscale",
+ "const": "string_batch",
+ "default": "string_batch",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["latents", "resize"],
- "title": "Scale Latents",
+ "tags": ["primitives", "string", "batch", "special"],
+ "title": "String Batch",
"type": "object",
- "version": "1.0.2",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/LatentsOutput"
+ "$ref": "#/components/schemas/StringOutput"
}
},
- "SchedulerInvocation": {
- "category": "latents",
+ "StringCollectionInvocation": {
+ "category": "primitives",
"class": "invocation",
"classification": "stable",
- "description": "Selects a scheduler.",
+ "description": "A collection of string primitive values",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -65090,134 +70941,67 @@
"title": "Use Cache",
"type": "boolean"
},
- "scheduler": {
- "default": "euler",
- "description": "Scheduler to use during inference",
- "enum": [
- "ddim",
- "ddpm",
- "deis",
- "deis_k",
- "lms",
- "lms_k",
- "pndm",
- "heun",
- "heun_k",
- "euler",
- "euler_k",
- "euler_a",
- "kdpm_2",
- "kdpm_2_k",
- "kdpm_2_a",
- "kdpm_2_a_k",
- "dpmpp_2s",
- "dpmpp_2s_k",
- "dpmpp_2m",
- "dpmpp_2m_k",
- "dpmpp_2m_sde",
- "dpmpp_2m_sde_k",
- "dpmpp_3m",
- "dpmpp_3m_k",
- "dpmpp_sde",
- "dpmpp_sde_k",
- "er_sde",
- "unipc",
- "unipc_k",
- "lcm",
- "tcd"
- ],
+ "collection": {
+ "default": [],
+ "description": "The collection of string values",
"field_kind": "input",
"input": "any",
- "orig_default": "euler",
+ "items": {
+ "type": "string"
+ },
+ "orig_default": [],
"orig_required": false,
- "title": "Scheduler",
- "type": "string",
- "ui_type": "SchedulerField"
+ "title": "Collection",
+ "type": "array"
},
"type": {
- "const": "scheduler",
- "default": "scheduler",
+ "const": "string_collection",
+ "default": "string_collection",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["scheduler"],
- "title": "Scheduler",
+ "tags": ["primitives", "string", "collection"],
+ "title": "String Collection Primitive",
"type": "object",
- "version": "1.0.0",
+ "version": "1.0.2",
"output": {
- "$ref": "#/components/schemas/SchedulerOutput"
+ "$ref": "#/components/schemas/StringCollectionOutput"
}
},
- "SchedulerOutput": {
+ "StringCollectionOutput": {
"class": "output",
+ "description": "Base class for nodes that output a collection of strings",
"properties": {
- "scheduler": {
- "description": "Scheduler to use during inference",
- "enum": [
- "ddim",
- "ddpm",
- "deis",
- "deis_k",
- "lms",
- "lms_k",
- "pndm",
- "heun",
- "heun_k",
- "euler",
- "euler_k",
- "euler_a",
- "kdpm_2",
- "kdpm_2_k",
- "kdpm_2_a",
- "kdpm_2_a_k",
- "dpmpp_2s",
- "dpmpp_2s_k",
- "dpmpp_2m",
- "dpmpp_2m_k",
- "dpmpp_2m_sde",
- "dpmpp_2m_sde_k",
- "dpmpp_3m",
- "dpmpp_3m_k",
- "dpmpp_sde",
- "dpmpp_sde_k",
- "er_sde",
- "unipc",
- "unipc_k",
- "lcm",
- "tcd"
- ],
+ "collection": {
+ "description": "The output strings",
"field_kind": "output",
- "title": "Scheduler",
- "type": "string",
- "ui_hidden": false,
- "ui_type": "SchedulerField"
+ "items": {
+ "type": "string"
+ },
+ "title": "Collection",
+ "type": "array",
+ "ui_hidden": false
},
"type": {
- "const": "scheduler_output",
- "default": "scheduler_output",
+ "const": "string_collection_output",
+ "default": "string_collection_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "scheduler", "type", "type"],
- "title": "SchedulerOutput",
+ "required": ["output_meta", "collection", "type", "type"],
+ "title": "StringCollectionOutput",
"type": "object"
},
- "SchedulerPredictionType": {
- "type": "string",
- "enum": ["epsilon", "v_prediction", "sample"],
- "title": "SchedulerPredictionType",
- "description": "Scheduler prediction type."
- },
- "Sd3ModelLoaderInvocation": {
- "category": "model",
+ "StringGenerator": {
+ "category": "batch",
"class": "invocation",
- "classification": "stable",
- "description": "Loads a SD3 base model, outputting its submodels.",
+ "classification": "special",
+ "description": "Generated a range of strings for use in a batched generation",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -65244,163 +71028,126 @@
"title": "Use Cache",
"type": "boolean"
},
- "model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "SD3 model (MMDiTX) to load",
+ "generator": {
+ "$ref": "#/components/schemas/StringGeneratorField",
+ "description": "The string generator.",
"field_kind": "input",
"input": "direct",
"orig_required": true,
- "ui_model_base": ["sd-3"],
- "ui_model_type": ["main"]
- },
- "t5_encoder_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "T5 tokenizer and text encoder",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "T5 Encoder",
- "ui_model_type": ["t5_encoder"]
- },
- "clip_l_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP Embed loader",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP L Encoder",
- "ui_model_type": ["clip_embed"],
- "ui_model_variant": ["large"]
- },
- "clip_g_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP-G Embed loader",
- "field_kind": "input",
- "input": "direct",
- "orig_default": null,
- "orig_required": false,
- "title": "CLIP G Encoder",
- "ui_model_type": ["clip_embed"],
- "ui_model_variant": ["gigantic"]
- },
- "vae_model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE model to load",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "VAE",
- "ui_model_base": ["sd-3"],
- "ui_model_type": ["vae"]
+ "title": "Generator Type"
},
"type": {
- "const": "sd3_model_loader",
- "default": "sd3_model_loader",
+ "const": "string_generator",
+ "default": "string_generator",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["model", "type", "id"],
- "tags": ["model", "sd3"],
- "title": "Main Model - SD3",
+ "required": ["generator", "type", "id"],
+ "tags": ["primitives", "string", "number", "batch", "special"],
+ "title": "String Generator",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/Sd3ModelLoaderOutput"
+ "$ref": "#/components/schemas/StringGeneratorOutput"
}
},
- "Sd3ModelLoaderOutput": {
+ "StringGeneratorField": {
+ "properties": {},
+ "title": "StringGeneratorField",
+ "type": "object"
+ },
+ "StringGeneratorOutput": {
"class": "output",
- "description": "SD3 base model loader output.",
+ "description": "Base class for nodes that output a collection of strings",
"properties": {
- "transformer": {
- "$ref": "#/components/schemas/TransformerField",
- "description": "Transformer",
+ "strings": {
+ "description": "The generated strings",
"field_kind": "output",
- "title": "Transformer",
+ "items": {
+ "type": "string"
+ },
+ "title": "Strings",
+ "type": "array",
"ui_hidden": false
},
- "clip_l": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP L",
- "ui_hidden": false
+ "type": {
+ "const": "string_generator_output",
+ "default": "string_generator_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "strings", "type", "type"],
+ "title": "StringGeneratorOutput",
+ "type": "object"
+ },
+ "StringInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "A string primitive value",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "clip_g": {
- "$ref": "#/components/schemas/CLIPField",
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "output",
- "title": "CLIP G",
- "ui_hidden": false
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "t5_encoder": {
- "$ref": "#/components/schemas/T5EncoderField",
- "description": "T5 tokenizer and text encoder",
- "field_kind": "output",
- "title": "T5 Encoder",
- "ui_hidden": false
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
+ "value": {
+ "default": "",
+ "description": "The string value",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "Value",
+ "type": "string",
+ "ui_component": "textarea"
},
"type": {
- "const": "sd3_model_loader_output",
- "default": "sd3_model_loader_output",
+ "const": "string",
+ "default": "string",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "transformer", "clip_l", "clip_g", "t5_encoder", "vae", "type", "type"],
- "title": "Sd3ModelLoaderOutput",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["primitives", "string"],
+ "title": "String Primitive",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/StringOutput"
+ }
},
- "Sd3TextEncoderInvocation": {
- "category": "prompt",
+ "StringJoinInvocation": {
+ "category": "strings",
"class": "invocation",
"classification": "stable",
- "description": "Encodes and preps a prompt for a SD3 image.",
+ "description": "Joins string left to string right",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -65427,93 +71174,50 @@
"title": "Use Cache",
"type": "boolean"
},
- "clip_l": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/CLIPField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "CLIP L"
- },
- "clip_g": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/CLIPField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "CLIP G"
- },
- "t5_encoder": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/T5EncoderField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "T5 tokenizer and text encoder",
+ "string_left": {
+ "default": "",
+ "description": "String Left",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
+ "input": "any",
+ "orig_default": "",
"orig_required": false,
- "title": "T5Encoder"
+ "title": "String Left",
+ "type": "string",
+ "ui_component": "textarea"
},
- "prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Text prompt to encode.",
+ "string_right": {
+ "default": "",
+ "description": "String Right",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "Prompt"
+ "orig_default": "",
+ "orig_required": false,
+ "title": "String Right",
+ "type": "string",
+ "ui_component": "textarea"
},
"type": {
- "const": "sd3_text_encoder",
- "default": "sd3_text_encoder",
+ "const": "string_join",
+ "default": "string_join",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["prompt", "conditioning", "sd3"],
- "title": "Prompt - SD3",
+ "tags": ["string", "join"],
+ "title": "String Join",
"type": "object",
"version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/SD3ConditioningOutput"
+ "$ref": "#/components/schemas/StringOutput"
}
},
- "SeamlessModeInvocation": {
- "category": "model",
+ "StringJoinThreeInvocation": {
+ "category": "strings",
"class": "invocation",
"classification": "stable",
- "description": "Applies the seamless transformation to the Model UNet and VAE.",
+ "description": "Joins string left to string middle to string right",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -65540,162 +71244,116 @@
"title": "Use Cache",
"type": "boolean"
},
- "unet": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/UNetField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "UNet"
- },
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE model to load",
+ "string_left": {
+ "default": "",
+ "description": "String Left",
"field_kind": "input",
- "input": "connection",
- "orig_default": null,
+ "input": "any",
+ "orig_default": "",
"orig_required": false,
- "title": "VAE"
+ "title": "String Left",
+ "type": "string",
+ "ui_component": "textarea"
},
- "seamless_y": {
- "default": true,
- "description": "Specify whether Y axis is seamless",
+ "string_middle": {
+ "default": "",
+ "description": "String Middle",
"field_kind": "input",
"input": "any",
- "orig_default": true,
+ "orig_default": "",
"orig_required": false,
- "title": "Seamless Y",
- "type": "boolean"
+ "title": "String Middle",
+ "type": "string",
+ "ui_component": "textarea"
},
- "seamless_x": {
- "default": true,
- "description": "Specify whether X axis is seamless",
+ "string_right": {
+ "default": "",
+ "description": "String Right",
"field_kind": "input",
"input": "any",
- "orig_default": true,
+ "orig_default": "",
"orig_required": false,
- "title": "Seamless X",
- "type": "boolean"
+ "title": "String Right",
+ "type": "string",
+ "ui_component": "textarea"
},
"type": {
- "const": "seamless",
- "default": "seamless",
+ "const": "string_join_three",
+ "default": "string_join_three",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["seamless", "model"],
- "title": "Apply Seamless - SD1.5, SDXL",
+ "tags": ["string", "join"],
+ "title": "String Join Three",
"type": "object",
- "version": "1.0.2",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/SeamlessModeOutput"
+ "$ref": "#/components/schemas/StringOutput"
}
},
- "SeamlessModeOutput": {
+ "StringOutput": {
"class": "output",
- "description": "Modified Seamless Model output",
+ "description": "Base class for nodes that output a single string",
"properties": {
- "unet": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/UNetField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
+ "value": {
+ "description": "The output string",
"field_kind": "output",
- "title": "UNet",
+ "title": "Value",
+ "type": "string",
"ui_hidden": false
},
- "vae": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/VAEField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "VAE",
+ "type": {
+ "const": "string_output",
+ "default": "string_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "value", "type", "type"],
+ "title": "StringOutput",
+ "type": "object"
+ },
+ "StringPosNegOutput": {
+ "class": "output",
+ "description": "Base class for invocations that output a positive and negative string",
+ "properties": {
+ "positive_string": {
+ "description": "Positive string",
"field_kind": "output",
- "title": "VAE",
+ "title": "Positive String",
+ "type": "string",
+ "ui_hidden": false
+ },
+ "negative_string": {
+ "description": "Negative string",
+ "field_kind": "output",
+ "title": "Negative String",
+ "type": "string",
"ui_hidden": false
},
"type": {
- "const": "seamless_output",
- "default": "seamless_output",
+ "const": "string_pos_neg_output",
+ "default": "string_pos_neg_output",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "unet", "vae", "type", "type"],
- "title": "SeamlessModeOutput",
+ "required": ["output_meta", "positive_string", "negative_string", "type", "type"],
+ "title": "StringPosNegOutput",
"type": "object"
},
- "SeedreamImageGenerationInvocation": {
- "category": "image",
+ "StringReplaceInvocation": {
+ "category": "strings",
"class": "invocation",
"classification": "stable",
- "description": "Generate images using a BytePlus Seedream model.",
+ "description": "Replaces the search string with the replace string",
"node_pack": "invokeai",
"properties": {
- "board": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/BoardField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
- },
- "metadata": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/MetadataField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -65720,208 +71378,71 @@
"title": "Use Cache",
"type": "boolean"
},
- "model": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelIdentifierField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Main model (UNet, VAE, CLIP) to load",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "ui_model_base": ["external"],
- "ui_model_format": ["external_api"],
- "ui_model_provider_id": ["seedream"],
- "ui_model_type": ["external_image_generator"]
- },
- "mode": {
- "default": "txt2img",
- "description": "Generation mode.",
- "enum": ["txt2img", "img2img", "inpaint"],
+ "string": {
+ "default": "",
+ "description": "String to work on",
"field_kind": "input",
"input": "any",
- "orig_default": "txt2img",
+ "orig_default": "",
"orig_required": false,
- "title": "Mode",
+ "title": "String",
"type": "string",
- "ui_hidden": true
- },
- "prompt": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Prompt",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Prompt"
- },
- "seed": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Seed for random number generation",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Seed"
- },
- "num_images": {
- "default": 1,
- "description": "Number of images to generate",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1,
- "orig_required": false,
- "title": "Num Images",
- "type": "integer"
- },
- "width": {
- "default": 1024,
- "description": "Width of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Width",
- "type": "integer"
- },
- "height": {
- "default": 1024,
- "description": "Height of output (px)",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 1024,
- "orig_required": false,
- "title": "Height",
- "type": "integer"
- },
- "image_size": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Image size preset (e.g. 1K, 2K, 4K)",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Image Size"
- },
- "init_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Init image for img2img/inpaint",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false
- },
- "mask_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Mask image for inpaint",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "ui_hidden": true
+ "ui_component": "textarea"
},
- "reference_images": {
- "default": [],
- "description": "Reference images",
+ "search_string": {
+ "default": "",
+ "description": "String to search for",
"field_kind": "input",
"input": "any",
- "items": {
- "$ref": "#/components/schemas/ImageField"
- },
- "orig_default": [],
+ "orig_default": "",
"orig_required": false,
- "title": "Reference Images",
- "type": "array"
+ "title": "Search String",
+ "type": "string",
+ "ui_component": "textarea"
},
- "watermark": {
- "default": false,
- "description": "Add watermark to generated images",
+ "replace_string": {
+ "default": "",
+ "description": "String to replace the search",
"field_kind": "input",
"input": "any",
- "orig_default": false,
+ "orig_default": "",
"orig_required": false,
- "title": "Watermark",
- "type": "boolean"
+ "title": "Replace String",
+ "type": "string",
+ "ui_component": "textarea"
},
- "optimize_prompt": {
+ "use_regex": {
"default": false,
- "description": "Let the model optimize the prompt before generation",
+ "description": "Use search string as a regex expression (non regex is case insensitive)",
"field_kind": "input",
"input": "any",
"orig_default": false,
"orig_required": false,
- "title": "Optimize Prompt",
+ "title": "Use Regex",
"type": "boolean"
},
"type": {
- "const": "seedream_image_generation",
- "default": "seedream_image_generation",
+ "const": "string_replace",
+ "default": "string_replace",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["external", "generation", "seedream"],
- "title": "Seedream Image Generation",
+ "tags": ["string", "replace", "regex"],
+ "title": "String Replace",
"type": "object",
- "version": "1.1.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/ImageCollectionOutput"
+ "$ref": "#/components/schemas/StringOutput"
}
},
- "SegmentAnythingInvocation": {
- "category": "segmentation",
+ "StringSplitInvocation": {
+ "category": "strings",
"class": "invocation",
"classification": "stable",
- "description": "Runs a Segment Anything Model (SAM or SAM2).",
+ "description": "Splits string into two strings, based on the first occurance of the delimiter. The delimiter will be removed from the string",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -65948,401 +71469,610 @@
"title": "Use Cache",
"type": "boolean"
},
- "model": {
- "anyOf": [
- {
- "enum": [
- "segment-anything-base",
- "segment-anything-large",
- "segment-anything-huge",
- "segment-anything-2-tiny",
- "segment-anything-2-small",
- "segment-anything-2-base",
- "segment-anything-2-large"
- ],
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The Segment Anything model to use (SAM or SAM2).",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Model"
- },
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to segment.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "bounding_boxes": {
- "anyOf": [
- {
- "items": {
- "$ref": "#/components/schemas/BoundingBoxField"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The bounding boxes to prompt the model with.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Bounding Boxes"
- },
- "point_lists": {
- "anyOf": [
- {
- "items": {
- "$ref": "#/components/schemas/SAMPointsField"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The list of point lists to prompt the model with. Each list of points represents a single object.",
- "field_kind": "input",
- "input": "any",
- "orig_default": null,
- "orig_required": false,
- "title": "Point Lists"
- },
- "apply_polygon_refinement": {
- "default": true,
- "description": "Whether to apply polygon refinement to the masks. This will smooth the edges of the masks slightly and ensure that each mask consists of a single closed polygon (before merging).",
+ "string": {
+ "default": "",
+ "description": "String to split",
"field_kind": "input",
"input": "any",
- "orig_default": true,
+ "orig_default": "",
"orig_required": false,
- "title": "Apply Polygon Refinement",
- "type": "boolean"
+ "title": "String",
+ "type": "string",
+ "ui_component": "textarea"
},
- "mask_filter": {
- "default": "all",
- "description": "The filtering to apply to the detected masks before merging them into a final output.",
- "enum": ["all", "largest", "highest_box_score"],
+ "delimiter": {
+ "default": "",
+ "description": "Delimiter to spilt with. blank will split on the first whitespace",
"field_kind": "input",
"input": "any",
- "orig_default": "all",
+ "orig_default": "",
"orig_required": false,
- "title": "Mask Filter",
+ "title": "Delimiter",
"type": "string"
},
"type": {
- "const": "segment_anything",
- "default": "segment_anything",
+ "const": "string_split",
+ "default": "string_split",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["prompt", "segmentation", "sam", "sam2"],
- "title": "Segment Anything",
+ "tags": ["string", "split"],
+ "title": "String Split",
"type": "object",
- "version": "1.3.0",
+ "version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/MaskOutput"
+ "$ref": "#/components/schemas/String2Output"
}
},
- "SessionProcessorStatus": {
+ "StringSplitNegInvocation": {
+ "category": "strings",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Splits string into two strings, inside [] goes into negative string everthing else goes into positive string. Each [ and ] character is replaced with a space",
+ "node_pack": "invokeai",
"properties": {
- "is_started": {
- "type": "boolean",
- "title": "Is Started",
- "description": "Whether the session processor is started"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "is_processing": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
"type": "boolean",
- "title": "Is Processing",
- "description": "Whether a session is being processed"
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "string": {
+ "default": "",
+ "description": "String to split",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "",
+ "orig_required": false,
+ "title": "String",
+ "type": "string",
+ "ui_component": "textarea"
+ },
+ "type": {
+ "const": "string_split_neg",
+ "default": "string_split_neg",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["string", "split", "negative"],
+ "title": "String Split Negative",
"type": "object",
- "required": ["is_started", "is_processing"],
- "title": "SessionProcessorStatus"
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/StringPosNegOutput"
+ }
},
- "SessionQueueAndProcessorStatus": {
+ "StylePresetField": {
+ "description": "A style preset primitive field",
"properties": {
- "queue": {
- "$ref": "#/components/schemas/SessionQueueStatus"
- },
- "processor": {
- "$ref": "#/components/schemas/SessionProcessorStatus"
+ "style_preset_id": {
+ "description": "The id of the style preset",
+ "title": "Style Preset Id",
+ "type": "string"
}
},
- "type": "object",
- "required": ["queue", "processor"],
- "title": "SessionQueueAndProcessorStatus",
- "description": "The overall status of session queue and processor"
+ "required": ["style_preset_id"],
+ "title": "StylePresetField",
+ "type": "object"
},
- "SessionQueueCountsByDestination": {
+ "StylePresetRecordWithImage": {
"properties": {
- "queue_id": {
- "type": "string",
- "title": "Queue Id",
- "description": "The ID of the queue"
- },
- "destination": {
+ "name": {
"type": "string",
- "title": "Destination",
- "description": "The destination of queue items included in this status"
- },
- "pending": {
- "type": "integer",
- "title": "Pending",
- "description": "Number of queue items with status 'pending' for the destination"
+ "title": "Name",
+ "description": "The name of the style preset."
},
- "in_progress": {
- "type": "integer",
- "title": "In Progress",
- "description": "Number of queue items with status 'in_progress' for the destination"
+ "preset_data": {
+ "$ref": "#/components/schemas/PresetData",
+ "description": "The preset data"
},
- "waiting": {
- "type": "integer",
- "title": "Waiting",
- "description": "Number of queue items with status 'waiting' for the destination"
+ "type": {
+ "$ref": "#/components/schemas/PresetType",
+ "description": "The type of style preset"
},
- "completed": {
- "type": "integer",
- "title": "Completed",
- "description": "Number of queue items with status 'complete' for the destination"
+ "is_public": {
+ "type": "boolean",
+ "title": "Is Public",
+ "description": "Whether the preset is visible to other users.",
+ "default": false
},
- "failed": {
- "type": "integer",
- "title": "Failed",
- "description": "Number of queue items with status 'error' for the destination"
+ "id": {
+ "type": "string",
+ "title": "Id",
+ "description": "The style preset ID."
},
- "canceled": {
- "type": "integer",
- "title": "Canceled",
- "description": "Number of queue items with status 'canceled' for the destination"
+ "user_id": {
+ "type": "string",
+ "title": "User Id",
+ "description": "The user who owns this style preset."
},
- "total": {
- "type": "integer",
- "title": "Total",
- "description": "Total number of queue items for the destination"
+ "image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Image",
+ "description": "The path for image"
}
},
"type": "object",
- "required": [
- "queue_id",
- "destination",
- "pending",
- "in_progress",
- "waiting",
- "completed",
- "failed",
- "canceled",
- "total"
+ "required": ["name", "preset_data", "type", "id", "user_id", "image"],
+ "title": "StylePresetRecordWithImage"
+ },
+ "SubModelType": {
+ "type": "string",
+ "enum": [
+ "unet",
+ "transformer",
+ "transformer_2",
+ "text_encoder",
+ "text_encoder_2",
+ "text_encoder_3",
+ "tokenizer",
+ "tokenizer_2",
+ "tokenizer_3",
+ "vae",
+ "vae_decoder",
+ "vae_encoder",
+ "scheduler",
+ "safety_checker"
],
- "title": "SessionQueueCountsByDestination"
+ "title": "SubModelType",
+ "description": "Submodel type."
},
- "SessionQueueItem": {
+ "SubmodelDefinition": {
"properties": {
- "item_id": {
- "type": "integer",
- "title": "Item Id",
- "description": "The identifier of the session queue item"
- },
- "status": {
+ "path_or_prefix": {
"type": "string",
- "enum": ["pending", "in_progress", "waiting", "completed", "failed", "canceled"],
- "title": "Status",
- "description": "The status of this queue item",
- "default": "pending"
+ "title": "Path Or Prefix"
},
- "status_sequence": {
+ "model_type": {
+ "$ref": "#/components/schemas/ModelType"
+ },
+ "variant": {
"anyOf": [
{
- "type": "integer"
+ "$ref": "#/components/schemas/ModelVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ClipVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/FluxVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/Flux2VariantType"
+ },
+ {
+ "$ref": "#/components/schemas/ZImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/QwenImageVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/WanLoRAVariantType"
+ },
+ {
+ "$ref": "#/components/schemas/Qwen3VariantType"
},
{
"type": "null"
}
],
- "title": "Status Sequence",
- "description": "A monotonically increasing version for this queue item's visible status lifecycle"
+ "title": "Variant"
+ }
+ },
+ "type": "object",
+ "required": ["path_or_prefix", "model_type"],
+ "title": "SubmodelDefinition"
+ },
+ "SubtractInvocation": {
+ "category": "math",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Subtracts two numbers",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "priority": {
- "type": "integer",
- "title": "Priority",
- "description": "The priority of this queue item",
- "default": 0
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "batch_id": {
- "type": "string",
- "title": "Batch Id",
- "description": "The ID of the batch associated with this queue item"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "origin": {
+ "a": {
+ "default": 0,
+ "description": "The first number",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "A",
+ "type": "integer"
+ },
+ "b": {
+ "default": 0,
+ "description": "The second number",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "B",
+ "type": "integer"
+ },
+ "type": {
+ "const": "sub",
+ "default": "sub",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["math", "subtract"],
+ "title": "Subtract Integers",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/IntegerOutput"
+ }
+ },
+ "T2IAdapterField": {
+ "properties": {
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The T2I-Adapter image prompt."
+ },
+ "t2i_adapter_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "The T2I-Adapter model to use."
+ },
+ "weight": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "title": "Origin",
- "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results."
+ "default": 1,
+ "description": "The weight given to the T2I-Adapter",
+ "title": "Weight"
+ },
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the T2I-Adapter is first applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "Begin Step Percent",
+ "type": "number"
+ },
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the T2I-Adapter is last applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "End Step Percent",
+ "type": "number"
+ },
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode to use",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "title": "Resize Mode",
+ "type": "string"
+ }
+ },
+ "required": ["image", "t2i_adapter_model"],
+ "title": "T2IAdapterField",
+ "type": "object"
+ },
+ "T2IAdapterInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Collects T2I-Adapter info to pass to other nodes.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "destination": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Destination",
- "description": "The origin of this queue item. This data is used by the frontend to determine how to handle results"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "session_id": {
- "type": "string",
- "title": "Session Id",
- "description": "The ID of the session associated with this queue item. The session doesn't exist in graph_executions until the queue item is executed."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "error_type": {
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Error Type",
- "description": "The error type if this queue item errored"
+ "default": null,
+ "description": "The IP-Adapter image prompt.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "error_message": {
+ "t2i_adapter_model": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Error Message",
- "description": "The error message if this queue item errored"
+ "default": null,
+ "description": "The T2I-Adapter model.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "T2I-Adapter Model",
+ "ui_model_base": ["sd-1", "sdxl"],
+ "ui_model_type": ["t2i_adapter"],
+ "ui_order": -1
},
- "error_traceback": {
+ "weight": {
"anyOf": [
{
- "type": "string"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "title": "Error Traceback",
- "description": "The error traceback if this queue item errored"
+ "default": 1,
+ "description": "The weight given to the T2I-Adapter",
+ "field_kind": "input",
+ "ge": 0,
+ "input": "any",
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Weight"
},
- "created_at": {
- "anyOf": [
- {
- "type": "string",
- "format": "date-time"
- },
- {
- "type": "string"
- }
- ],
- "title": "Created At",
- "description": "When this queue item was created"
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the T2I-Adapter is first applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Begin Step Percent",
+ "type": "number"
},
- "updated_at": {
- "anyOf": [
- {
- "type": "string",
- "format": "date-time"
- },
- {
- "type": "string"
- }
- ],
- "title": "Updated At",
- "description": "When this queue item was updated"
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the T2I-Adapter is last applied (% of total steps)",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "End Step Percent",
+ "type": "number"
},
- "started_at": {
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode applied to the T2I-Adapter input image so that it matches the target output size.",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "just_resize",
+ "orig_required": false,
+ "title": "Resize Mode",
+ "type": "string"
+ },
+ "type": {
+ "const": "t2i_adapter",
+ "default": "t2i_adapter",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["t2i_adapter", "control"],
+ "title": "T2I-Adapter - SD1.5, SDXL",
+ "type": "object",
+ "version": "1.0.4",
+ "output": {
+ "$ref": "#/components/schemas/T2IAdapterOutput"
+ }
+ },
+ "T2IAdapterMetadataField": {
+ "properties": {
+ "image": {
+ "$ref": "#/components/schemas/ImageField",
+ "description": "The control image."
+ },
+ "processed_image": {
"anyOf": [
{
- "type": "string",
- "format": "date-time"
- },
- {
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Started At",
- "description": "When this queue item was started"
+ "default": null,
+ "description": "The control image, after processing."
},
- "completed_at": {
+ "t2i_adapter_model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "The T2I-Adapter model to use."
+ },
+ "weight": {
"anyOf": [
{
- "type": "string",
- "format": "date-time"
- },
- {
- "type": "string"
+ "type": "number"
},
{
- "type": "null"
+ "items": {
+ "type": "number"
+ },
+ "type": "array"
}
],
- "title": "Completed At",
- "description": "When this queue item was completed"
+ "default": 1,
+ "description": "The weight given to the T2I-Adapter",
+ "title": "Weight"
},
- "queue_id": {
+ "begin_step_percent": {
+ "default": 0,
+ "description": "When the T2I-Adapter is first applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "Begin Step Percent",
+ "type": "number"
+ },
+ "end_step_percent": {
+ "default": 1,
+ "description": "When the T2I-Adapter is last applied (% of total steps)",
+ "maximum": 1,
+ "minimum": 0,
+ "title": "End Step Percent",
+ "type": "number"
+ },
+ "resize_mode": {
+ "default": "just_resize",
+ "description": "The resize mode to use",
+ "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "title": "Resize Mode",
+ "type": "string"
+ }
+ },
+ "required": ["image", "t2i_adapter_model"],
+ "title": "T2IAdapterMetadataField",
+ "type": "object"
+ },
+ "T2IAdapterOutput": {
+ "class": "output",
+ "properties": {
+ "t2i_adapter": {
+ "$ref": "#/components/schemas/T2IAdapterField",
+ "description": "T2I-Adapter(s) to apply",
+ "field_kind": "output",
+ "title": "T2I Adapter",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "t2i_adapter_output",
+ "default": "t2i_adapter_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "t2i_adapter", "type", "type"],
+ "title": "T2IAdapterOutput",
+ "type": "object"
+ },
+ "T2IAdapter_Diffusers_SD1_Config": {
+ "properties": {
+ "key": {
"type": "string",
- "title": "Queue Id",
- "description": "The id of the queue with which this item is associated"
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "user_id": {
+ "hash": {
"type": "string",
- "title": "User Id",
- "description": "The id of the user who created this queue item",
- "default": "system"
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "user_display_name": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "User Display Name",
- "description": "The display name of the user who created this queue item, if available"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "user_email": {
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
"type": "string"
@@ -66351,37 +72081,19 @@
"type": "null"
}
],
- "title": "User Email",
- "description": "The email of the user who created this queue item, if available"
+ "title": "Description",
+ "description": "Model description"
},
- "field_values": {
- "anyOf": [
- {
- "items": {
- "$ref": "#/components/schemas/NodeFieldValue"
- },
- "type": "array"
- },
- {
- "type": "null"
- }
- ],
- "title": "Field Values",
- "description": "The field values that were used for this queue item"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "retried_from_item_id": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "title": "Retried From Item Id",
- "description": "The item_id of the queue item that this item was retried from"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "workflow_call_id": {
+ "source_api_response": {
"anyOf": [
{
"type": "string"
@@ -66390,22 +72102,22 @@
"type": "null"
}
],
- "title": "Workflow Call Id",
- "description": "The active workflow-call relationship id when this queue item is a child execution."
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "parent_item_id": {
+ "source_url": {
"anyOf": [
{
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Parent Item Id",
- "description": "The parent queue item id when this queue item is a child workflow execution."
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "parent_session_id": {
+ "cover_image": {
"anyOf": [
{
"type": "string"
@@ -66414,97 +72126,91 @@
"type": "null"
}
],
- "title": "Parent Session Id",
- "description": "The parent session id when this queue item is a child workflow execution."
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "root_item_id": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "title": "Root Item Id",
- "description": "The root queue item id for this workflow call chain, if any."
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
},
- "workflow_call_depth": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "title": "Workflow Call Depth",
- "description": "The 1-based workflow-call depth for this queue item when it is a child execution."
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
- "session": {
- "$ref": "#/components/schemas/GraphExecutionState",
- "description": "The fully-populated session to be executed"
+ "type": {
+ "type": "string",
+ "const": "t2i_adapter",
+ "title": "Type",
+ "default": "t2i_adapter"
},
- "workflow": {
+ "default_settings": {
"anyOf": [
{
- "$ref": "#/components/schemas/WorkflowWithoutID"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "description": "The workflow associated with this queue item"
+ ]
+ },
+ "base": {
+ "type": "string",
+ "const": "sd-1",
+ "title": "Base",
+ "default": "sd-1"
}
},
"type": "object",
"required": [
- "item_id",
- "status",
- "batch_id",
- "queue_id",
- "session_id",
- "session",
- "priority",
- "session_id",
- "created_at",
- "updated_at"
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "default_settings",
+ "base"
],
- "title": "SessionQueueItem",
- "description": "Session queue item without the full graph. Used for serialization."
+ "title": "T2IAdapter_Diffusers_SD1_Config"
},
- "SessionQueueStatus": {
+ "T2IAdapter_Diffusers_SDXL_Config": {
"properties": {
- "queue_id": {
+ "key": {
"type": "string",
- "title": "Queue Id",
- "description": "The ID of the queue"
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "item_id": {
- "anyOf": [
- {
- "type": "integer"
- },
- {
- "type": "null"
- }
- ],
- "title": "Item Id",
- "description": "The current queue item id"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "batch_id": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Batch Id",
- "description": "The current queue item's batch id"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "session_id": {
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
"type": "string"
@@ -66513,93 +72219,43 @@
"type": "null"
}
],
- "title": "Session Id",
- "description": "The current queue item's session id"
- },
- "pending": {
- "type": "integer",
- "title": "Pending",
- "description": "Number of queue items with status 'pending'"
- },
- "in_progress": {
- "type": "integer",
- "title": "In Progress",
- "description": "Number of queue items with status 'in_progress'"
- },
- "waiting": {
- "type": "integer",
- "title": "Waiting",
- "description": "Number of queue items with status 'waiting'"
- },
- "completed": {
- "type": "integer",
- "title": "Completed",
- "description": "Number of queue items with status 'complete'"
- },
- "failed": {
- "type": "integer",
- "title": "Failed",
- "description": "Number of queue items with status 'error'"
+ "title": "Description",
+ "description": "Model description"
},
- "canceled": {
- "type": "integer",
- "title": "Canceled",
- "description": "Number of queue items with status 'canceled'"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "total": {
- "type": "integer",
- "title": "Total",
- "description": "Total number of queue items"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "user_pending": {
+ "source_api_response": {
"anyOf": [
{
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "User Pending",
- "description": "Number of the requesting user's queue items with status 'pending' (None for admins/global callers)"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "user_in_progress": {
+ "source_url": {
"anyOf": [
{
- "type": "integer"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "User In Progress",
- "description": "Number of the requesting user's queue items with status 'in_progress' (None for admins/global callers)"
- }
- },
- "type": "object",
- "required": [
- "queue_id",
- "item_id",
- "batch_id",
- "session_id",
- "pending",
- "in_progress",
- "waiting",
- "completed",
- "failed",
- "canceled",
- "total"
- ],
- "title": "SessionQueueStatus"
- },
- "SetupRequest": {
- "properties": {
- "email": {
- "type": "string",
- "title": "Email",
- "description": "Admin email address"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "display_name": {
+ "cover_image": {
"anyOf": [
{
"type": "string"
@@ -66608,136 +72264,87 @@
"type": "null"
}
],
- "title": "Display Name",
- "description": "Admin display name"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "password": {
+ "format": {
"type": "string",
- "title": "Password",
- "description": "Admin password"
- }
- },
- "type": "object",
- "required": ["email", "password"],
- "title": "SetupRequest",
- "description": "Request body for initial admin setup."
- },
- "SetupResponse": {
- "properties": {
- "success": {
- "type": "boolean",
- "title": "Success",
- "description": "Whether setup was successful"
- },
- "user": {
- "$ref": "#/components/schemas/UserDTO",
- "description": "Created admin user information"
- }
- },
- "type": "object",
- "required": ["success", "user"],
- "title": "SetupResponse",
- "description": "Response from successful admin setup."
- },
- "SetupStatusResponse": {
- "properties": {
- "setup_required": {
- "type": "boolean",
- "title": "Setup Required",
- "description": "Whether initial setup is required"
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
},
- "multiuser_enabled": {
- "type": "boolean",
- "title": "Multiuser Enabled",
- "description": "Whether multiuser mode is enabled"
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
- "strict_password_checking": {
- "type": "boolean",
- "title": "Strict Password Checking",
- "description": "Whether strict password requirements are enforced"
+ "type": {
+ "type": "string",
+ "const": "t2i_adapter",
+ "title": "Type",
+ "default": "t2i_adapter"
},
- "admin_email": {
+ "default_settings": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
},
{
"type": "null"
}
- ],
- "title": "Admin Email",
- "description": "Email of the first active admin user, if any"
+ ]
+ },
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
}
},
"type": "object",
- "required": ["setup_required", "multiuser_enabled", "strict_password_checking"],
- "title": "SetupStatusResponse",
- "description": "Response for setup status check."
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "default_settings",
+ "base"
+ ],
+ "title": "T2IAdapter_Diffusers_SDXL_Config"
},
- "ShowImageInvocation": {
- "category": "image",
- "class": "invocation",
- "classification": "stable",
- "description": "Displays a provided image using the OS image viewer, and passes it forward in the pipeline.",
- "node_pack": "invokeai",
+ "T5EncoderField": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "tokenizer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load tokenizer submodel"
},
- "image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The image to show",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "text_encoder": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load text_encoder submodel"
},
- "type": {
- "const": "show_image",
- "default": "show_image",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "loras": {
+ "description": "LoRAs to apply on model loading",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "Loras",
+ "type": "array"
}
},
- "required": ["type", "id"],
- "tags": ["image"],
- "title": "Show Image",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": ["tokenizer", "text_encoder", "loras"],
+ "title": "T5EncoderField",
+ "type": "object"
},
- "SigLIP_Diffusers_Config": {
+ "T5Encoder_BnBLLMint8_Config": {
"properties": {
"key": {
"type": "string",
@@ -66821,27 +72428,23 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "format": {
+ "base": {
"type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
"type": {
"type": "string",
- "const": "siglip",
+ "const": "t5_encoder",
"title": "Type",
- "default": "siglip"
+ "default": "t5_encoder"
},
- "base": {
+ "format": {
"type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
+ "const": "bnb_quantized_int8b",
+ "title": "Format",
+ "default": "bnb_quantized_int8b"
},
"cpu_only": {
"anyOf": [
@@ -66869,282 +72472,296 @@
"source_api_response",
"source_url",
"cover_image",
- "format",
- "repo_variant",
- "type",
"base",
+ "type",
+ "format",
"cpu_only"
],
- "title": "SigLIP_Diffusers_Config",
- "description": "Model config for SigLIP."
+ "title": "T5Encoder_BnBLLMint8_Config",
+ "description": "Configuration for T5 Encoder models quantized by bitsandbytes' LLM.int8."
},
- "SpandrelImageToImageAutoscaleInvocation": {
- "category": "upscale",
- "class": "invocation",
- "classification": "stable",
- "description": "Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel) until the target scale is reached.",
- "node_pack": "invokeai",
+ "T5Encoder_T5Encoder_Config": {
"properties": {
- "board": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
},
- "metadata": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "image": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The input image",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "image_to_image_model": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Image-to-Image model",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Image-to-Image Model",
- "ui_model_type": ["spandrel_image_to_image"]
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "tile_size": {
- "default": 512,
- "description": "The tile size for tiled image-to-image. Set to 0 to disable tiling.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 512,
- "orig_required": false,
- "title": "Tile Size",
- "type": "integer"
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
"type": {
- "const": "spandrel_image_to_image_autoscale",
- "default": "spandrel_image_to_image_autoscale",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "t5_encoder",
+ "title": "Type",
+ "default": "t5_encoder"
},
- "scale": {
- "default": 4.0,
- "description": "The final scale of the output image. If the model does not upscale the image, this will be ignored.",
- "exclusiveMinimum": 0.0,
- "field_kind": "input",
- "input": "any",
- "maximum": 16.0,
- "orig_default": 4.0,
- "orig_required": false,
- "title": "Scale",
- "type": "number"
+ "format": {
+ "type": "string",
+ "const": "t5_encoder",
+ "title": "Format",
+ "default": "t5_encoder"
},
- "fit_to_multiple_of_8": {
- "default": false,
- "description": "If true, the output image will be resized to the nearest multiple of 8 in both dimensions.",
- "field_kind": "input",
- "input": "any",
- "orig_default": false,
- "orig_required": false,
- "title": "Fit To Multiple Of 8",
- "type": "boolean"
+ "cpu_only": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
}
},
- "required": ["type", "id"],
- "tags": ["upscale"],
- "title": "Image-to-Image (Autoscale)",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "base",
+ "type",
+ "format",
+ "cpu_only"
+ ],
+ "title": "T5Encoder_T5Encoder_Config",
+ "description": "Configuration for T5 Encoder models in a bespoke, diffusers-like format. The model weights are expected to be in\na folder called text_encoder_2 inside the model directory, with a config file named model.safetensors.index.json."
},
- "SpandrelImageToImageInvocation": {
- "category": "upscale",
- "class": "invocation",
- "classification": "stable",
- "description": "Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel).",
- "node_pack": "invokeai",
+ "TBLR": {
"properties": {
- "board": {
+ "top": {
+ "title": "Top",
+ "type": "integer"
+ },
+ "bottom": {
+ "title": "Bottom",
+ "type": "integer"
+ },
+ "left": {
+ "title": "Left",
+ "type": "integer"
+ },
+ "right": {
+ "title": "Right",
+ "type": "integer"
+ }
+ },
+ "required": ["top", "bottom", "left", "right"],
+ "title": "TBLR",
+ "type": "object"
+ },
+ "TI_File_SD1_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/BoardField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The board to save the image to",
- "field_kind": "internal",
- "input": "direct",
- "orig_required": false,
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "metadata": {
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/MetadataField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Optional metadata to be saved with the image",
- "field_kind": "internal",
- "input": "connection",
- "orig_required": false,
- "ui_hidden": false
- },
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "image": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The input image",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "image_to_image_model": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Image-to-Image model",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Image-to-Image Model",
- "ui_model_type": ["spandrel_image_to_image"]
- },
- "tile_size": {
- "default": 512,
- "description": "The tile size for tiled image-to-image. Set to 0 to disable tiling.",
- "field_kind": "input",
- "input": "any",
- "orig_default": 512,
- "orig_required": false,
- "title": "Tile Size",
- "type": "integer"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
"type": {
- "const": "spandrel_image_to_image",
- "default": "spandrel_image_to_image",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "embedding",
+ "title": "Type",
+ "default": "embedding"
+ },
+ "format": {
+ "type": "string",
+ "const": "embedding_file",
+ "title": "Format",
+ "default": "embedding_file"
+ },
+ "base": {
+ "type": "string",
+ "const": "sd-1",
+ "title": "Base",
+ "default": "sd-1"
}
},
- "required": ["type", "id"],
- "tags": ["upscale"],
- "title": "Image-to-Image",
"type": "object",
- "version": "1.3.0",
- "output": {
- "$ref": "#/components/schemas/ImageOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "format",
+ "base"
+ ],
+ "title": "TI_File_SD1_Config"
},
- "Spandrel_Checkpoint_Config": {
+ "TI_File_SD2_Config": {
"properties": {
"key": {
"type": "string",
@@ -67228,23 +72845,23 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
- },
"type": {
"type": "string",
- "const": "spandrel_image_to_image",
+ "const": "embedding",
"title": "Type",
- "default": "spandrel_image_to_image"
+ "default": "embedding"
},
"format": {
"type": "string",
- "const": "checkpoint",
+ "const": "embedding_file",
"title": "Format",
- "default": "checkpoint"
+ "default": "embedding_file"
+ },
+ "base": {
+ "type": "string",
+ "const": "sd-2",
+ "title": "Base",
+ "default": "sd-2"
}
},
"type": "object",
@@ -67260,854 +72877,518 @@
"source_api_response",
"source_url",
"cover_image",
- "base",
"type",
- "format"
+ "format",
+ "base"
],
- "title": "Spandrel_Checkpoint_Config",
- "description": "Model config for Spandrel Image to Image models."
- },
- "StarredImagesResult": {
- "properties": {
- "affected_boards": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Affected Boards",
- "description": "The ids of boards affected by the delete operation"
- },
- "starred_images": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Starred Images",
- "description": "The names of the images that were starred"
- }
- },
- "type": "object",
- "required": ["affected_boards", "starred_images"],
- "title": "StarredImagesResult"
+ "title": "TI_File_SD2_Config"
},
- "StarterModel": {
+ "TI_File_SDXL_Config": {
"properties": {
- "description": {
+ "key": {
"type": "string",
- "title": "Description"
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "source": {
+ "hash": {
"type": "string",
- "title": "Source"
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "name": {
+ "path": {
"type": "string",
- "title": "Name"
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "base": {
- "$ref": "#/components/schemas/BaseModelType"
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "type": {
- "$ref": "#/components/schemas/ModelType"
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
},
- "format": {
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelFormat"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "title": "Description",
+ "description": "Model description"
},
- "variant": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelVariantType"
- },
- {
- "$ref": "#/components/schemas/ClipVariantType"
- },
- {
- "$ref": "#/components/schemas/FluxVariantType"
+ "type": "string"
},
{
- "$ref": "#/components/schemas/Flux2VariantType"
- },
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
+ },
+ "source_url": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/ZImageVariantType"
+ "type": "string"
},
{
- "$ref": "#/components/schemas/QwenImageVariantType"
- },
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
{
- "$ref": "#/components/schemas/Qwen3VariantType"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Variant"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "is_installed": {
- "type": "boolean",
- "title": "Is Installed",
- "default": false
+ "type": {
+ "type": "string",
+ "const": "embedding",
+ "title": "Type",
+ "default": "embedding"
},
- "capabilities": {
+ "format": {
+ "type": "string",
+ "const": "embedding_file",
+ "title": "Format",
+ "default": "embedding_file"
+ },
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
+ }
+ },
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "format",
+ "base"
+ ],
+ "title": "TI_File_SDXL_Config"
+ },
+ "TI_Folder_SD1_Config": {
+ "properties": {
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
+ },
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalModelCapabilities"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "title": "Description",
+ "description": "Model description"
},
- "default_settings": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "panel_schema": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalModelPanelSchema"
+ "type": "string"
},
{
"type": "null"
}
- ]
- },
- "previous_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Previous Names",
- "default": []
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "dependencies": {
+ "cover_image": {
"anyOf": [
{
- "items": {
- "$ref": "#/components/schemas/StarterModelWithoutDependencies"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Dependencies"
- }
- },
- "type": "object",
- "required": ["description", "source", "name", "base", "type"],
- "title": "StarterModel"
- },
- "StarterModelBundle": {
- "properties": {
- "name": {
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "type": {
"type": "string",
- "title": "Name"
+ "const": "embedding",
+ "title": "Type",
+ "default": "embedding"
},
- "models": {
- "items": {
- "$ref": "#/components/schemas/StarterModel"
- },
- "type": "array",
- "title": "Models"
- }
- },
- "type": "object",
- "required": ["name", "models"],
- "title": "StarterModelBundle"
- },
- "StarterModelResponse": {
- "properties": {
- "starter_models": {
- "items": {
- "$ref": "#/components/schemas/StarterModel"
- },
- "type": "array",
- "title": "Starter Models"
+ "format": {
+ "type": "string",
+ "const": "embedding_folder",
+ "title": "Format",
+ "default": "embedding_folder"
},
- "starter_bundles": {
- "additionalProperties": {
- "$ref": "#/components/schemas/StarterModelBundle"
- },
- "type": "object",
- "title": "Starter Bundles"
+ "base": {
+ "type": "string",
+ "const": "sd-1",
+ "title": "Base",
+ "default": "sd-1"
}
},
"type": "object",
- "required": ["starter_models", "starter_bundles"],
- "title": "StarterModelResponse"
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "format",
+ "base"
+ ],
+ "title": "TI_Folder_SD1_Config"
},
- "StarterModelWithoutDependencies": {
+ "TI_Folder_SD2_Config": {
"properties": {
- "description": {
+ "key": {
"type": "string",
- "title": "Description"
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "source": {
+ "hash": {
"type": "string",
- "title": "Source"
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "name": {
+ "path": {
"type": "string",
- "title": "Name"
- },
- "base": {
- "$ref": "#/components/schemas/BaseModelType"
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "type": {
- "$ref": "#/components/schemas/ModelType"
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "format": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ModelFormat"
- },
- {
- "type": "null"
- }
- ]
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
},
- "variant": {
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelVariantType"
- },
- {
- "$ref": "#/components/schemas/ClipVariantType"
- },
- {
- "$ref": "#/components/schemas/FluxVariantType"
- },
- {
- "$ref": "#/components/schemas/Flux2VariantType"
- },
- {
- "$ref": "#/components/schemas/ZImageVariantType"
- },
- {
- "$ref": "#/components/schemas/QwenImageVariantType"
- },
- {
- "$ref": "#/components/schemas/Qwen3VariantType"
+ "type": "string"
},
{
"type": "null"
}
],
- "title": "Variant"
+ "title": "Description",
+ "description": "Model description"
+ },
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "is_installed": {
- "type": "boolean",
- "title": "Is Installed",
- "default": false
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "capabilities": {
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalModelCapabilities"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "default_settings": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalApiModelDefaultSettings"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "panel_schema": {
+ "cover_image": {
"anyOf": [
{
- "$ref": "#/components/schemas/ExternalModelPanelSchema"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "previous_names": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Previous Names",
- "default": []
- }
- },
- "type": "object",
- "required": ["description", "source", "name", "base", "type"],
- "title": "StarterModelWithoutDependencies"
- },
- "String2Output": {
- "class": "output",
- "description": "Base class for invocations that output two strings",
- "properties": {
- "string_1": {
- "description": "string 1",
- "field_kind": "output",
- "title": "String 1",
+ "type": {
"type": "string",
- "ui_hidden": false
+ "const": "embedding",
+ "title": "Type",
+ "default": "embedding"
},
- "string_2": {
- "description": "string 2",
- "field_kind": "output",
- "title": "String 2",
+ "format": {
"type": "string",
- "ui_hidden": false
+ "const": "embedding_folder",
+ "title": "Format",
+ "default": "embedding_folder"
},
- "type": {
- "const": "string_2_output",
- "default": "string_2_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "base": {
+ "type": "string",
+ "const": "sd-2",
+ "title": "Base",
+ "default": "sd-2"
}
},
- "required": ["output_meta", "string_1", "string_2", "type", "type"],
- "title": "String2Output",
- "type": "object"
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "format",
+ "base"
+ ],
+ "title": "TI_Folder_SD2_Config"
},
- "StringBatchInvocation": {
- "category": "batch",
- "class": "invocation",
- "classification": "special",
- "description": "Create a batched generation, where the workflow is executed once for each string in the batch.",
- "node_pack": "invokeai",
+ "TI_Folder_SDXL_Config": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "batch_group_id": {
- "default": "None",
- "description": "The ID of this batch node's group. If provided, all batch nodes in with the same ID will be 'zipped' before execution, and all nodes' collections must be of the same size.",
- "enum": ["None", "Group 1", "Group 2", "Group 3", "Group 4", "Group 5"],
- "field_kind": "input",
- "input": "direct",
- "orig_default": "None",
- "orig_required": false,
- "title": "Batch Group",
- "type": "string"
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "strings": {
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "items": {
- "type": "string"
- },
- "minItems": 1,
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The strings to batch over",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Strings"
- },
- "type": {
- "const": "string_batch",
- "default": "string_batch",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["primitives", "string", "batch", "special"],
- "title": "String Batch",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/StringOutput"
- }
- },
- "StringCollectionInvocation": {
- "category": "primitives",
- "class": "invocation",
- "classification": "stable",
- "description": "A collection of string primitive values",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "collection": {
- "default": [],
- "description": "The collection of string values",
- "field_kind": "input",
- "input": "any",
- "items": {
- "type": "string"
- },
- "orig_default": [],
- "orig_required": false,
- "title": "Collection",
- "type": "array"
- },
- "type": {
- "const": "string_collection",
- "default": "string_collection",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["primitives", "string", "collection"],
- "title": "String Collection Primitive",
- "type": "object",
- "version": "1.0.2",
- "output": {
- "$ref": "#/components/schemas/StringCollectionOutput"
- }
- },
- "StringCollectionOutput": {
- "class": "output",
- "description": "Base class for nodes that output a collection of strings",
- "properties": {
- "collection": {
- "description": "The output strings",
- "field_kind": "output",
- "items": {
- "type": "string"
- },
- "title": "Collection",
- "type": "array",
- "ui_hidden": false
- },
- "type": {
- "const": "string_collection_output",
- "default": "string_collection_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "collection", "type", "type"],
- "title": "StringCollectionOutput",
- "type": "object"
- },
- "StringGenerator": {
- "category": "batch",
- "class": "invocation",
- "classification": "special",
- "description": "Generated a range of strings for use in a batched generation",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "generator": {
- "$ref": "#/components/schemas/StringGeneratorField",
- "description": "The string generator.",
- "field_kind": "input",
- "input": "direct",
- "orig_required": true,
- "title": "Generator Type"
- },
- "type": {
- "const": "string_generator",
- "default": "string_generator",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["generator", "type", "id"],
- "tags": ["primitives", "string", "number", "batch", "special"],
- "title": "String Generator",
- "type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/StringGeneratorOutput"
- }
- },
- "StringGeneratorField": {
- "properties": {},
- "title": "StringGeneratorField",
- "type": "object"
- },
- "StringGeneratorOutput": {
- "class": "output",
- "description": "Base class for nodes that output a collection of strings",
- "properties": {
- "strings": {
- "description": "The generated strings",
- "field_kind": "output",
- "items": {
- "type": "string"
- },
- "title": "Strings",
- "type": "array",
- "ui_hidden": false
- },
- "type": {
- "const": "string_generator_output",
- "default": "string_generator_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "strings", "type", "type"],
- "title": "StringGeneratorOutput",
- "type": "object"
- },
- "StringInvocation": {
- "category": "primitives",
- "class": "invocation",
- "classification": "stable",
- "description": "A string primitive value",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "value": {
- "default": "",
- "description": "The string value",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Value",
- "type": "string",
- "ui_component": "textarea"
- },
- "type": {
- "const": "string",
- "default": "string",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["primitives", "string"],
- "title": "String Primitive",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/StringOutput"
- }
- },
- "StringJoinInvocation": {
- "category": "strings",
- "class": "invocation",
- "classification": "stable",
- "description": "Joins string left to string right",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "string_left": {
- "default": "",
- "description": "String Left",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "String Left",
- "type": "string",
- "ui_component": "textarea"
+ "title": "Description",
+ "description": "Model description"
},
- "string_right": {
- "default": "",
- "description": "String Right",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "String Right",
+ "source": {
"type": "string",
- "ui_component": "textarea"
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "type": {
- "const": "string_join",
- "default": "string_join",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["string", "join"],
- "title": "String Join",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/StringOutput"
- }
- },
- "StringJoinThreeInvocation": {
- "category": "strings",
- "class": "invocation",
- "classification": "stable",
- "description": "Joins string left to string middle to string right",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "string_left": {
- "default": "",
- "description": "String Left",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "String Left",
- "type": "string",
- "ui_component": "textarea"
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "string_middle": {
- "default": "",
- "description": "String Middle",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "String Middle",
+ "type": {
"type": "string",
- "ui_component": "textarea"
+ "const": "embedding",
+ "title": "Type",
+ "default": "embedding"
},
- "string_right": {
- "default": "",
- "description": "String Right",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "String Right",
+ "format": {
"type": "string",
- "ui_component": "textarea"
+ "const": "embedding_folder",
+ "title": "Format",
+ "default": "embedding_folder"
},
- "type": {
- "const": "string_join_three",
- "default": "string_join_three",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["string", "join"],
- "title": "String Join Three",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/StringOutput"
- }
- },
- "StringOutput": {
- "class": "output",
- "description": "Base class for nodes that output a single string",
- "properties": {
- "value": {
- "description": "The output string",
- "field_kind": "output",
- "title": "Value",
+ "base": {
"type": "string",
- "ui_hidden": false
- },
- "type": {
- "const": "string_output",
- "default": "string_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
}
},
- "required": ["output_meta", "value", "type", "type"],
- "title": "StringOutput",
- "type": "object"
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "type",
+ "format",
+ "base"
+ ],
+ "title": "TI_Folder_SDXL_Config"
},
- "StringPosNegOutput": {
- "class": "output",
- "description": "Base class for invocations that output a positive and negative string",
+ "TensorField": {
+ "description": "A tensor primitive field.",
"properties": {
- "positive_string": {
- "description": "Positive string",
- "field_kind": "output",
- "title": "Positive String",
- "type": "string",
- "ui_hidden": false
- },
- "negative_string": {
- "description": "Negative string",
- "field_kind": "output",
- "title": "Negative String",
- "type": "string",
- "ui_hidden": false
- },
- "type": {
- "const": "string_pos_neg_output",
- "default": "string_pos_neg_output",
- "field_kind": "node_attribute",
- "title": "type",
+ "tensor_name": {
+ "description": "The name of a tensor.",
+ "title": "Tensor Name",
"type": "string"
}
},
- "required": ["output_meta", "positive_string", "negative_string", "type", "type"],
- "title": "StringPosNegOutput",
+ "required": ["tensor_name"],
+ "title": "TensorField",
"type": "object"
},
- "StringReplaceInvocation": {
- "category": "strings",
+ "TextLLMInvocation": {
+ "category": "llm",
"class": "invocation",
- "classification": "stable",
- "description": "Replaces the search string with the replace string",
+ "classification": "beta",
+ "description": "Run a text language model to generate or expand text (e.g. for prompt expansion).",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -68134,239 +73415,147 @@
"title": "Use Cache",
"type": "boolean"
},
- "string": {
+ "prompt": {
"default": "",
- "description": "String to work on",
+ "description": "Input text prompt.",
"field_kind": "input",
"input": "any",
"orig_default": "",
"orig_required": false,
- "title": "String",
+ "title": "Prompt",
"type": "string",
"ui_component": "textarea"
},
- "search_string": {
- "default": "",
- "description": "String to search for",
+ "system_prompt": {
+ "default": "You are an expert prompt writer for AI image generation. Given a brief description, expand it into a detailed, vivid prompt suitable for generating high-quality images. Only output the expanded prompt, nothing else.",
+ "description": "System prompt that guides the model's behavior.",
"field_kind": "input",
"input": "any",
- "orig_default": "",
+ "orig_default": "You are an expert prompt writer for AI image generation. Given a brief description, expand it into a detailed, vivid prompt suitable for generating high-quality images. Only output the expanded prompt, nothing else.",
"orig_required": false,
- "title": "Search String",
+ "title": "System Prompt",
"type": "string",
"ui_component": "textarea"
},
- "replace_string": {
- "default": "",
- "description": "String to replace the search",
+ "text_llm_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The text language model to use for text generation",
"field_kind": "input",
"input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Replace String",
- "type": "string",
- "ui_component": "textarea"
+ "orig_required": true,
+ "title": "Text LLM Model",
+ "ui_model_type": ["text_llm"]
},
- "use_regex": {
- "default": false,
- "description": "Use search string as a regex expression (non regex is case insensitive)",
+ "max_tokens": {
+ "default": 300,
+ "description": "Maximum number of tokens to generate.",
"field_kind": "input",
"input": "any",
- "orig_default": false,
+ "maximum": 2048,
+ "minimum": 1,
+ "orig_default": 300,
"orig_required": false,
- "title": "Use Regex",
- "type": "boolean"
+ "title": "Max Tokens",
+ "type": "integer"
},
"type": {
- "const": "string_replace",
- "default": "string_replace",
+ "const": "text_llm",
+ "default": "text_llm",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["string", "replace", "regex"],
- "title": "String Replace",
+ "tags": ["llm", "text", "prompt"],
+ "title": "Text LLM",
"type": "object",
- "version": "1.0.1",
+ "version": "1.0.0",
"output": {
"$ref": "#/components/schemas/StringOutput"
}
},
- "StringSplitInvocation": {
- "category": "strings",
- "class": "invocation",
- "classification": "stable",
- "description": "Splits string into two strings, based on the first occurance of the delimiter. The delimiter will be removed from the string",
- "node_pack": "invokeai",
+ "TextLLM_Diffusers_Config": {
"properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "string": {
- "default": "",
- "description": "String to split",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "String",
+ "key": {
"type": "string",
- "ui_component": "textarea"
- },
- "delimiter": {
- "default": "",
- "description": "Delimiter to spilt with. blank will split on the first whitespace",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Delimiter",
- "type": "string"
- },
- "type": {
- "const": "string_split",
- "default": "string_split",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["string", "split"],
- "title": "String Split",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/String2Output"
- }
- },
- "StringSplitNegInvocation": {
- "category": "strings",
- "class": "invocation",
- "classification": "stable",
- "description": "Splits string into two strings, inside [] goes into negative string everthing else goes into positive string. Each [ and ] character is replaced with a space",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "string": {
- "default": "",
- "description": "String to split",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "String",
+ "path": {
"type": "string",
- "ui_component": "textarea"
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "type": {
- "const": "string_split_neg",
- "default": "string_split_neg",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["string", "split", "negative"],
- "title": "String Split Negative",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/StringPosNegOutput"
- }
- },
- "StylePresetField": {
- "description": "A style preset primitive field",
- "properties": {
- "style_preset_id": {
- "description": "The id of the style preset",
- "title": "Style Preset Id",
- "type": "string"
- }
- },
- "required": ["style_preset_id"],
- "title": "StylePresetField",
- "type": "object"
- },
- "StylePresetRecordWithImage": {
- "properties": {
"name": {
"type": "string",
"title": "Name",
- "description": "The name of the style preset."
+ "description": "Name of the model."
},
- "preset_data": {
- "$ref": "#/components/schemas/PresetData",
- "description": "The preset data"
+ "description": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Description",
+ "description": "Model description"
},
- "type": {
- "$ref": "#/components/schemas/PresetType",
- "description": "The type of style preset"
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "is_public": {
- "type": "boolean",
- "title": "Is Public",
- "description": "Whether the preset is visible to other users.",
- "default": false
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
},
- "id": {
- "type": "string",
- "title": "Id",
- "description": "The style preset ID."
+ "source_api_response": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "user_id": {
- "type": "string",
- "title": "User Id",
- "description": "The user who owns this style preset."
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "image": {
+ "cover_image": {
"anyOf": [
{
"type": "string"
@@ -68375,82 +73564,86 @@
"type": "null"
}
],
- "title": "Image",
- "description": "The path for image"
- }
- },
- "type": "object",
- "required": ["name", "preset_data", "type", "id", "user_id", "image"],
- "title": "StylePresetRecordWithImage"
- },
- "SubModelType": {
- "type": "string",
- "enum": [
- "unet",
- "transformer",
- "text_encoder",
- "text_encoder_2",
- "text_encoder_3",
- "tokenizer",
- "tokenizer_2",
- "tokenizer_3",
- "vae",
- "vae_decoder",
- "vae_encoder",
- "scheduler",
- "safety_checker"
- ],
- "title": "SubModelType",
- "description": "Submodel type."
- },
- "SubmodelDefinition": {
- "properties": {
- "path_or_prefix": {
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "format": {
"type": "string",
- "title": "Path Or Prefix"
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
},
- "model_type": {
- "$ref": "#/components/schemas/ModelType"
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "const": "text_llm",
+ "title": "Type",
+ "default": "text_llm"
+ },
+ "base": {
+ "type": "string",
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
- "variant": {
+ "cpu_only": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelVariantType"
- },
- {
- "$ref": "#/components/schemas/ClipVariantType"
- },
- {
- "$ref": "#/components/schemas/FluxVariantType"
- },
- {
- "$ref": "#/components/schemas/Flux2VariantType"
- },
- {
- "$ref": "#/components/schemas/ZImageVariantType"
- },
- {
- "$ref": "#/components/schemas/QwenImageVariantType"
- },
- {
- "$ref": "#/components/schemas/Qwen3VariantType"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "title": "Variant"
+ "title": "Cpu Only",
+ "description": "Whether this model should run on CPU only"
}
},
"type": "object",
- "required": ["path_or_prefix", "model_type"],
- "title": "SubmodelDefinition"
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "base",
+ "cpu_only"
+ ],
+ "title": "TextLLM_Diffusers_Config",
+ "description": "Model config for text-only causal language models (e.g. Llama, Phi, Qwen, Mistral)."
},
- "SubtractInvocation": {
- "category": "math",
+ "Tile": {
+ "properties": {
+ "coords": {
+ "$ref": "#/components/schemas/TBLR",
+ "description": "The coordinates of this tile relative to its parent image."
+ },
+ "overlap": {
+ "$ref": "#/components/schemas/TBLR",
+ "description": "The amount of overlap with adjacent tiles on each side of this tile."
+ }
+ },
+ "required": ["coords", "overlap"],
+ "title": "Tile",
+ "type": "object"
+ },
+ "TileToPropertiesInvocation": {
+ "category": "tiles",
"class": "invocation",
"classification": "stable",
- "description": "Subtracts two numbers",
+ "description": "Split a Tile into its individual properties.",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -68477,102 +73670,155 @@
"title": "Use Cache",
"type": "boolean"
},
- "a": {
- "default": 0,
- "description": "The first number",
- "field_kind": "input",
- "input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "A",
- "type": "integer"
- },
- "b": {
- "default": 0,
- "description": "The second number",
+ "tile": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/Tile"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The tile to split into properties.",
"field_kind": "input",
"input": "any",
- "orig_default": 0,
- "orig_required": false,
- "title": "B",
- "type": "integer"
+ "orig_required": true
},
"type": {
- "const": "sub",
- "default": "sub",
+ "const": "tile_to_properties",
+ "default": "tile_to_properties",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["math", "subtract"],
- "title": "Subtract Integers",
+ "tags": ["tiles"],
+ "title": "Tile to Properties",
"type": "object",
"version": "1.0.1",
"output": {
- "$ref": "#/components/schemas/IntegerOutput"
+ "$ref": "#/components/schemas/TileToPropertiesOutput"
}
},
- "T2IAdapterField": {
+ "TileToPropertiesOutput": {
+ "class": "output",
"properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The T2I-Adapter image prompt."
+ "coords_left": {
+ "description": "Left coordinate of the tile relative to its parent image.",
+ "field_kind": "output",
+ "title": "Coords Left",
+ "type": "integer",
+ "ui_hidden": false
},
- "t2i_adapter_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "The T2I-Adapter model to use."
+ "coords_right": {
+ "description": "Right coordinate of the tile relative to its parent image.",
+ "field_kind": "output",
+ "title": "Coords Right",
+ "type": "integer",
+ "ui_hidden": false
},
- "weight": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "items": {
- "type": "number"
- },
- "type": "array"
- }
- ],
- "default": 1,
- "description": "The weight given to the T2I-Adapter",
- "title": "Weight"
+ "coords_top": {
+ "description": "Top coordinate of the tile relative to its parent image.",
+ "field_kind": "output",
+ "title": "Coords Top",
+ "type": "integer",
+ "ui_hidden": false
},
- "begin_step_percent": {
- "default": 0,
- "description": "When the T2I-Adapter is first applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "Begin Step Percent",
- "type": "number"
+ "coords_bottom": {
+ "description": "Bottom coordinate of the tile relative to its parent image.",
+ "field_kind": "output",
+ "title": "Coords Bottom",
+ "type": "integer",
+ "ui_hidden": false
},
- "end_step_percent": {
- "default": 1,
- "description": "When the T2I-Adapter is last applied (% of total steps)",
- "maximum": 1,
- "minimum": 0,
- "title": "End Step Percent",
- "type": "number"
+ "width": {
+ "description": "The width of the tile. Equal to coords_right - coords_left.",
+ "field_kind": "output",
+ "title": "Width",
+ "type": "integer",
+ "ui_hidden": false
},
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode to use",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
- "title": "Resize Mode",
+ "height": {
+ "description": "The height of the tile. Equal to coords_bottom - coords_top.",
+ "field_kind": "output",
+ "title": "Height",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "overlap_top": {
+ "description": "Overlap between this tile and its top neighbor.",
+ "field_kind": "output",
+ "title": "Overlap Top",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "overlap_bottom": {
+ "description": "Overlap between this tile and its bottom neighbor.",
+ "field_kind": "output",
+ "title": "Overlap Bottom",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "overlap_left": {
+ "description": "Overlap between this tile and its left neighbor.",
+ "field_kind": "output",
+ "title": "Overlap Left",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "overlap_right": {
+ "description": "Overlap between this tile and its right neighbor.",
+ "field_kind": "output",
+ "title": "Overlap Right",
+ "type": "integer",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "tile_to_properties_output",
+ "default": "tile_to_properties_output",
+ "field_kind": "node_attribute",
+ "title": "type",
"type": "string"
}
},
- "required": ["image", "t2i_adapter_model"],
- "title": "T2IAdapterField",
+ "required": [
+ "output_meta",
+ "coords_left",
+ "coords_right",
+ "coords_top",
+ "coords_bottom",
+ "width",
+ "height",
+ "overlap_top",
+ "overlap_bottom",
+ "overlap_left",
+ "overlap_right",
+ "type",
+ "type"
+ ],
+ "title": "TileToPropertiesOutput",
"type": "object"
},
- "T2IAdapterInvocation": {
- "category": "conditioning",
+ "TileWithImage": {
+ "properties": {
+ "tile": {
+ "$ref": "#/components/schemas/Tile"
+ },
+ "image": {
+ "$ref": "#/components/schemas/ImageField"
+ }
+ },
+ "required": ["tile", "image"],
+ "title": "TileWithImage",
+ "type": "object"
+ },
+ "TiledMultiDiffusionDenoiseLatents": {
+ "category": "latents",
"class": "invocation",
"classification": "stable",
- "description": "Collects T2I-Adapter info to pass to other nodes.",
+ "description": "Tiled Multi-Diffusion denoising.\n\nThis node handles automatically tiling the input image, and is primarily intended for global refinement of images\nin tiled upscaling workflows. Future Multi-Diffusion nodes should allow the user to specify custom regions with\ndifferent parameters for each region to harness the full power of Multi-Diffusion.\n\nThis node has a similar interface to the `DenoiseLatents` node, but it has a reduced feature set (no IP-Adapter,\nT2I-Adapter, masking, etc.).",
"node_pack": "invokeai",
"properties": {
"id": {
@@ -68599,136 +73845,116 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "positive_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "$ref": "#/components/schemas/ConditioningField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The IP-Adapter image prompt.",
+ "description": "Positive conditioning tensor",
"field_kind": "input",
- "input": "any",
+ "input": "connection",
"orig_required": true
},
- "t2i_adapter_model": {
+ "negative_conditioning": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/ConditioningField"
},
{
"type": "null"
}
],
"default": null,
- "description": "The T2I-Adapter model.",
+ "description": "Negative conditioning tensor",
"field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "T2I-Adapter Model",
- "ui_model_base": ["sd-1", "sdxl"],
- "ui_model_type": ["t2i_adapter"],
- "ui_order": -1
+ "input": "connection",
+ "orig_required": true
},
- "weight": {
+ "noise": {
"anyOf": [
{
- "type": "number"
+ "$ref": "#/components/schemas/LatentsField"
},
{
- "items": {
- "type": "number"
- },
- "type": "array"
+ "type": "null"
}
],
- "default": 1,
- "description": "The weight given to the T2I-Adapter",
+ "default": null,
+ "description": "Noise tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "tile_height": {
+ "default": 1024,
+ "description": "Height of the tiles in image space.",
+ "exclusiveMinimum": 0,
"field_kind": "input",
- "ge": 0,
"input": "any",
- "orig_default": 1,
+ "multipleOf": 8,
+ "orig_default": 1024,
"orig_required": false,
- "title": "Weight"
+ "title": "Tile Height",
+ "type": "integer"
},
- "begin_step_percent": {
- "default": 0,
- "description": "When the T2I-Adapter is first applied (% of total steps)",
+ "tile_width": {
+ "default": 1024,
+ "description": "Width of the tiles in image space.",
+ "exclusiveMinimum": 0,
"field_kind": "input",
"input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0,
+ "multipleOf": 8,
+ "orig_default": 1024,
"orig_required": false,
- "title": "Begin Step Percent",
- "type": "number"
+ "title": "Tile Width",
+ "type": "integer"
},
- "end_step_percent": {
- "default": 1,
- "description": "When the T2I-Adapter is last applied (% of total steps)",
+ "tile_overlap": {
+ "default": 32,
+ "description": "The overlap between adjacent tiles in pixel space. (Of course, tile merging is applied in latent space.) Tiles will be cropped during merging (if necessary) to ensure that they overlap by exactly this amount.",
+ "exclusiveMinimum": 0,
"field_kind": "input",
"input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1,
+ "multipleOf": 8,
+ "orig_default": 32,
"orig_required": false,
- "title": "End Step Percent",
- "type": "number"
+ "title": "Tile Overlap",
+ "type": "integer"
},
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode applied to the T2I-Adapter input image so that it matches the target output size.",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
+ "steps": {
+ "default": 18,
+ "description": "Number of steps to run",
+ "exclusiveMinimum": 0,
"field_kind": "input",
"input": "any",
- "orig_default": "just_resize",
+ "orig_default": 18,
"orig_required": false,
- "title": "Resize Mode",
- "type": "string"
- },
- "type": {
- "const": "t2i_adapter",
- "default": "t2i_adapter",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["t2i_adapter", "control"],
- "title": "T2I-Adapter - SD1.5, SDXL",
- "type": "object",
- "version": "1.0.4",
- "output": {
- "$ref": "#/components/schemas/T2IAdapterOutput"
- }
- },
- "T2IAdapterMetadataField": {
- "properties": {
- "image": {
- "$ref": "#/components/schemas/ImageField",
- "description": "The control image."
- },
- "processed_image": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ImageField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The control image, after processing."
- },
- "t2i_adapter_model": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "The T2I-Adapter model to use."
+ "title": "Steps",
+ "type": "integer"
},
- "weight": {
+ "cfg_scale": {
"anyOf": [
{
"type": "number"
@@ -68740,133 +73966,194 @@
"type": "array"
}
],
- "default": 1,
- "description": "The weight given to the T2I-Adapter",
- "title": "Weight"
+ "default": 6.0,
+ "description": "Classifier-Free Guidance scale",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 6.0,
+ "orig_required": false,
+ "title": "CFG Scale"
},
- "begin_step_percent": {
- "default": 0,
- "description": "When the T2I-Adapter is first applied (% of total steps)",
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
"maximum": 1,
"minimum": 0,
- "title": "Begin Step Percent",
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Denoising Start",
"type": "number"
},
- "end_step_percent": {
- "default": 1,
- "description": "When the T2I-Adapter is last applied (% of total steps)",
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
"maximum": 1,
"minimum": 0,
- "title": "End Step Percent",
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
"type": "number"
},
- "resize_mode": {
- "default": "just_resize",
- "description": "The resize mode to use",
- "enum": ["just_resize", "crop_resize", "fill_resize", "just_resize_simple"],
- "title": "Resize Mode",
- "type": "string"
- }
- },
- "required": ["image", "t2i_adapter_model"],
- "title": "T2IAdapterMetadataField",
- "type": "object"
- },
- "T2IAdapterOutput": {
- "class": "output",
- "properties": {
- "t2i_adapter": {
- "$ref": "#/components/schemas/T2IAdapterField",
- "description": "T2I-Adapter(s) to apply",
- "field_kind": "output",
- "title": "T2I Adapter",
- "ui_hidden": false
- },
- "type": {
- "const": "t2i_adapter_output",
- "default": "t2i_adapter_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "t2i_adapter", "type", "type"],
- "title": "T2IAdapterOutput",
- "type": "object"
- },
- "T2IAdapter_Diffusers_SD1_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
+ "scheduler": {
+ "default": "euler",
+ "description": "Scheduler to use during inference",
+ "enum": [
+ "ddim",
+ "ddpm",
+ "deis",
+ "deis_k",
+ "lms",
+ "lms_k",
+ "pndm",
+ "heun",
+ "heun_k",
+ "euler",
+ "euler_k",
+ "euler_a",
+ "kdpm_2",
+ "kdpm_2_k",
+ "kdpm_2_a",
+ "kdpm_2_a_k",
+ "dpmpp_2s",
+ "dpmpp_2s_k",
+ "dpmpp_2m",
+ "dpmpp_2m_k",
+ "dpmpp_2m_sde",
+ "dpmpp_2m_sde_k",
+ "dpmpp_3m",
+ "dpmpp_3m_k",
+ "dpmpp_sde",
+ "dpmpp_sde_k",
+ "er_sde",
+ "unipc",
+ "unipc_k",
+ "lcm",
+ "tcd"
+ ],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "euler",
+ "orig_required": false,
+ "title": "Scheduler",
"type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "ui_type": "SchedulerField"
},
- "description": {
+ "unet": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/UNetField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "default": null,
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "UNet"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "cfg_rescale_multiplier": {
+ "default": 0,
+ "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
+ "exclusiveMaximum": 1,
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "CFG Rescale Multiplier",
+ "type": "number"
},
- "source_api_response": {
+ "control": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ControlField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/ControlField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Control"
},
- "source_url": {
+ "type": {
+ "const": "tiled_multi_diffusion_denoise_latents",
+ "default": "tiled_multi_diffusion_denoise_latents",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["upscale", "denoise"],
+ "title": "Tiled Multi-Diffusion Denoise - SD1.5, SDXL",
+ "type": "object",
+ "version": "1.0.1",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
+ },
+ "TransformerField": {
+ "properties": {
+ "transformer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load Transformer submodel"
+ },
+ "loras": {
+ "description": "LoRAs to apply on model loading",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "Loras",
+ "type": "array"
+ }
+ },
+ "required": ["transformer", "loras"],
+ "title": "TransformerField",
+ "type": "object"
+ },
+ "UIComponent": {
+ "description": "The type of UI component to use for a field, used to override the default components, which are\ninferred from the field type.",
+ "enum": ["none", "textarea", "slider", "video-frame-index"],
+ "title": "UIComponent",
+ "type": "string"
+ },
+ "UIConfigBase": {
+ "description": "Provides additional node configuration to the UI.\nThis is used internally by the @invocation decorator logic. Do not use this directly.",
+ "properties": {
+ "tags": {
"anyOf": [
{
- "type": "string"
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "The node's tags",
+ "title": "Tags"
},
- "cover_image": {
+ "title": {
"anyOf": [
{
"type": "string"
@@ -68875,136 +74162,199 @@
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
- },
- "type": {
- "type": "string",
- "const": "t2i_adapter",
- "title": "Type",
- "default": "t2i_adapter"
+ "default": null,
+ "description": "The node's display name",
+ "title": "Title"
},
- "default_settings": {
+ "category": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
+ "type": "string"
},
{
"type": "null"
}
- ]
+ ],
+ "default": null,
+ "description": "The node's category",
+ "title": "Category"
},
- "base": {
- "type": "string",
- "const": "sd-1",
- "title": "Base",
- "default": "sd-1"
+ "version": {
+ "description": "The node's version. Should be a valid semver string e.g. \"1.0.0\" or \"3.8.13\".",
+ "title": "Version",
+ "type": "string"
+ },
+ "node_pack": {
+ "description": "The node pack that this node belongs to, will be 'invokeai' for built-in nodes",
+ "title": "Node Pack",
+ "type": "string"
+ },
+ "classification": {
+ "$ref": "#/components/schemas/Classification",
+ "default": "stable",
+ "description": "The node's classification"
}
},
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "default_settings",
- "base"
+ "required": ["tags", "title", "category", "version", "node_pack", "classification"],
+ "title": "UIConfigBase",
+ "type": "object"
+ },
+ "UIType": {
+ "description": "Type hints for the UI for situations in which the field type is not enough to infer the correct UI type.\n\n- Model Fields\nThe most common node-author-facing use will be for model fields. Internally, there is no difference\nbetween SD-1, SD-2 and SDXL model fields - they all use the class `MainModelField`. To ensure the\nbase-model-specific UI is rendered, use e.g. `ui_type=UIType.SDXLMainModelField` to indicate that\nthe field is an SDXL main model field.\n\n- Any Field\nWe cannot infer the usage of `typing.Any` via schema parsing, so you *must* use `ui_type=UIType.Any` to\nindicate that the field accepts any type. Use with caution. This cannot be used on outputs.\n\n- Scheduler Field\nSpecial handling in the UI is needed for this field, which otherwise would be parsed as a plain enum field.\n\n- Internal Fields\nSimilar to the Any Field, the `collect` and `iterate` nodes make use of `typing.Any`. To facilitate\nhandling these types in the client, we use `UIType._Collection` and `UIType._CollectionItem`. These\nshould not be used by node authors.\n\n- DEPRECATED Fields\nThese types are deprecated and should not be used by node authors. A warning will be logged if one is\nused, and the type will be ignored. They are included here for backwards compatibility.",
+ "enum": [
+ "SchedulerField",
+ "AnyField",
+ "SavedWorkflowField",
+ "CollectionField",
+ "CollectionItemField",
+ "IsIntermediate",
+ "DEPRECATED_Boolean",
+ "DEPRECATED_Color",
+ "DEPRECATED_Conditioning",
+ "DEPRECATED_Control",
+ "DEPRECATED_Float",
+ "DEPRECATED_Image",
+ "DEPRECATED_Integer",
+ "DEPRECATED_Latents",
+ "DEPRECATED_String",
+ "DEPRECATED_BooleanCollection",
+ "DEPRECATED_ColorCollection",
+ "DEPRECATED_ConditioningCollection",
+ "DEPRECATED_ControlCollection",
+ "DEPRECATED_FloatCollection",
+ "DEPRECATED_ImageCollection",
+ "DEPRECATED_IntegerCollection",
+ "DEPRECATED_LatentsCollection",
+ "DEPRECATED_StringCollection",
+ "DEPRECATED_BooleanPolymorphic",
+ "DEPRECATED_ColorPolymorphic",
+ "DEPRECATED_ConditioningPolymorphic",
+ "DEPRECATED_ControlPolymorphic",
+ "DEPRECATED_FloatPolymorphic",
+ "DEPRECATED_ImagePolymorphic",
+ "DEPRECATED_IntegerPolymorphic",
+ "DEPRECATED_LatentsPolymorphic",
+ "DEPRECATED_StringPolymorphic",
+ "DEPRECATED_UNet",
+ "DEPRECATED_Vae",
+ "DEPRECATED_CLIP",
+ "DEPRECATED_Collection",
+ "DEPRECATED_CollectionItem",
+ "DEPRECATED_Enum",
+ "DEPRECATED_WorkflowField",
+ "DEPRECATED_BoardField",
+ "DEPRECATED_MetadataItem",
+ "DEPRECATED_MetadataItemCollection",
+ "DEPRECATED_MetadataItemPolymorphic",
+ "DEPRECATED_MetadataDict",
+ "DEPRECATED_MainModelField",
+ "DEPRECATED_CogView4MainModelField",
+ "DEPRECATED_FluxMainModelField",
+ "DEPRECATED_SD3MainModelField",
+ "DEPRECATED_SDXLMainModelField",
+ "DEPRECATED_SDXLRefinerModelField",
+ "DEPRECATED_ONNXModelField",
+ "DEPRECATED_VAEModelField",
+ "DEPRECATED_FluxVAEModelField",
+ "DEPRECATED_LoRAModelField",
+ "DEPRECATED_ControlNetModelField",
+ "DEPRECATED_IPAdapterModelField",
+ "DEPRECATED_T2IAdapterModelField",
+ "DEPRECATED_T5EncoderModelField",
+ "DEPRECATED_CLIPEmbedModelField",
+ "DEPRECATED_CLIPLEmbedModelField",
+ "DEPRECATED_CLIPGEmbedModelField",
+ "DEPRECATED_SpandrelImageToImageModelField",
+ "DEPRECATED_ControlLoRAModelField",
+ "DEPRECATED_SigLipModelField",
+ "DEPRECATED_FluxReduxModelField",
+ "DEPRECATED_LLaVAModelField",
+ "DEPRECATED_Imagen3ModelField",
+ "DEPRECATED_Imagen4ModelField",
+ "DEPRECATED_ChatGPT4oModelField",
+ "DEPRECATED_Gemini2_5ModelField",
+ "DEPRECATED_FluxKontextModelField",
+ "DEPRECATED_Veo3ModelField",
+ "DEPRECATED_RunwayModelField"
],
- "title": "T2IAdapter_Diffusers_SD1_Config"
+ "title": "UIType",
+ "type": "string"
},
- "T2IAdapter_Diffusers_SDXL_Config": {
+ "UNetField": {
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "unet": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load unet submodel"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "scheduler": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load scheduler submodel"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "loras": {
+ "description": "LoRAs to apply on model loading",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "Loras",
+ "type": "array"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "seamless_axes": {
+ "description": "Axes(\"x\" and \"y\") to which apply seamless",
+ "items": {
+ "type": "string"
+ },
+ "title": "Seamless Axes",
+ "type": "array"
},
- "description": {
+ "freeu_config": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/FreeUConfig"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
+ "default": null,
+ "description": "FreeU configuration"
+ }
+ },
+ "required": ["unet", "scheduler", "loras"],
+ "title": "UNetField",
+ "type": "object"
+ },
+ "UNetOutput": {
+ "class": "output",
+ "description": "Base class for invocations that output a UNet field.",
+ "properties": {
+ "unet": {
+ "$ref": "#/components/schemas/UNetField",
+ "description": "UNet (scheduler, LoRAs)",
+ "field_kind": "output",
+ "title": "UNet",
+ "ui_hidden": false
},
- "source": {
+ "type": {
+ "const": "unet_output",
+ "default": "unet_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "unet", "type", "type"],
+ "title": "UNetOutput",
+ "type": "object"
+ },
+ "URLModelSource": {
+ "properties": {
+ "url": {
"type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
- },
- "source_api_response": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
- },
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "minLength": 1,
+ "format": "uri",
+ "title": "Url"
},
- "cover_image": {
+ "access_token": {
"anyOf": [
{
"type": "string"
@@ -69013,87 +74363,61 @@
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "title": "Access Token"
},
"type": {
"type": "string",
- "const": "t2i_adapter",
+ "const": "url",
"title": "Type",
- "default": "t2i_adapter"
- },
- "default_settings": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ControlAdapterDefaultSettings"
- },
- {
- "type": "null"
- }
- ]
+ "default": "url"
+ }
+ },
+ "type": "object",
+ "required": ["url"],
+ "title": "URLModelSource",
+ "description": "A generic URL point to a checkpoint file."
+ },
+ "URLRegexTokenPair": {
+ "properties": {
+ "url_regex": {
+ "type": "string",
+ "title": "Url Regex",
+ "description": "Regular expression to match against the URL"
},
- "base": {
+ "token": {
"type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
+ "title": "Token",
+ "description": "Token to use when the URL matches the regex"
}
},
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "default_settings",
- "base"
- ],
- "title": "T2IAdapter_Diffusers_SDXL_Config"
+ "required": ["url_regex", "token"],
+ "title": "URLRegexTokenPair"
},
- "T5EncoderField": {
+ "UninstallNodePackResponse": {
"properties": {
- "tokenizer": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load tokenizer submodel"
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "The name of the uninstalled node pack."
},
- "text_encoder": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load text_encoder submodel"
+ "success": {
+ "type": "boolean",
+ "title": "Success",
+ "description": "Whether the uninstall was successful."
},
- "loras": {
- "description": "LoRAs to apply on model loading",
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "title": "Loras",
- "type": "array"
+ "message": {
+ "type": "string",
+ "title": "Message",
+ "description": "Status message."
}
},
- "required": ["tokenizer", "text_encoder", "loras"],
- "title": "T5EncoderField",
- "type": "object"
+ "type": "object",
+ "required": ["name", "success", "message"],
+ "title": "UninstallNodePackResponse",
+ "description": "Response after uninstalling a node pack."
},
- "T5Encoder_BnBLLMint8_Config": {
+ "Unknown_Config": {
"properties": {
"key": {
"type": "string",
@@ -69179,33 +74503,21 @@
},
"base": {
"type": "string",
- "const": "any",
+ "const": "unknown",
"title": "Base",
- "default": "any"
+ "default": "unknown"
},
"type": {
"type": "string",
- "const": "t5_encoder",
+ "const": "unknown",
"title": "Type",
- "default": "t5_encoder"
+ "default": "unknown"
},
"format": {
"type": "string",
- "const": "bnb_quantized_int8b",
+ "const": "unknown",
"title": "Format",
- "default": "bnb_quantized_int8b"
- },
- "cpu_only": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
+ "default": "unknown"
}
},
"type": "object",
@@ -69223,13 +74535,419 @@
"cover_image",
"base",
"type",
- "format",
- "cpu_only"
+ "format"
],
- "title": "T5Encoder_BnBLLMint8_Config",
- "description": "Configuration for T5 Encoder models quantized by bitsandbytes' LLM.int8."
+ "title": "Unknown_Config",
+ "description": "Model config for unknown models, used as a fallback when we cannot positively identify a model."
},
- "T5Encoder_T5Encoder_Config": {
+ "UnsharpMaskInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Applies an unsharp mask filter to an image",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ImageField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The image to use",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
+ },
+ "radius": {
+ "default": 2,
+ "description": "Unsharp mask radius",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 2,
+ "orig_required": false,
+ "title": "Radius",
+ "type": "number"
+ },
+ "strength": {
+ "default": 50,
+ "description": "Unsharp mask strength",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 0,
+ "orig_default": 50,
+ "orig_required": false,
+ "title": "Strength",
+ "type": "number"
+ },
+ "type": {
+ "const": "unsharp_mask",
+ "default": "unsharp_mask",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "unsharp_mask"],
+ "title": "Unsharp Mask",
+ "type": "object",
+ "version": "1.2.2",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
+ },
+ "UnstarredImagesResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the delete operation"
+ },
+ "unstarred_images": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Unstarred Images",
+ "description": "The names of the images that were unstarred"
+ }
+ },
+ "type": "object",
+ "required": ["affected_boards", "unstarred_images"],
+ "title": "UnstarredImagesResult"
+ },
+ "UnstarredVideosResult": {
+ "properties": {
+ "affected_boards": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Affected Boards",
+ "description": "The ids of boards affected by the operation"
+ },
+ "unstarred_videos": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Unstarred Videos",
+ "description": "The names of the videos that were unstarred"
+ }
+ },
+ "type": "object",
+ "required": ["affected_boards", "unstarred_videos"],
+ "title": "UnstarredVideosResult"
+ },
+ "UpdateAppGenerationSettingsRequest": {
+ "properties": {
+ "image_subfolder_strategy": {
+ "type": "string",
+ "enum": ["flat", "date", "type", "hash"],
+ "title": "Image Subfolder Strategy",
+ "description": "Strategy for organizing images into subfolders."
+ },
+ "max_queue_history": {
+ "anyOf": [
+ {
+ "type": "integer",
+ "minimum": 0.0
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Max Queue History",
+ "description": "Keep the last N completed, failed, and canceled queue items on startup. Set to 0 to prune all terminal items."
+ }
+ },
+ "type": "object",
+ "title": "UpdateAppGenerationSettingsRequest",
+ "description": "Writable generation-related app settings."
+ },
+ "UserDTO": {
+ "properties": {
+ "user_id": {
+ "type": "string",
+ "title": "User Id",
+ "description": "Unique user identifier"
+ },
+ "email": {
+ "type": "string",
+ "title": "Email",
+ "description": "User email address"
+ },
+ "display_name": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Display Name",
+ "description": "Display name"
+ },
+ "is_admin": {
+ "type": "boolean",
+ "title": "Is Admin",
+ "description": "Whether user has admin privileges",
+ "default": false
+ },
+ "is_active": {
+ "type": "boolean",
+ "title": "Is Active",
+ "description": "Whether user account is active",
+ "default": true
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time",
+ "title": "Created At",
+ "description": "When the user was created"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time",
+ "title": "Updated At",
+ "description": "When the user was last updated"
+ },
+ "last_login_at": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Last Login At",
+ "description": "When user last logged in"
+ }
+ },
+ "type": "object",
+ "required": ["user_id", "email", "created_at", "updated_at"],
+ "title": "UserDTO",
+ "description": "User data transfer object."
+ },
+ "UserProfileUpdateRequest": {
+ "properties": {
+ "display_name": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Display Name",
+ "description": "New display name"
+ },
+ "current_password": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Current Password",
+ "description": "Current password (required when changing password)"
+ },
+ "new_password": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "New Password",
+ "description": "New password"
+ }
+ },
+ "type": "object",
+ "title": "UserProfileUpdateRequest",
+ "description": "Request body for a user to update their own profile."
+ },
+ "VAEField": {
+ "properties": {
+ "vae": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load vae submodel"
+ },
+ "seamless_axes": {
+ "description": "Axes(\"x\" and \"y\") to which apply seamless",
+ "items": {
+ "type": "string"
+ },
+ "title": "Seamless Axes",
+ "type": "array"
+ }
+ },
+ "required": ["vae"],
+ "title": "VAEField",
+ "type": "object"
+ },
+ "VAELoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Loads a VAE model, outputting a VaeLoaderOutput",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "vae_model": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/ModelIdentifierField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "VAE model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "VAE",
+ "ui_model_base": ["sd-1", "sd-2", "sdxl", "sd-3", "flux", "flux2"],
+ "ui_model_type": ["vae"]
+ },
+ "type": {
+ "const": "vae_loader",
+ "default": "vae_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["vae", "model"],
+ "title": "VAE Model - SD1.5, SD2, SDXL, SD3, FLUX",
+ "type": "object",
+ "version": "1.0.4",
+ "output": {
+ "$ref": "#/components/schemas/VAEOutput"
+ }
+ },
+ "VAEOutput": {
+ "class": "output",
+ "description": "Base class for invocations that output a VAE field",
+ "properties": {
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "vae_output",
+ "default": "vae_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "vae", "type", "type"],
+ "title": "VAEOutput",
+ "type": "object"
+ },
+ "VAE_Checkpoint_Anima_Config": {
"properties": {
"key": {
"type": "string",
@@ -69313,35 +75031,35 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "base": {
- "type": "string",
- "const": "any",
- "title": "Base",
- "default": "any"
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
"type": {
"type": "string",
- "const": "t5_encoder",
+ "const": "vae",
"title": "Type",
- "default": "t5_encoder"
+ "default": "vae"
},
"format": {
"type": "string",
- "const": "t5_encoder",
+ "const": "checkpoint",
"title": "Format",
- "default": "t5_encoder"
+ "default": "checkpoint"
},
- "cpu_only": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
+ "base": {
+ "type": "string",
+ "const": "anima",
+ "title": "Base",
+ "default": "anima"
}
},
"type": "object",
@@ -69357,38 +75075,15 @@
"source_api_response",
"source_url",
"cover_image",
- "base",
+ "config_path",
"type",
"format",
- "cpu_only"
+ "base"
],
- "title": "T5Encoder_T5Encoder_Config",
- "description": "Configuration for T5 Encoder models in a bespoke, diffusers-like format. The model weights are expected to be in\na folder called text_encoder_2 inside the model directory, with a config file named model.safetensors.index.json."
- },
- "TBLR": {
- "properties": {
- "top": {
- "title": "Top",
- "type": "integer"
- },
- "bottom": {
- "title": "Bottom",
- "type": "integer"
- },
- "left": {
- "title": "Left",
- "type": "integer"
- },
- "right": {
- "title": "Right",
- "type": "integer"
- }
- },
- "required": ["top", "bottom", "left", "right"],
- "title": "TBLR",
- "type": "object"
+ "title": "VAE_Checkpoint_Anima_Config",
+ "description": "Model config for Anima QwenImage VAE checkpoint models (AutoencoderKLQwenImage)."
},
- "TI_File_SD1_Config": {
+ "VAE_Checkpoint_FLUX_Config": {
"properties": {
"key": {
"type": "string",
@@ -69472,23 +75167,35 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
"type": {
"type": "string",
- "const": "embedding",
+ "const": "vae",
"title": "Type",
- "default": "embedding"
+ "default": "vae"
},
"format": {
"type": "string",
- "const": "embedding_file",
+ "const": "checkpoint",
"title": "Format",
- "default": "embedding_file"
+ "default": "checkpoint"
},
"base": {
"type": "string",
- "const": "sd-1",
+ "const": "flux",
"title": "Base",
- "default": "sd-1"
+ "default": "flux"
}
},
"type": "object",
@@ -69504,13 +75211,14 @@
"source_api_response",
"source_url",
"cover_image",
+ "config_path",
"type",
"format",
"base"
],
- "title": "TI_File_SD1_Config"
+ "title": "VAE_Checkpoint_FLUX_Config"
},
- "TI_File_SD2_Config": {
+ "VAE_Checkpoint_Flux2_Config": {
"properties": {
"key": {
"type": "string",
@@ -69594,23 +75302,35 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
"type": {
"type": "string",
- "const": "embedding",
+ "const": "vae",
"title": "Type",
- "default": "embedding"
+ "default": "vae"
},
"format": {
"type": "string",
- "const": "embedding_file",
+ "const": "checkpoint",
"title": "Format",
- "default": "embedding_file"
+ "default": "checkpoint"
},
"base": {
"type": "string",
- "const": "sd-2",
+ "const": "flux2",
"title": "Base",
- "default": "sd-2"
+ "default": "flux2"
}
},
"type": "object",
@@ -69626,13 +75346,15 @@
"source_api_response",
"source_url",
"cover_image",
+ "config_path",
"type",
"format",
"base"
],
- "title": "TI_File_SD2_Config"
+ "title": "VAE_Checkpoint_Flux2_Config",
+ "description": "Model config for FLUX.2 VAE checkpoint models (AutoencoderKLFlux2)."
},
- "TI_File_SDXL_Config": {
+ "VAE_Checkpoint_QwenImage_Config": {
"properties": {
"key": {
"type": "string",
@@ -69716,23 +75438,35 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
"type": {
"type": "string",
- "const": "embedding",
+ "const": "vae",
"title": "Type",
- "default": "embedding"
+ "default": "vae"
},
"format": {
"type": "string",
- "const": "embedding_file",
+ "const": "checkpoint",
"title": "Format",
- "default": "embedding_file"
+ "default": "checkpoint"
},
"base": {
"type": "string",
- "const": "sdxl",
+ "const": "qwen-image",
"title": "Base",
- "default": "sdxl"
+ "default": "qwen-image"
}
},
"type": "object",
@@ -69748,13 +75482,15 @@
"source_api_response",
"source_url",
"cover_image",
+ "config_path",
"type",
"format",
"base"
],
- "title": "TI_File_SDXL_Config"
+ "title": "VAE_Checkpoint_QwenImage_Config",
+ "description": "Model config for Qwen Image VAE checkpoint models (AutoencoderKLQwenImage)."
},
- "TI_Folder_SD1_Config": {
+ "VAE_Checkpoint_SD1_Config": {
"properties": {
"key": {
"type": "string",
@@ -69838,17 +75574,29 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
"type": {
"type": "string",
- "const": "embedding",
+ "const": "vae",
"title": "Type",
- "default": "embedding"
+ "default": "vae"
},
"format": {
"type": "string",
- "const": "embedding_folder",
+ "const": "checkpoint",
"title": "Format",
- "default": "embedding_folder"
+ "default": "checkpoint"
},
"base": {
"type": "string",
@@ -69870,13 +75618,14 @@
"source_api_response",
"source_url",
"cover_image",
+ "config_path",
"type",
"format",
"base"
],
- "title": "TI_Folder_SD1_Config"
+ "title": "VAE_Checkpoint_SD1_Config"
},
- "TI_Folder_SD2_Config": {
+ "VAE_Checkpoint_SD2_Config": {
"properties": {
"key": {
"type": "string",
@@ -69960,17 +75709,29 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
+ },
"type": {
"type": "string",
- "const": "embedding",
+ "const": "vae",
"title": "Type",
- "default": "embedding"
+ "default": "vae"
},
"format": {
"type": "string",
- "const": "embedding_folder",
+ "const": "checkpoint",
"title": "Format",
- "default": "embedding_folder"
+ "default": "checkpoint"
},
"base": {
"type": "string",
@@ -69992,13 +75753,14 @@
"source_api_response",
"source_url",
"cover_image",
+ "config_path",
"type",
"format",
"base"
],
- "title": "TI_Folder_SD2_Config"
+ "title": "VAE_Checkpoint_SD2_Config"
},
- "TI_Folder_SDXL_Config": {
+ "VAE_Checkpoint_SDXL_Config": {
"properties": {
"key": {
"type": "string",
@@ -70082,157 +75844,58 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "type": {
- "type": "string",
- "const": "embedding",
- "title": "Type",
- "default": "embedding"
- },
- "format": {
- "type": "string",
- "const": "embedding_folder",
- "title": "Format",
- "default": "embedding_folder"
- },
- "base": {
- "type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "type",
- "format",
- "base"
- ],
- "title": "TI_Folder_SDXL_Config"
- },
- "TensorField": {
- "description": "A tensor primitive field.",
- "properties": {
- "tensor_name": {
- "description": "The name of a tensor.",
- "title": "Tensor Name",
- "type": "string"
- }
- },
- "required": ["tensor_name"],
- "title": "TensorField",
- "type": "object"
- },
- "TextLLMInvocation": {
- "category": "llm",
- "class": "invocation",
- "classification": "beta",
- "description": "Run a text language model to generate or expand text (e.g. for prompt expansion).",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "prompt": {
- "default": "",
- "description": "Input text prompt.",
- "field_kind": "input",
- "input": "any",
- "orig_default": "",
- "orig_required": false,
- "title": "Prompt",
- "type": "string",
- "ui_component": "textarea"
- },
- "system_prompt": {
- "default": "You are an expert prompt writer for AI image generation. Given a brief description, expand it into a detailed, vivid prompt suitable for generating high-quality images. Only output the expanded prompt, nothing else.",
- "description": "System prompt that guides the model's behavior.",
- "field_kind": "input",
- "input": "any",
- "orig_default": "You are an expert prompt writer for AI image generation. Given a brief description, expand it into a detailed, vivid prompt suitable for generating high-quality images. Only output the expanded prompt, nothing else.",
- "orig_required": false,
- "title": "System Prompt",
- "type": "string",
- "ui_component": "textarea"
- },
- "text_llm_model": {
+ "config_path": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The text language model to use for text generation",
- "field_kind": "input",
- "input": "any",
- "orig_required": true,
- "title": "Text LLM Model",
- "ui_model_type": ["text_llm"]
- },
- "max_tokens": {
- "default": 300,
- "description": "Maximum number of tokens to generate.",
- "field_kind": "input",
- "input": "any",
- "maximum": 2048,
- "minimum": 1,
- "orig_default": 300,
- "orig_required": false,
- "title": "Max Tokens",
- "type": "integer"
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
"type": {
- "const": "text_llm",
- "default": "text_llm",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "vae",
+ "title": "Type",
+ "default": "vae"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
+ },
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
}
},
- "required": ["type", "id"],
- "tags": ["llm", "text", "prompt"],
- "title": "Text LLM",
"type": "object",
- "version": "1.0.0",
- "output": {
- "$ref": "#/components/schemas/StringOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "config_path",
+ "type",
+ "format",
+ "base"
+ ],
+ "title": "VAE_Checkpoint_SDXL_Config"
},
- "TextLLM_Diffusers_Config": {
+ "VAE_Checkpoint_Wan_Config": {
"properties": {
"key": {
"type": "string",
@@ -70316,39 +75979,41 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "config_path": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Config Path",
+ "description": "Path to the config for this model, if any."
},
"type": {
"type": "string",
- "const": "text_llm",
+ "const": "vae",
"title": "Type",
- "default": "text_llm"
+ "default": "vae"
+ },
+ "format": {
+ "type": "string",
+ "const": "checkpoint",
+ "title": "Format",
+ "default": "checkpoint"
},
"base": {
"type": "string",
- "const": "any",
+ "const": "wan",
"title": "Base",
- "default": "any"
+ "default": "wan"
},
- "cpu_only": {
- "anyOf": [
- {
- "type": "boolean"
- },
- {
- "type": "null"
- }
- ],
- "title": "Cpu Only",
- "description": "Whether this model should run on CPU only"
+ "latent_channels": {
+ "type": "integer",
+ "enum": [16, 48],
+ "title": "Latent Channels",
+ "description": "VAE latent channel count: 16 for A14B (standard Wan VAE) or 48 for TI2V-5B (Wan2.2-VAE)."
}
},
"type": "object",
@@ -70364,545 +76029,192 @@
"source_api_response",
"source_url",
"cover_image",
- "format",
- "repo_variant",
+ "config_path",
"type",
+ "format",
"base",
- "cpu_only"
+ "latent_channels"
],
- "title": "TextLLM_Diffusers_Config",
- "description": "Model config for text-only causal language models (e.g. Llama, Phi, Qwen, Mistral)."
- },
- "Tile": {
- "properties": {
- "coords": {
- "$ref": "#/components/schemas/TBLR",
- "description": "The coordinates of this tile relative to its parent image."
- },
- "overlap": {
- "$ref": "#/components/schemas/TBLR",
- "description": "The amount of overlap with adjacent tiles on each side of this tile."
- }
- },
- "required": ["coords", "overlap"],
- "title": "Tile",
- "type": "object"
- },
- "TileToPropertiesInvocation": {
- "category": "tiles",
- "class": "invocation",
- "classification": "stable",
- "description": "Split a Tile into its individual properties.",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "tile": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/Tile"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "The tile to split into properties.",
- "field_kind": "input",
- "input": "any",
- "orig_required": true
- },
- "type": {
- "const": "tile_to_properties",
- "default": "tile_to_properties",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["type", "id"],
- "tags": ["tiles"],
- "title": "Tile to Properties",
- "type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/TileToPropertiesOutput"
- }
+ "title": "VAE_Checkpoint_Wan_Config",
+ "description": "Model config for Wan 2.2 VAE checkpoint models (AutoencoderKLWan).\n\nDistinguishes A14B (z_dim=16, standard Wan VAE) from TI2V-5B (z_dim=48,\nWan2.2-VAE) via the input channel count of ``decoder.conv_in.weight``."
},
- "TileToPropertiesOutput": {
- "class": "output",
+ "VAE_Diffusers_Flux2_Config": {
"properties": {
- "coords_left": {
- "description": "Left coordinate of the tile relative to its parent image.",
- "field_kind": "output",
- "title": "Coords Left",
- "type": "integer",
- "ui_hidden": false
- },
- "coords_right": {
- "description": "Right coordinate of the tile relative to its parent image.",
- "field_kind": "output",
- "title": "Coords Right",
- "type": "integer",
- "ui_hidden": false
- },
- "coords_top": {
- "description": "Top coordinate of the tile relative to its parent image.",
- "field_kind": "output",
- "title": "Coords Top",
- "type": "integer",
- "ui_hidden": false
- },
- "coords_bottom": {
- "description": "Bottom coordinate of the tile relative to its parent image.",
- "field_kind": "output",
- "title": "Coords Bottom",
- "type": "integer",
- "ui_hidden": false
- },
- "width": {
- "description": "The width of the tile. Equal to coords_right - coords_left.",
- "field_kind": "output",
- "title": "Width",
- "type": "integer",
- "ui_hidden": false
- },
- "height": {
- "description": "The height of the tile. Equal to coords_bottom - coords_top.",
- "field_kind": "output",
- "title": "Height",
- "type": "integer",
- "ui_hidden": false
- },
- "overlap_top": {
- "description": "Overlap between this tile and its top neighbor.",
- "field_kind": "output",
- "title": "Overlap Top",
- "type": "integer",
- "ui_hidden": false
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "overlap_bottom": {
- "description": "Overlap between this tile and its bottom neighbor.",
- "field_kind": "output",
- "title": "Overlap Bottom",
- "type": "integer",
- "ui_hidden": false
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "overlap_left": {
- "description": "Overlap between this tile and its left neighbor.",
- "field_kind": "output",
- "title": "Overlap Left",
- "type": "integer",
- "ui_hidden": false
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "overlap_right": {
- "description": "Overlap between this tile and its right neighbor.",
- "field_kind": "output",
- "title": "Overlap Right",
+ "file_size": {
"type": "integer",
- "ui_hidden": false
- },
- "type": {
- "const": "tile_to_properties_output",
- "default": "tile_to_properties_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": [
- "output_meta",
- "coords_left",
- "coords_right",
- "coords_top",
- "coords_bottom",
- "width",
- "height",
- "overlap_top",
- "overlap_bottom",
- "overlap_left",
- "overlap_right",
- "type",
- "type"
- ],
- "title": "TileToPropertiesOutput",
- "type": "object"
- },
- "TileWithImage": {
- "properties": {
- "tile": {
- "$ref": "#/components/schemas/Tile"
- },
- "image": {
- "$ref": "#/components/schemas/ImageField"
- }
- },
- "required": ["tile", "image"],
- "title": "TileWithImage",
- "type": "object"
- },
- "TiledMultiDiffusionDenoiseLatents": {
- "category": "latents",
- "class": "invocation",
- "classification": "stable",
- "description": "Tiled Multi-Diffusion denoising.\n\nThis node handles automatically tiling the input image, and is primarily intended for global refinement of images\nin tiled upscaling workflows. Future Multi-Diffusion nodes should allow the user to specify custom regions with\ndifferent parameters for each region to harness the full power of Multi-Diffusion.\n\nThis node has a similar interface to the `DenoiseLatents` node, but it has a reduced feature set (no IP-Adapter,\nT2I-Adapter, masking, etc.).",
- "node_pack": "invokeai",
- "properties": {
- "id": {
- "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
- "field_kind": "node_attribute",
- "title": "Id",
- "type": "string"
- },
- "is_intermediate": {
- "default": false,
- "description": "Whether or not this is an intermediate invocation.",
- "field_kind": "node_attribute",
- "input": "direct",
- "orig_required": true,
- "title": "Is Intermediate",
- "type": "boolean",
- "ui_hidden": false,
- "ui_type": "IsIntermediate"
- },
- "use_cache": {
- "default": true,
- "description": "Whether or not to use the cache",
- "field_kind": "node_attribute",
- "title": "Use Cache",
- "type": "boolean"
- },
- "positive_conditioning": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ConditioningField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Positive conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
- },
- "negative_conditioning": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/ConditioningField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Negative conditioning tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "noise": {
- "anyOf": [
- {
- "$ref": "#/components/schemas/LatentsField"
- },
- {
- "type": "null"
- }
- ],
- "default": null,
- "description": "Noise tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
},
- "latents": {
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/LatentsField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "Latents tensor",
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false
- },
- "tile_height": {
- "default": 1024,
- "description": "Height of the tiles in image space.",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "multipleOf": 8,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Tile Height",
- "type": "integer"
- },
- "tile_width": {
- "default": 1024,
- "description": "Width of the tiles in image space.",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "multipleOf": 8,
- "orig_default": 1024,
- "orig_required": false,
- "title": "Tile Width",
- "type": "integer"
- },
- "tile_overlap": {
- "default": 32,
- "description": "The overlap between adjacent tiles in pixel space. (Of course, tile merging is applied in latent space.) Tiles will be cropped during merging (if necessary) to ensure that they overlap by exactly this amount.",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "multipleOf": 8,
- "orig_default": 32,
- "orig_required": false,
- "title": "Tile Overlap",
- "type": "integer"
- },
- "steps": {
- "default": 18,
- "description": "Number of steps to run",
- "exclusiveMinimum": 0,
- "field_kind": "input",
- "input": "any",
- "orig_default": 18,
- "orig_required": false,
- "title": "Steps",
- "type": "integer"
- },
- "cfg_scale": {
- "anyOf": [
- {
- "type": "number"
- },
- {
- "items": {
- "type": "number"
- },
- "type": "array"
- }
- ],
- "default": 6.0,
- "description": "Classifier-Free Guidance scale",
- "field_kind": "input",
- "input": "any",
- "orig_default": 6.0,
- "orig_required": false,
- "title": "CFG Scale"
- },
- "denoising_start": {
- "default": 0.0,
- "description": "When to start denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 0.0,
- "orig_required": false,
- "title": "Denoising Start",
- "type": "number"
- },
- "denoising_end": {
- "default": 1.0,
- "description": "When to stop denoising, expressed a percentage of total steps",
- "field_kind": "input",
- "input": "any",
- "maximum": 1,
- "minimum": 0,
- "orig_default": 1.0,
- "orig_required": false,
- "title": "Denoising End",
- "type": "number"
+ "title": "Description",
+ "description": "Model description"
},
- "scheduler": {
- "default": "euler",
- "description": "Scheduler to use during inference",
- "enum": [
- "ddim",
- "ddpm",
- "deis",
- "deis_k",
- "lms",
- "lms_k",
- "pndm",
- "heun",
- "heun_k",
- "euler",
- "euler_k",
- "euler_a",
- "kdpm_2",
- "kdpm_2_k",
- "kdpm_2_a",
- "kdpm_2_a_k",
- "dpmpp_2s",
- "dpmpp_2s_k",
- "dpmpp_2m",
- "dpmpp_2m_k",
- "dpmpp_2m_sde",
- "dpmpp_2m_sde_k",
- "dpmpp_3m",
- "dpmpp_3m_k",
- "dpmpp_sde",
- "dpmpp_sde_k",
- "er_sde",
- "unipc",
- "unipc_k",
- "lcm",
- "tcd"
- ],
- "field_kind": "input",
- "input": "any",
- "orig_default": "euler",
- "orig_required": false,
- "title": "Scheduler",
+ "source": {
"type": "string",
- "ui_type": "SchedulerField"
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "unet": {
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
- "$ref": "#/components/schemas/UNetField"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "input",
- "input": "connection",
- "orig_required": true,
- "title": "UNet"
- },
- "cfg_rescale_multiplier": {
- "default": 0,
- "description": "Rescale multiplier for CFG guidance, used for models trained with zero-terminal SNR",
- "exclusiveMaximum": 1,
- "field_kind": "input",
- "input": "any",
- "minimum": 0,
- "orig_default": 0,
- "orig_required": false,
- "title": "CFG Rescale Multiplier",
- "type": "number"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "control": {
+ "source_url": {
"anyOf": [
{
- "$ref": "#/components/schemas/ControlField"
+ "type": "string"
},
{
- "items": {
- "$ref": "#/components/schemas/ControlField"
- },
- "type": "array"
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
+ },
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "field_kind": "input",
- "input": "connection",
- "orig_default": null,
- "orig_required": false,
- "title": "Control"
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
"type": {
- "const": "tiled_multi_diffusion_denoise_latents",
- "default": "tiled_multi_diffusion_denoise_latents",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
+ "type": "string",
+ "const": "vae",
+ "title": "Type",
+ "default": "vae"
+ },
+ "base": {
+ "type": "string",
+ "const": "flux2",
+ "title": "Base",
+ "default": "flux2"
}
},
- "required": ["type", "id"],
- "tags": ["upscale", "denoise"],
- "title": "Tiled Multi-Diffusion Denoise - SD1.5, SDXL",
"type": "object",
- "version": "1.0.1",
- "output": {
- "$ref": "#/components/schemas/LatentsOutput"
- }
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "base"
+ ],
+ "title": "VAE_Diffusers_Flux2_Config",
+ "description": "Model config for FLUX.2 VAE models in diffusers format (AutoencoderKLFlux2)."
},
- "TransformerField": {
+ "VAE_Diffusers_SD1_Config": {
"properties": {
- "transformer": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load Transformer submodel"
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "loras": {
- "description": "LoRAs to apply on model loading",
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "title": "Loras",
- "type": "array"
- }
- },
- "required": ["transformer", "loras"],
- "title": "TransformerField",
- "type": "object"
- },
- "UIComponent": {
- "description": "The type of UI component to use for a field, used to override the default components, which are\ninferred from the field type.",
- "enum": ["none", "textarea", "slider"],
- "title": "UIComponent",
- "type": "string"
- },
- "UIConfigBase": {
- "description": "Provides additional node configuration to the UI.\nThis is used internally by the @invocation decorator logic. Do not use this directly.",
- "properties": {
- "tags": {
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
+ },
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ },
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
+ },
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "items": {
- "type": "string"
- },
- "type": "array"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "The node's tags",
- "title": "Tags"
+ "title": "Description",
+ "description": "Model description"
},
- "title": {
+ "source": {
+ "type": "string",
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
+ },
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
"type": "string"
@@ -70911,11 +76223,10 @@
"type": "null"
}
],
- "default": null,
- "description": "The node's display name",
- "title": "Title"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "category": {
+ "source_url": {
"anyOf": [
{
"type": "string"
@@ -70924,186 +76235,113 @@
"type": "null"
}
],
- "default": null,
- "description": "The node's category",
- "title": "Category"
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "version": {
- "description": "The node's version. Should be a valid semver string e.g. \"1.0.0\" or \"3.8.13\".",
- "title": "Version",
- "type": "string"
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
},
- "node_pack": {
- "description": "The node pack that this node belongs to, will be 'invokeai' for built-in nodes",
- "title": "Node Pack",
- "type": "string"
+ "format": {
+ "type": "string",
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
},
- "classification": {
- "$ref": "#/components/schemas/Classification",
- "default": "stable",
- "description": "The node's classification"
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
+ },
+ "type": {
+ "type": "string",
+ "const": "vae",
+ "title": "Type",
+ "default": "vae"
+ },
+ "base": {
+ "type": "string",
+ "const": "sd-1",
+ "title": "Base",
+ "default": "sd-1"
}
},
- "required": ["tags", "title", "category", "version", "node_pack", "classification"],
- "title": "UIConfigBase",
- "type": "object"
- },
- "UIType": {
- "description": "Type hints for the UI for situations in which the field type is not enough to infer the correct UI type.\n\n- Model Fields\nThe most common node-author-facing use will be for model fields. Internally, there is no difference\nbetween SD-1, SD-2 and SDXL model fields - they all use the class `MainModelField`. To ensure the\nbase-model-specific UI is rendered, use e.g. `ui_type=UIType.SDXLMainModelField` to indicate that\nthe field is an SDXL main model field.\n\n- Any Field\nWe cannot infer the usage of `typing.Any` via schema parsing, so you *must* use `ui_type=UIType.Any` to\nindicate that the field accepts any type. Use with caution. This cannot be used on outputs.\n\n- Scheduler Field\nSpecial handling in the UI is needed for this field, which otherwise would be parsed as a plain enum field.\n\n- Internal Fields\nSimilar to the Any Field, the `collect` and `iterate` nodes make use of `typing.Any`. To facilitate\nhandling these types in the client, we use `UIType._Collection` and `UIType._CollectionItem`. These\nshould not be used by node authors.\n\n- DEPRECATED Fields\nThese types are deprecated and should not be used by node authors. A warning will be logged if one is\nused, and the type will be ignored. They are included here for backwards compatibility.",
- "enum": [
- "SchedulerField",
- "AnyField",
- "SavedWorkflowField",
- "CollectionField",
- "CollectionItemField",
- "IsIntermediate",
- "DEPRECATED_Boolean",
- "DEPRECATED_Color",
- "DEPRECATED_Conditioning",
- "DEPRECATED_Control",
- "DEPRECATED_Float",
- "DEPRECATED_Image",
- "DEPRECATED_Integer",
- "DEPRECATED_Latents",
- "DEPRECATED_String",
- "DEPRECATED_BooleanCollection",
- "DEPRECATED_ColorCollection",
- "DEPRECATED_ConditioningCollection",
- "DEPRECATED_ControlCollection",
- "DEPRECATED_FloatCollection",
- "DEPRECATED_ImageCollection",
- "DEPRECATED_IntegerCollection",
- "DEPRECATED_LatentsCollection",
- "DEPRECATED_StringCollection",
- "DEPRECATED_BooleanPolymorphic",
- "DEPRECATED_ColorPolymorphic",
- "DEPRECATED_ConditioningPolymorphic",
- "DEPRECATED_ControlPolymorphic",
- "DEPRECATED_FloatPolymorphic",
- "DEPRECATED_ImagePolymorphic",
- "DEPRECATED_IntegerPolymorphic",
- "DEPRECATED_LatentsPolymorphic",
- "DEPRECATED_StringPolymorphic",
- "DEPRECATED_UNet",
- "DEPRECATED_Vae",
- "DEPRECATED_CLIP",
- "DEPRECATED_Collection",
- "DEPRECATED_CollectionItem",
- "DEPRECATED_Enum",
- "DEPRECATED_WorkflowField",
- "DEPRECATED_BoardField",
- "DEPRECATED_MetadataItem",
- "DEPRECATED_MetadataItemCollection",
- "DEPRECATED_MetadataItemPolymorphic",
- "DEPRECATED_MetadataDict",
- "DEPRECATED_MainModelField",
- "DEPRECATED_CogView4MainModelField",
- "DEPRECATED_FluxMainModelField",
- "DEPRECATED_SD3MainModelField",
- "DEPRECATED_SDXLMainModelField",
- "DEPRECATED_SDXLRefinerModelField",
- "DEPRECATED_ONNXModelField",
- "DEPRECATED_VAEModelField",
- "DEPRECATED_FluxVAEModelField",
- "DEPRECATED_LoRAModelField",
- "DEPRECATED_ControlNetModelField",
- "DEPRECATED_IPAdapterModelField",
- "DEPRECATED_T2IAdapterModelField",
- "DEPRECATED_T5EncoderModelField",
- "DEPRECATED_CLIPEmbedModelField",
- "DEPRECATED_CLIPLEmbedModelField",
- "DEPRECATED_CLIPGEmbedModelField",
- "DEPRECATED_SpandrelImageToImageModelField",
- "DEPRECATED_ControlLoRAModelField",
- "DEPRECATED_SigLipModelField",
- "DEPRECATED_FluxReduxModelField",
- "DEPRECATED_LLaVAModelField",
- "DEPRECATED_Imagen3ModelField",
- "DEPRECATED_Imagen4ModelField",
- "DEPRECATED_ChatGPT4oModelField",
- "DEPRECATED_Gemini2_5ModelField",
- "DEPRECATED_FluxKontextModelField",
- "DEPRECATED_Veo3ModelField",
- "DEPRECATED_RunwayModelField"
+ "type": "object",
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "base"
],
- "title": "UIType",
- "type": "string"
+ "title": "VAE_Diffusers_SD1_Config"
},
- "UNetField": {
+ "VAE_Diffusers_SDXL_Config": {
"properties": {
- "unet": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load unet submodel"
+ "key": {
+ "type": "string",
+ "title": "Key",
+ "description": "A unique key for this model."
},
- "scheduler": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load scheduler submodel"
+ "hash": {
+ "type": "string",
+ "title": "Hash",
+ "description": "The hash of the model file(s)."
},
- "loras": {
- "description": "LoRAs to apply on model loading",
- "items": {
- "$ref": "#/components/schemas/LoRAField"
- },
- "title": "Loras",
- "type": "array"
+ "path": {
+ "type": "string",
+ "title": "Path",
+ "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
},
- "seamless_axes": {
- "description": "Axes(\"x\" and \"y\") to which apply seamless",
- "items": {
- "type": "string"
- },
- "title": "Seamless Axes",
- "type": "array"
+ "file_size": {
+ "type": "integer",
+ "title": "File Size",
+ "description": "The size of the model in bytes."
},
- "freeu_config": {
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "Name of the model."
+ },
+ "description": {
"anyOf": [
{
- "$ref": "#/components/schemas/FreeUConfig"
+ "type": "string"
},
{
"type": "null"
}
],
- "default": null,
- "description": "FreeU configuration"
- }
- },
- "required": ["unet", "scheduler", "loras"],
- "title": "UNetField",
- "type": "object"
- },
- "UNetOutput": {
- "class": "output",
- "description": "Base class for invocations that output a UNet field.",
- "properties": {
- "unet": {
- "$ref": "#/components/schemas/UNetField",
- "description": "UNet (scheduler, LoRAs)",
- "field_kind": "output",
- "title": "UNet",
- "ui_hidden": false
+ "title": "Description",
+ "description": "Model description"
},
- "type": {
- "const": "unet_output",
- "default": "unet_output",
- "field_kind": "node_attribute",
- "title": "type",
- "type": "string"
- }
- },
- "required": ["output_meta", "unet", "type", "type"],
- "title": "UNetOutput",
- "type": "object"
- },
- "URLModelSource": {
- "properties": {
- "url": {
+ "source": {
"type": "string",
- "minLength": 1,
- "format": "uri",
- "title": "Url"
+ "title": "Source",
+ "description": "The original source of the model (path, URL or repo_id)."
},
- "access_token": {
+ "source_type": {
+ "$ref": "#/components/schemas/ModelSourceType",
+ "description": "The type of source"
+ },
+ "source_api_response": {
"anyOf": [
{
"type": "string"
@@ -71112,61 +76350,77 @@
"type": "null"
}
],
- "title": "Access Token"
+ "title": "Source Api Response",
+ "description": "The original API response from the source, as stringified JSON."
},
- "type": {
- "type": "string",
- "const": "url",
- "title": "Type",
- "default": "url"
- }
- },
- "type": "object",
- "required": ["url"],
- "title": "URLModelSource",
- "description": "A generic URL point to a checkpoint file."
- },
- "URLRegexTokenPair": {
- "properties": {
- "url_regex": {
- "type": "string",
- "title": "Url Regex",
- "description": "Regular expression to match against the URL"
+ "source_url": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Source Url",
+ "description": "Optional URL for the model (e.g. download page or model page)."
},
- "token": {
- "type": "string",
- "title": "Token",
- "description": "Token to use when the URL matches the regex"
- }
- },
- "type": "object",
- "required": ["url_regex", "token"],
- "title": "URLRegexTokenPair"
- },
- "UninstallNodePackResponse": {
- "properties": {
- "name": {
+ "cover_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Cover Image",
+ "description": "Url for image to preview model"
+ },
+ "format": {
"type": "string",
- "title": "Name",
- "description": "The name of the uninstalled node pack."
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
},
- "success": {
- "type": "boolean",
- "title": "Success",
- "description": "Whether the uninstall was successful."
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
- "message": {
+ "type": {
"type": "string",
- "title": "Message",
- "description": "Status message."
+ "const": "vae",
+ "title": "Type",
+ "default": "vae"
+ },
+ "base": {
+ "type": "string",
+ "const": "sdxl",
+ "title": "Base",
+ "default": "sdxl"
}
},
"type": "object",
- "required": ["name", "success", "message"],
- "title": "UninstallNodePackResponse",
- "description": "Response after uninstalling a node pack."
+ "required": [
+ "key",
+ "hash",
+ "path",
+ "file_size",
+ "name",
+ "description",
+ "source",
+ "source_type",
+ "source_api_response",
+ "source_url",
+ "cover_image",
+ "format",
+ "repo_variant",
+ "type",
+ "base"
+ ],
+ "title": "VAE_Diffusers_SDXL_Config"
},
- "Unknown_Config": {
+ "VAE_Diffusers_Wan_Config": {
"properties": {
"key": {
"type": "string",
@@ -71250,23 +76504,34 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "base": {
+ "format": {
"type": "string",
- "const": "unknown",
- "title": "Base",
- "default": "unknown"
+ "const": "diffusers",
+ "title": "Format",
+ "default": "diffusers"
+ },
+ "repo_variant": {
+ "$ref": "#/components/schemas/ModelRepoVariant",
+ "default": ""
},
"type": {
"type": "string",
- "const": "unknown",
+ "const": "vae",
"title": "Type",
- "default": "unknown"
+ "default": "vae"
},
- "format": {
+ "base": {
"type": "string",
- "const": "unknown",
- "title": "Format",
- "default": "unknown"
+ "const": "wan",
+ "title": "Base",
+ "default": "wan"
+ },
+ "latent_channels": {
+ "type": "integer",
+ "enum": [16, 48],
+ "title": "Latent Channels",
+ "description": "VAE latent channel count: 16 for A14B or 48 for TI2V-5B's Wan2.2-VAE.",
+ "default": 16
}
},
"type": "object",
@@ -71282,18 +76547,66 @@
"source_api_response",
"source_url",
"cover_image",
- "base",
+ "format",
+ "repo_variant",
"type",
- "format"
+ "base",
+ "latent_channels"
],
- "title": "Unknown_Config",
- "description": "Model config for unknown models, used as a fallback when we cannot positively identify a model."
+ "title": "VAE_Diffusers_Wan_Config",
+ "description": "Model config for Wan 2.2 VAE in diffusers folder layout (AutoencoderKLWan)."
},
- "UnsharpMaskInvocation": {
- "category": "image",
+ "ValidationError": {
+ "properties": {
+ "loc": {
+ "items": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "integer"
+ }
+ ]
+ },
+ "type": "array",
+ "title": "Location"
+ },
+ "msg": {
+ "type": "string",
+ "title": "Message"
+ },
+ "type": {
+ "type": "string",
+ "title": "Error Type"
+ }
+ },
+ "type": "object",
+ "required": ["loc", "msg", "type"],
+ "title": "ValidationError"
+ },
+ "VideoBoardArg": {
+ "properties": {
+ "board_id": {
+ "type": "string",
+ "title": "Board Id",
+ "description": "The id of the board to add or remove the video from"
+ },
+ "video_name": {
+ "type": "string",
+ "title": "Video Name",
+ "description": "The name of the video to add to / remove from the board"
+ }
+ },
+ "type": "object",
+ "required": ["board_id", "video_name"],
+ "title": "VideoBoardArg"
+ },
+ "VideoConcatInvocation": {
+ "category": "video",
"class": "invocation",
- "classification": "stable",
- "description": "Applies an unsharp mask filter to an image",
+ "classification": "prototype",
+ "description": "Join two or more videos into a single MP4.\n\nTransitions:\n\n* ``cut`` \u2014 hard splice, no blending. Fastest; total length is the sum of inputs.\n* ``crossfade`` \u2014 linear A\u2192B cross-dissolve over ``transition_frames``. Each boundary\n consumes ``transition_frames`` from both adjacent clips, so total length is\n ``sum(inputs) - transition_frames * (n - 1)``.\n* ``fade_through_black`` \u2014 A fades to black, then B fades in from black. Each boundary\n consumes ``transition_frames // 2`` frames from the preceding clip's tail and the\n remainder (``transition_frames - transition_frames // 2``) from the next clip's head,\n so the total emitted is exactly ``transition_frames`` per boundary \u2014 even for odd\n ``transition_frames`` \u2014 and the overall length equals the sum of inputs.\n\nAll inputs must share the same pixel dimensions. Output frame rate defaults to the\nfirst input's fps; override with ``fps`` to force a specific rate (the frames are not\nresampled, only the container is encoded at the new rate).",
"node_pack": "invokeai",
"properties": {
"board": {
@@ -71352,179 +76665,185 @@
"title": "Use Cache",
"type": "boolean"
},
- "image": {
+ "videos": {
"anyOf": [
{
- "$ref": "#/components/schemas/ImageField"
+ "items": {
+ "$ref": "#/components/schemas/VideoField"
+ },
+ "minItems": 2,
+ "type": "array"
},
{
"type": "null"
}
],
"default": null,
- "description": "The image to use",
+ "description": "Videos to concatenate, in order. At least two are required.",
"field_kind": "input",
"input": "any",
- "orig_required": true
+ "orig_required": true,
+ "title": "Videos"
},
- "radius": {
- "default": 2,
- "description": "Unsharp mask radius",
- "exclusiveMinimum": 0,
+ "transition": {
+ "default": "cut",
+ "description": "Transition between consecutive clips.",
+ "enum": ["cut", "crossfade", "fade_through_black"],
"field_kind": "input",
"input": "any",
- "orig_default": 2,
+ "orig_default": "cut",
"orig_required": false,
- "title": "Radius",
- "type": "number"
+ "title": "Transition",
+ "type": "string"
},
- "strength": {
- "default": 50,
- "description": "Unsharp mask strength",
+ "transition_frames": {
+ "default": 8,
+ "description": "Length of each transition in frames. Ignored when transition is 'cut'.",
"field_kind": "input",
"input": "any",
+ "maximum": 240,
"minimum": 0,
- "orig_default": 50,
+ "orig_default": 8,
"orig_required": false,
- "title": "Strength",
- "type": "number"
+ "title": "Transition Frames",
+ "type": "integer"
+ },
+ "fps": {
+ "anyOf": [
+ {
+ "maximum": 120,
+ "minimum": 1,
+ "type": "integer"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Output frame rate. Defaults to the first input's fps.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Fps"
},
"type": {
- "const": "unsharp_mask",
- "default": "unsharp_mask",
+ "const": "video_concat",
+ "default": "video_concat",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["image", "unsharp_mask"],
- "title": "Unsharp Mask",
+ "tags": ["video", "concat", "transition"],
+ "title": "Concatenate Videos",
"type": "object",
- "version": "1.2.2",
+ "version": "1.0.0",
"output": {
- "$ref": "#/components/schemas/ImageOutput"
+ "$ref": "#/components/schemas/VideoOutput"
}
},
- "UnstarredImagesResult": {
+ "VideoDTO": {
"properties": {
- "affected_boards": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Affected Boards",
- "description": "The ids of boards affected by the delete operation"
+ "video_name": {
+ "type": "string",
+ "title": "Video Name",
+ "description": "The unique name of the video."
},
- "unstarred_images": {
- "items": {
- "type": "string"
- },
- "type": "array",
- "title": "Unstarred Images",
- "description": "The names of the images that were unstarred"
- }
- },
- "type": "object",
- "required": ["affected_boards", "unstarred_images"],
- "title": "UnstarredImagesResult"
- },
- "UpdateAppGenerationSettingsRequest": {
- "properties": {
- "image_subfolder_strategy": {
+ "video_url": {
"type": "string",
- "enum": ["flat", "date", "type", "hash"],
- "title": "Image Subfolder Strategy",
- "description": "Strategy for organizing images into subfolders."
+ "title": "Video Url",
+ "description": "The URL of the video file (MP4)."
},
- "max_queue_history": {
+ "thumbnail_url": {
+ "type": "string",
+ "title": "Thumbnail Url",
+ "description": "The URL of the video's first-frame thumbnail (WebP)."
+ },
+ "video_origin": {
+ "$ref": "#/components/schemas/ResourceOrigin",
+ "description": "The origin of the video."
+ },
+ "video_category": {
+ "$ref": "#/components/schemas/ImageCategory",
+ "description": "The category of the video (reuses ImageCategory)."
+ },
+ "width": {
+ "type": "integer",
+ "title": "Width",
+ "description": "The pixel width of the video."
+ },
+ "height": {
+ "type": "integer",
+ "title": "Height",
+ "description": "The pixel height of the video."
+ },
+ "duration": {
+ "type": "number",
+ "title": "Duration",
+ "description": "The duration of the video in seconds."
+ },
+ "fps": {
"anyOf": [
{
- "type": "integer",
- "minimum": 0.0
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Max Queue History",
- "description": "Keep the last N completed, failed, and canceled queue items on startup. Set to 0 to prune all terminal items."
- }
- },
- "type": "object",
- "title": "UpdateAppGenerationSettingsRequest",
- "description": "Writable generation-related app settings."
- },
- "UserDTO": {
- "properties": {
- "user_id": {
- "type": "string",
- "title": "User Id",
- "description": "Unique user identifier"
+ "title": "Fps",
+ "description": "The frames-per-second of the video, if known."
},
- "email": {
- "type": "string",
- "title": "Email",
- "description": "User email address"
- },
- "display_name": {
+ "created_at": {
"anyOf": [
{
- "type": "string"
+ "type": "string",
+ "format": "date-time"
},
{
- "type": "null"
+ "type": "string"
}
],
- "title": "Display Name",
- "description": "Display name"
- },
- "is_admin": {
- "type": "boolean",
- "title": "Is Admin",
- "description": "Whether user has admin privileges",
- "default": false
- },
- "is_active": {
- "type": "boolean",
- "title": "Is Active",
- "description": "Whether user account is active",
- "default": true
- },
- "created_at": {
- "type": "string",
- "format": "date-time",
"title": "Created At",
- "description": "When the user was created"
+ "description": "The created timestamp of the video."
},
"updated_at": {
- "type": "string",
- "format": "date-time",
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "string"
+ }
+ ],
"title": "Updated At",
- "description": "When the user was last updated"
+ "description": "The updated timestamp of the video."
},
- "last_login_at": {
+ "deleted_at": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
+ {
+ "type": "string"
+ },
{
"type": "null"
}
],
- "title": "Last Login At",
- "description": "When user last logged in"
- }
- },
- "type": "object",
- "required": ["user_id", "email", "created_at", "updated_at"],
- "title": "UserDTO",
- "description": "User data transfer object."
- },
- "UserProfileUpdateRequest": {
- "properties": {
- "display_name": {
+ "title": "Deleted At",
+ "description": "The deleted timestamp of the video."
+ },
+ "is_intermediate": {
+ "type": "boolean",
+ "title": "Is Intermediate",
+ "description": "Whether this is an intermediate video."
+ },
+ "session_id": {
"anyOf": [
{
"type": "string"
@@ -71533,10 +76852,10 @@
"type": "null"
}
],
- "title": "Display Name",
- "description": "New display name"
+ "title": "Session Id",
+ "description": "The session ID that produced this video, if any."
},
- "current_password": {
+ "node_id": {
"anyOf": [
{
"type": "string"
@@ -71545,10 +76864,26 @@
"type": "null"
}
],
- "title": "Current Password",
- "description": "Current password (required when changing password)"
+ "title": "Node Id",
+ "description": "The node ID that produced this video, if any."
},
- "new_password": {
+ "starred": {
+ "type": "boolean",
+ "title": "Starred",
+ "description": "Whether this video is starred."
+ },
+ "has_workflow": {
+ "type": "boolean",
+ "title": "Has Workflow",
+ "description": "Whether this video has a workflow associated."
+ },
+ "video_subfolder": {
+ "type": "string",
+ "title": "Video Subfolder",
+ "description": "The subfolder where the video is stored on disk.",
+ "default": ""
+ },
+ "board_id": {
"anyOf": [
{
"type": "string"
@@ -71557,40 +76892,81 @@
"type": "null"
}
],
- "title": "New Password",
- "description": "New password"
+ "title": "Board Id",
+ "description": "The id of the board the video belongs to, if one exists."
}
},
"type": "object",
- "title": "UserProfileUpdateRequest",
- "description": "Request body for a user to update their own profile."
+ "required": [
+ "video_name",
+ "video_url",
+ "thumbnail_url",
+ "video_origin",
+ "video_category",
+ "width",
+ "height",
+ "duration",
+ "created_at",
+ "updated_at",
+ "is_intermediate",
+ "starred",
+ "has_workflow"
+ ],
+ "title": "VideoDTO",
+ "description": "Deserialized video record, enriched for the frontend."
},
- "VAEField": {
+ "VideoField": {
"properties": {
- "vae": {
- "$ref": "#/components/schemas/ModelIdentifierField",
- "description": "Info to load vae submodel"
- },
- "seamless_axes": {
- "description": "Axes(\"x\" and \"y\") to which apply seamless",
- "items": {
- "type": "string"
- },
- "title": "Seamless Axes",
- "type": "array"
+ "video_name": {
+ "type": "string",
+ "title": "Video Name",
+ "description": "The name of the video"
}
},
- "required": ["vae"],
- "title": "VAEField",
- "type": "object"
+ "type": "object",
+ "required": ["video_name"],
+ "title": "VideoField",
+ "description": "A video primitive field"
},
- "VAELoaderInvocation": {
- "category": "model",
+ "VideoFrameExtractInvocation": {
+ "category": "image",
"class": "invocation",
- "classification": "stable",
- "description": "Loads a VAE model, outputting a VaeLoaderOutput",
+ "classification": "prototype",
+ "description": "Extract a single frame from a video and save it as an image.\n\n``frame_index`` is 0-based. Negative indices count from the end, so the\ndefault of -1 returns the final frame \u2014 the typical setup for chaining\nI2V clips into a longer sequence.",
"node_pack": "invokeai",
"properties": {
+ "board": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/BoardField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
+ },
+ "metadata": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/MetadataField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
+ },
"id": {
"description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
"field_kind": "node_attribute",
@@ -71615,125 +76991,209 @@
"title": "Use Cache",
"type": "boolean"
},
- "vae_model": {
+ "video": {
"anyOf": [
{
- "$ref": "#/components/schemas/ModelIdentifierField"
+ "$ref": "#/components/schemas/VideoField"
},
{
"type": "null"
}
],
"default": null,
- "description": "VAE model to load",
+ "description": "The video to extract a frame from.",
"field_kind": "input",
"input": "any",
- "orig_required": true,
- "title": "VAE",
- "ui_model_base": ["sd-1", "sd-2", "sdxl", "sd-3", "flux", "flux2"],
- "ui_model_type": ["vae"]
+ "orig_required": true
+ },
+ "frame_index": {
+ "default": -1,
+ "description": "Index of the frame to extract. 0 = first frame, -1 = last frame, -2 = second-to-last, etc.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": -1,
+ "orig_required": false,
+ "title": "Frame Index",
+ "type": "integer",
+ "ui_component": "video-frame-index"
},
"type": {
- "const": "vae_loader",
- "default": "vae_loader",
+ "const": "video_frame_extract",
+ "default": "video_frame_extract",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
"required": ["type", "id"],
- "tags": ["vae", "model"],
- "title": "VAE Model - SD1.5, SD2, SDXL, SD3, FLUX",
+ "tags": ["video", "image", "frame"],
+ "title": "Frame from Video",
"type": "object",
- "version": "1.0.4",
+ "version": "1.1.0",
"output": {
- "$ref": "#/components/schemas/VAEOutput"
+ "$ref": "#/components/schemas/ImageOutput"
}
},
- "VAEOutput": {
- "class": "output",
- "description": "Base class for invocations that output a VAE field",
+ "VideoInvocation": {
+ "category": "primitives",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "A video primitive value. Drop a video onto the field to make it available as an input\nto downstream nodes (e.g. Frame from Video, Concatenate Videos).",
+ "node_pack": "invokeai",
"properties": {
- "vae": {
- "$ref": "#/components/schemas/VAEField",
- "description": "VAE",
- "field_kind": "output",
- "title": "VAE",
- "ui_hidden": false
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "video": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/VideoField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "The video to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
"type": {
- "const": "vae_output",
- "default": "vae_output",
+ "const": "video",
+ "default": "video",
"field_kind": "node_attribute",
"title": "type",
"type": "string"
}
},
- "required": ["output_meta", "vae", "type", "type"],
- "title": "VAEOutput",
- "type": "object"
+ "required": ["type", "id"],
+ "tags": ["primitives", "video"],
+ "title": "Video Primitive",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/VideoOutput"
+ }
},
- "VAE_Checkpoint_Anima_Config": {
+ "VideoNamesResult": {
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "video_names": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Video Names",
+ "description": "Ordered list of video names"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "starred_count": {
+ "type": "integer",
+ "title": "Starred Count",
+ "description": "Number of starred videos (when starred_first=True)"
},
- "file_size": {
+ "total_count": {
"type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "title": "Total Count",
+ "description": "Total number of videos matching the query"
+ }
+ },
+ "type": "object",
+ "required": ["video_names", "starred_count", "total_count"],
+ "title": "VideoNamesResult",
+ "description": "Response containing ordered video names with metadata for optimistic updates."
+ },
+ "VideoOutput": {
+ "class": "output",
+ "description": "Output of a node that produces a video file (e.g. Wan 2.2 latents-to-video).",
+ "properties": {
+ "video": {
+ "$ref": "#/components/schemas/VideoField",
+ "description": "The output video",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "width": {
+ "description": "The width of the video in pixels",
+ "field_kind": "output",
+ "title": "Width",
+ "type": "integer",
+ "ui_hidden": false
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "height": {
+ "description": "The height of the video in pixels",
+ "field_kind": "output",
+ "title": "Height",
+ "type": "integer",
+ "ui_hidden": false
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "num_frames": {
+ "description": "The number of frames in the video",
+ "field_kind": "output",
+ "title": "Num Frames",
+ "type": "integer",
+ "ui_hidden": false
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "fps": {
+ "description": "The frames-per-second of the video",
+ "field_kind": "output",
+ "title": "Fps",
+ "type": "number",
+ "ui_hidden": false
},
- "source_api_response": {
+ "duration": {
+ "description": "The duration of the video in seconds",
+ "field_kind": "output",
+ "title": "Duration",
+ "type": "number",
+ "ui_hidden": false
+ },
+ "type": {
+ "const": "video_output",
+ "default": "video_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "video", "width", "height", "num_frames", "fps", "duration", "type", "type"],
+ "title": "VideoOutput",
+ "type": "object"
+ },
+ "VideoRecordChanges": {
+ "properties": {
+ "video_category": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageCategory"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "description": "The video's new category."
},
- "source_url": {
+ "session_id": {
"anyOf": [
{
"type": "string"
@@ -71742,101 +77202,96 @@
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "title": "Session Id",
+ "description": "The video's new session ID."
},
- "cover_image": {
+ "is_intermediate": {
"anyOf": [
{
- "type": "string"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "title": "Is Intermediate",
+ "description": "The video's new `is_intermediate` flag."
},
- "config_path": {
+ "starred": {
"anyOf": [
{
- "type": "string"
+ "type": "boolean"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "type": {
+ "title": "Starred",
+ "description": "The video's new `starred` state."
+ }
+ },
+ "additionalProperties": true,
+ "type": "object",
+ "title": "VideoRecordChanges",
+ "description": "Allowed mutations on a video record."
+ },
+ "VideoUrlsDTO": {
+ "properties": {
+ "video_name": {
"type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
+ "title": "Video Name",
+ "description": "The unique name of the video."
},
- "format": {
+ "video_url": {
"type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "title": "Video Url",
+ "description": "The URL of the video file (MP4)."
},
- "base": {
+ "thumbnail_url": {
"type": "string",
- "const": "anima",
- "title": "Base",
- "default": "anima"
+ "title": "Thumbnail Url",
+ "description": "The URL of the video's first-frame thumbnail (WebP)."
}
},
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base"
- ],
- "title": "VAE_Checkpoint_Anima_Config",
- "description": "Model config for Anima QwenImage VAE checkpoint models (AutoencoderKLQwenImage)."
+ "required": ["video_name", "video_url", "thumbnail_url"],
+ "title": "VideoUrlsDTO",
+ "description": "The URLs for a video and its thumbnail."
},
- "VAE_Checkpoint_FLUX_Config": {
+ "VirtualSubBoardDTO": {
"properties": {
- "key": {
+ "virtual_board_id": {
"type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "title": "Virtual Board Id",
+ "description": "The virtual board ID, e.g. 'by_date:2026-03-18'."
},
- "hash": {
+ "board_name": {
"type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "title": "Board Name",
+ "description": "The display name of the virtual sub-board, e.g. '2026-03-18'."
},
- "path": {
+ "date": {
"type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "title": "Date",
+ "description": "The ISO date string, e.g. '2026-03-18'."
},
- "file_size": {
+ "image_count": {
"type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "title": "Image Count",
+ "description": "The number of general images for this date."
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "asset_count": {
+ "type": "integer",
+ "title": "Asset Count",
+ "description": "The number of asset images for this date."
},
- "description": {
+ "video_count": {
+ "type": "integer",
+ "title": "Video Count",
+ "description": "The number of videos for this date.",
+ "default": 0
+ },
+ "cover_image_name": {
"anyOf": [
{
"type": "string"
@@ -71845,19 +77300,10 @@
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "title": "Cover Image Name",
+ "description": "The most recent image name for this date."
},
- "source_api_response": {
+ "cover_video_name": {
"anyOf": [
{
"type": "string"
@@ -71866,890 +77312,1348 @@
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "title": "Cover Video Name",
+ "description": "The most recent video name for this date. Set instead of cover_image_name when the newest item for the date is a video."
+ }
+ },
+ "type": "object",
+ "required": ["virtual_board_id", "board_name", "date", "image_count", "asset_count"],
+ "title": "VirtualSubBoardDTO",
+ "description": "A virtual sub-board computed from image/video metadata, not stored in the database."
+ },
+ "WanConditioningField": {
+ "description": "A Wan 2.2 conditioning tensor primitive value.\n\nWan conditioning is the UMT5-XXL hidden state for the prompt plus an attention\nmask marking valid (non-padding) tokens.",
+ "properties": {
+ "conditioning_name": {
+ "description": "The name of conditioning tensor",
+ "title": "Conditioning Name",
+ "type": "string"
+ }
+ },
+ "required": ["conditioning_name"],
+ "title": "WanConditioningField",
+ "type": "object"
+ },
+ "WanConditioningOutput": {
+ "class": "output",
+ "description": "Base class for nodes that output a Wan 2.2 text conditioning tensor.",
+ "properties": {
+ "conditioning": {
+ "$ref": "#/components/schemas/WanConditioningField",
+ "description": "Conditioning tensor",
+ "field_kind": "output",
+ "ui_hidden": false
},
- "source_url": {
+ "type": {
+ "const": "wan_conditioning_output",
+ "default": "wan_conditioning_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "conditioning", "type", "type"],
+ "title": "WanConditioningOutput",
+ "type": "object"
+ },
+ "WanDenoiseInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Run the denoising process with a Wan 2.2 model.\n\nDrives a flow-matching Euler schedule via Diffusers'\n``FlowMatchEulerDiscreteScheduler``. CFG is supported when negative\nconditioning is provided and ``guidance_scale != 1.0``.\n\nFor Wan 2.2 A14B the high-noise expert handles timesteps at and above\n``boundary_ratio * num_train_timesteps``; the low-noise expert handles\ntimesteps below. Both experts share the model cache; only the active one is\nGPU-resident at any time.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "transformer": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanTransformerField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "Wan transformer field (transformer + optional dual-expert metadata).",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Transformer"
},
- "cover_image": {
+ "positive_conditioning": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanConditioningField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "config_path": {
+ "negative_conditioning": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanConditioningField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "Negative conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
+ "ref_image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/WanRefImageConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Reference-image (VAE-latent) conditioning for Wan 2.2 I2V.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Reference Image"
},
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "latents": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/LatentsField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "base": {
- "type": "string",
- "const": "flux",
- "title": "Base",
- "default": "flux"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base"
- ],
- "title": "VAE_Checkpoint_FLUX_Config"
- },
- "VAE_Checkpoint_Flux2_Config": {
- "properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "denoise_mask": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/DenoiseMaskField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "denoising_start": {
+ "default": 0.0,
+ "description": "When to start denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 0.0,
+ "orig_required": false,
+ "title": "Denoising Start",
+ "type": "number"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "denoising_end": {
+ "default": 1.0,
+ "description": "When to stop denoising, expressed a percentage of total steps",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 1,
+ "minimum": 0,
+ "orig_default": 1.0,
+ "orig_required": false,
+ "title": "Denoising End",
+ "type": "number"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "add_noise": {
+ "default": true,
+ "description": "Add noise based on denoising start.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": true,
+ "orig_required": false,
+ "title": "Add Noise",
+ "type": "boolean"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "guidance_scale": {
+ "default": 4.0,
+ "description": "Classifier-free guidance scale. 4.0 is the Wan 2.2 default for A14B; TI2V-5B can tolerate higher values up to ~5.5.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1.0,
+ "orig_default": 4.0,
+ "orig_required": false,
+ "title": "Guidance Scale",
+ "type": "number"
},
- "description": {
+ "guidance_scale_low_noise": {
"anyOf": [
{
- "type": "string"
+ "minimum": 0.0,
+ "type": "number"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
+ "default": null,
+ "description": "Optional separate CFG scale for the low-noise expert (Wan 2.2 A14B only). Values below 1.0 (including 0) fall back to the primary 'Guidance Scale'. Ignored for TI2V-5B.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Guidance Scale (Low Noise)"
},
- "source": {
+ "width": {
+ "default": 1024,
+ "description": "Width of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height of the generated image.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "steps": {
+ "default": 40,
+ "description": "Number of denoising steps.",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 40,
+ "orig_required": false,
+ "title": "Steps",
+ "type": "integer"
+ },
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
+ },
+ "type": {
+ "const": "wan_denoise",
+ "default": "wan_denoise",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "wan"],
+ "title": "Denoise - Wan 2.2",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
+ },
+ "WanI2VIdealDimensionsInvocation": {
+ "category": "video",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Ideal dimensions for the Wan 2.2 A14B models (I2V-A14B and T2V-A14B).\n\nUse this node for the A14B family. For the TI2V-5B model use \"Wan 2.2 TI2V\nIdeal Dimensions\" instead \u2014 TI2V-5B requires multiples of 32, and feeding it\nthese multiples-of-16 dims fails the patchify step.\n\nScales the input W\u00d7H so the shorter side equals the chosen preset (480 / 720 /\n1080 px), then snaps each dimension to a multiple of 16 (the A14B pixel-grid\nconstraint). Wire from ``Image Primitive``'s width/height outputs and into\n``wan_ref_image_encoder`` / ``wan_denoise``.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "width": {
+ "default": 1024,
+ "description": "Source image width in pixels.",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Source image height in pixels.",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "target_resolution": {
+ "default": "720p",
+ "description": "Short-side resolution preset. 480p and 720p are Wan 2.2's native training resolutions; 1080p works but is extrapolation and costs ~2.25x the memory of 720p.",
+ "enum": ["480p", "720p", "1080p"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "720p",
+ "orig_required": false,
+ "title": "Target Resolution",
"type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "ui_choice_labels": {
+ "1080p": "1080p (extrapolated \u2014 not a Wan training size)",
+ "480p": "480p (Wan native)",
+ "720p": "720p (Wan native, default)"
+ }
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "rounding": {
+ "default": "nearest",
+ "description": "How to snap each dimension to a multiple of 16. 'floor' rounds down \u2014 safest for VRAM, guaranteed not to exceed the unsnapped target. 'ceiling' rounds up. 'nearest' minimizes aspect-ratio drift (default).",
+ "enum": ["nearest", "floor", "ceiling"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "nearest",
+ "orig_required": false,
+ "title": "Rounding",
+ "type": "string"
},
- "source_api_response": {
+ "type": {
+ "const": "wan_i2v_ideal_dimensions",
+ "default": "wan_i2v_ideal_dimensions",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["wan", "video", "dimensions", "math"],
+ "title": "Wan 2.2 I2V Ideal Dimensions (A14B)",
+ "type": "object",
+ "version": "1.1.0",
+ "output": {
+ "$ref": "#/components/schemas/IdealSizeOutput"
+ }
+ },
+ "WanImageToLatentsInvocation": {
+ "category": "image",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Encodes an image with the Wan VAE (AutoencoderKLWan).\n\nThe output latents have the temporal dimension squeezed out, so downstream\nnodes see 4D ``[B, C, H, W]``. The denoise loop re-adds ``T=1`` before\nfeeding the transformer.",
+ "node_pack": "invokeai",
+ "properties": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "cover_image": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "The image to encode.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "config_path": {
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
"type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
- },
- "base": {
- "type": "string",
- "const": "flux2",
- "title": "Base",
- "default": "flux2"
+ "const": "wan_i2l",
+ "default": "wan_i2l",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["image", "latents", "vae", "i2l", "wan"],
+ "title": "Image to Latents - Wan 2.2",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base"
- ],
- "title": "VAE_Checkpoint_Flux2_Config",
- "description": "Model config for FLUX.2 VAE checkpoint models (AutoencoderKLFlux2)."
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
- "VAE_Checkpoint_QwenImage_Config": {
+ "WanLatentsToImageInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Decodes Wan latents back to RGB.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "cover_image": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "config_path": {
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
"type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
- },
- "base": {
- "type": "string",
- "const": "qwen-image",
- "title": "Base",
- "default": "qwen-image"
+ "const": "wan_l2i",
+ "default": "wan_l2i",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["latents", "image", "vae", "l2i", "wan"],
+ "title": "Latents to Image - Wan 2.2",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base"
- ],
- "title": "VAE_Checkpoint_QwenImage_Config",
- "description": "Model config for Qwen Image VAE checkpoint models (AutoencoderKLQwenImage)."
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/ImageOutput"
+ }
},
- "VAE_Checkpoint_SD1_Config": {
+ "WanLatentsToVideoInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Decode 5D Wan latents to RGB frames and encode an MP4.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "board": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/BoardField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
- },
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
- },
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "default": null,
+ "description": "The board to save the image to",
+ "field_kind": "internal",
+ "input": "direct",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_api_response": {
+ "metadata": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/MetadataField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional metadata to be saved with the image",
+ "field_kind": "internal",
+ "input": "connection",
+ "orig_required": false,
+ "ui_hidden": false
},
- "source_url": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "cover_image": {
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "latents": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LatentsField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "Latents tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "config_path": {
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
- },
- "type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
+ "fps": {
+ "default": 16,
+ "description": "Frames-per-second for the encoded MP4. Wan 2.2 was trained at 16 FPS.",
+ "field_kind": "input",
+ "input": "any",
+ "maximum": 120,
+ "minimum": 1,
+ "orig_default": 16,
+ "orig_required": false,
+ "title": "Fps",
+ "type": "integer"
},
- "base": {
- "type": "string",
- "const": "sd-1",
- "title": "Base",
- "default": "sd-1"
+ "type": {
+ "const": "wan_l2v",
+ "default": "wan_l2v",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["latents", "video", "vae", "l2v", "wan"],
+ "title": "Latents to Video - Wan 2.2",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base"
- ],
- "title": "VAE_Checkpoint_SD1_Config"
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/VideoOutput"
+ }
},
- "VAE_Checkpoint_SD2_Config": {
+ "WanLoRACollectionLoader": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Apply a collection of LoRAs to the Wan 2.2 transformer(s).\n\nEach LoRA is routed to the primary and/or low-noise list based on its\nrecorded ``expert`` tag (set by the probe from the filename). Untagged\nLoRAs go to both lists.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "source_api_response": {
+ "loras": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ {
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "type": "array"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "LoRAs to apply. May be a single LoRA or a collection.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "LoRAs",
+ "ui_model_base": ["wan"],
+ "ui_model_type": ["lora"]
},
- "source_url": {
+ "transformer": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanTransformerField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Wan Transformer"
},
- "cover_image": {
+ "type": {
+ "const": "wan_lora_collection_loader",
+ "default": "wan_lora_collection_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["lora", "model", "wan"],
+ "title": "Apply LoRA Collection - Wan 2.2",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/WanLoRALoaderOutput"
+ }
+ },
+ "WanLoRALoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Apply a LoRA to the Wan 2.2 transformer(s).\n\nFor A14B (dual expert) the LoRA's recorded ``expert`` field determines\nwhich expert list it lands in: ``\"high\"`` -> primary list, ``\"low\"`` ->\nlow-noise list, ``None`` (untagged) -> both lists. Use the ``target``\nfield to override.\n\nFor TI2V-5B (single transformer) only the primary list is used at denoise\ntime; the low-noise routing is harmless but ignored.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
+ },
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
+ },
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "lora": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "LoRA model to load",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "LoRA",
+ "ui_model_base": ["wan"],
+ "ui_model_type": ["lora"]
},
- "config_path": {
+ "weight": {
+ "default": 0.75,
+ "description": "The weight at which the LoRA is applied to each model",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0.75,
+ "orig_required": false,
+ "title": "Weight",
+ "type": "number"
+ },
+ "target": {
+ "default": "auto",
+ "description": "Which expert(s) to apply this LoRA to. 'auto' uses the LoRA's recorded expert tag (or both if untagged); 'both'/'high'/'low' override it.",
+ "enum": ["auto", "both", "high", "low"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "auto",
+ "orig_required": false,
+ "title": "Target",
+ "type": "string"
+ },
+ "transformer": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanTransformerField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Wan Transformer"
},
"type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
- },
- "base": {
- "type": "string",
- "const": "sd-2",
- "title": "Base",
- "default": "sd-2"
+ "const": "wan_lora_loader",
+ "default": "wan_lora_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["lora", "model", "wan"],
+ "title": "Apply LoRA - Wan 2.2",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base"
- ],
- "title": "VAE_Checkpoint_SD2_Config"
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/WanLoRALoaderOutput"
+ }
},
- "VAE_Checkpoint_SDXL_Config": {
+ "WanLoRALoaderOutput": {
+ "class": "output",
+ "description": "Wan 2.2 LoRA loader output.",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
- },
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
- },
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
- },
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
- },
- "description": {
+ "transformer": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanTransformerField"
},
{
"type": "null"
}
],
- "title": "Description",
- "description": "Model description"
+ "default": null,
+ "description": "Transformer",
+ "field_kind": "output",
+ "title": "Wan Transformer",
+ "ui_hidden": false
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "type": {
+ "const": "wan_lora_loader_output",
+ "default": "wan_lora_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "transformer", "type", "type"],
+ "title": "WanLoRALoaderOutput",
+ "type": "object"
+ },
+ "WanLoRAVariantType": {
+ "type": "string",
+ "enum": ["a14b", "5b"],
+ "title": "WanLoRAVariantType",
+ "description": "Wan 2.2 LoRA variants, identifying which model family a LoRA targets.\n\nDetected from the LoRA's inner attention dim: A14B has ``inner_dim=5120``,\nTI2V-5B has ``inner_dim=3072``. A14B and 5B LoRAs are NOT interchangeable \u2014\napplying one against the wrong main model crashes in the layer patcher\nwith a tensor-shape error."
+ },
+ "WanModelLoaderInvocation": {
+ "category": "model",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Loads a Wan 2.2 model, outputting its submodels.\n\nComponents can be mixed and matched, mirroring the Qwen Image loader pattern:\n\n- Transformer(s):\n * Diffusers main: emits ``transformer/`` and (for A14B) ``transformer_2/``\n from the same model record.\n * GGUF main: emits the single GGUF as the primary transformer; for A14B\n the second-expert GGUF must be wired to ``Transformer (Low Noise)``.\n- VAE: standalone Wan VAE > main (if Diffusers) > Component Source (Diffusers).\n- UMT5-XXL encoder: standalone Wan T5 encoder > main (if Diffusers) >\n Component Source (Diffusers).\n\nThe Component Source slot lets users supply a Diffusers Wan main model purely\nfor VAE / encoder extraction when the actual transformer is in a single-file\nformat. Together, the standalone VAE + standalone encoder let a GGUF\ntransformer run without a full ~30 GB Diffusers install.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "source_api_response": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "model": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Wan 2.2 model (Transformer) to load",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Transformer",
+ "ui_model_base": ["wan"],
+ "ui_model_type": ["main"]
+ },
+ "transformer_low_noise_model": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Optional second GGUF transformer for the A14B low-noise expert. Only relevant when the main model is a single-file GGUF and the variant is A14B; ignored when the main is a Diffusers A14B (both experts are pulled from transformer/ and transformer_2/ already) or when the variant is TI2V-5B.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Transformer (Low Noise)",
+ "ui_model_base": ["wan"],
+ "ui_model_format": ["gguf_quantized"],
+ "ui_model_type": ["main"]
},
- "source_url": {
+ "vae_model": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "Standalone Wan VAE model. If not set, the VAE is loaded from the main model (when in Diffusers format) or from the Component Source.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "VAE",
+ "ui_model_base": ["wan"],
+ "ui_model_type": ["vae"]
},
- "cover_image": {
+ "wan_t5_encoder_model": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
+ "default": null,
+ "description": "Standalone Wan UMT5-XXL encoder. If not set, the encoder is loaded from the main model (when in Diffusers format) or from the Component Source.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Wan T5 Encoder",
+ "ui_model_type": ["wan_t5_encoder"]
},
- "config_path": {
+ "component_source": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Config Path",
- "description": "Path to the config for this model, if any."
+ "default": null,
+ "description": "Diffusers Wan main model to extract VAE and/or encoder from. Use this if you don't have separate VAE/encoder models. Ignored for any submodel that is provided separately.",
+ "field_kind": "input",
+ "input": "direct",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Component Source (Diffusers)",
+ "ui_model_base": ["wan"],
+ "ui_model_format": ["diffusers"],
+ "ui_model_type": ["main"]
},
"type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
- },
- "format": {
- "type": "string",
- "const": "checkpoint",
- "title": "Format",
- "default": "checkpoint"
- },
- "base": {
- "type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
+ "const": "wan_model_loader",
+ "default": "wan_model_loader",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["model", "type", "id"],
+ "tags": ["model", "wan"],
+ "title": "Main Model - Wan 2.2",
"type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "config_path",
- "type",
- "format",
- "base"
- ],
- "title": "VAE_Checkpoint_SDXL_Config"
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/WanModelLoaderOutput"
+ }
},
- "VAE_Diffusers_Flux2_Config": {
+ "WanModelLoaderOutput": {
+ "class": "output",
+ "description": "Wan 2.2 model loader output.",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "transformer": {
+ "$ref": "#/components/schemas/WanTransformerField",
+ "description": "Wan transformer (one or two experts depending on the variant)",
+ "field_kind": "output",
+ "title": "Transformer",
+ "ui_hidden": false
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "wan_t5_encoder": {
+ "$ref": "#/components/schemas/WanT5EncoderField",
+ "description": "UMT5-XXL tokenizer and text encoder for Wan 2.2",
+ "field_kind": "output",
+ "title": "UMT5-XXL Encoder",
+ "ui_hidden": false
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "vae": {
+ "$ref": "#/components/schemas/VAEField",
+ "description": "VAE",
+ "field_kind": "output",
+ "title": "VAE",
+ "ui_hidden": false
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "type": {
+ "const": "wan_model_loader_output",
+ "default": "wan_model_loader_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "transformer", "wan_t5_encoder", "vae", "type", "type"],
+ "title": "WanModelLoaderOutput",
+ "type": "object"
+ },
+ "WanRefImageConditioningField": {
+ "description": "Reference-image conditioning for Wan 2.2 I2V.\n\nCarries the 20-channel VAE-latent condition tensor (4-channel first-frame\nmask + 16-channel ref-image latents). The denoise loop concatenates this\nto the 16-channel noise latents along the channel dim each step, producing\nthe 36-channel input the I2V-A14B transformer expects.\n\nAlso carries the spatial dims and frame count used to encode the image so\nthe denoise node can sanity-check the user's width/height/num_frames \u2014 a\nlatent temporal-dim mismatch is hard to debug from the downstream error.",
+ "properties": {
+ "condition_tensor_name": {
+ "description": "Name of the saved [1, 20, T_lat, H/8, W/8] condition tensor.",
+ "title": "Condition Tensor Name",
+ "type": "string"
},
- "name": {
- "type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "width": {
+ "description": "Image width used during VAE encoding (matches denoise width).",
+ "title": "Width",
+ "type": "integer"
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "height": {
+ "description": "Image height used during VAE encoding (matches denoise height).",
+ "title": "Height",
+ "type": "integer"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "num_frames": {
+ "default": 1,
+ "description": "Pixel-frame count the condition was built for. 1 for single-frame I2V (image output), 81+ for video.",
+ "title": "Num Frames",
+ "type": "integer"
+ }
+ },
+ "required": ["condition_tensor_name", "width", "height"],
+ "title": "WanRefImageConditioningField",
+ "type": "object"
+ },
+ "WanRefImageEncoderInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "VAE-encode a reference image into Wan 2.2 I2V conditioning.\n\nOutput is a ``[1, 20, T_lat, height // 8, width // 8]`` condition tensor\nthat the denoise loop concatenates to the 16-channel noise latents each\nstep, producing the 36-channel input the I2V-A14B transformer expects.\n\nFor image (single-frame) I2V leave ``num_frames=1`` (T_lat=1). For video\nI2V set ``num_frames`` to match the value on the video-denoise node\n(e.g. 81 for the Wan 2.2 reference defaults).\n\nSupply an optional ``end_image`` for **first-last-frame interpolation\n(FLF2V)** \u2014 the model then interpolates the motion from ``image`` (first\nframe) to ``end_image`` (final frame). FLF2V is I2V-A14B video only\n(``num_frames > 1``); it is not supported for TI2V-5B or single-frame I2V.\n\nOnly works with I2V-A14B (the denoise loop's variant gate enforces this).\nFor T2V or TI2V-5B, omit this node entirely.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "source_api_response": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Reference image to condition on (the first frame).",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true
},
- "source_url": {
+ "vae": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/VAEField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "VAE",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "VAE"
},
- "cover_image": {
+ "width": {
+ "default": 1024,
+ "description": "Width to resize the reference image to (must match denoise width).",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 1024,
+ "description": "Height to resize the reference image to (must match denoise height).",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "num_frames": {
+ "default": 1,
+ "description": "Pixel-frame count to build the condition for. Use 1 for single-frame image I2V. For video I2V, set this to match the video-denoise node's num_frames (and ensure (num_frames - 1) %% 4 == 0, e.g. 81).",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1,
+ "orig_default": 1,
+ "orig_required": false,
+ "title": "Number of Frames",
+ "type": "integer"
+ },
+ "end_image": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ImageField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
+ "default": null,
+ "description": "Optional end frame for first-last-frame interpolation (FLF2V). When set, the video interpolates from the reference image (first frame) to this image (final frame). I2V-A14B video only (num_frames > 1); not supported for TI2V-5B or single-frame I2V.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "End Image (FLF2V)"
},
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "type": {
+ "const": "wan_ref_image_encoder",
+ "default": "wan_ref_image_encoder",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["image", "conditioning", "wan", "i2v"],
+ "title": "Reference Image - Wan 2.2",
+ "type": "object",
+ "version": "1.2.0",
+ "output": {
+ "$ref": "#/components/schemas/WanRefImageOutput"
+ }
+ },
+ "WanRefImageOutput": {
+ "class": "output",
+ "description": "Output of a Wan 2.2 reference-image VAE-encoder.",
+ "properties": {
+ "ref_image": {
+ "$ref": "#/components/schemas/WanRefImageConditioningField",
+ "description": "VAE-latent reference-image conditioning for Wan 2.2 I2V.",
+ "field_kind": "output",
+ "title": "Reference Image",
+ "ui_hidden": false
},
"type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
+ "const": "wan_ref_image_output",
+ "default": "wan_ref_image_output",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["output_meta", "ref_image", "type", "type"],
+ "title": "WanRefImageOutput",
+ "type": "object"
+ },
+ "WanT5EncoderField": {
+ "description": "Field for the UMT5-XXL text encoder used by Wan 2.2 models.",
+ "properties": {
+ "tokenizer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load tokenizer submodel"
},
- "base": {
- "type": "string",
- "const": "flux2",
- "title": "Base",
- "default": "flux2"
+ "text_encoder": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Info to load text_encoder submodel"
+ },
+ "loras": {
+ "description": "LoRAs to apply on model loading",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "Loras",
+ "type": "array"
}
},
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "base"
- ],
- "title": "VAE_Diffusers_Flux2_Config",
- "description": "Model config for FLUX.2 VAE models in diffusers format (AutoencoderKLFlux2)."
+ "required": ["tokenizer", "text_encoder"],
+ "title": "WanT5EncoderField",
+ "type": "object"
},
- "VAE_Diffusers_SD1_Config": {
+ "WanT5Encoder_WanT5Encoder_Config": {
"properties": {
"key": {
"type": "string",
@@ -72833,27 +78737,23 @@
"title": "Cover Image",
"description": "Url for image to preview model"
},
- "format": {
+ "base": {
"type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
+ "const": "any",
+ "title": "Base",
+ "default": "any"
},
"type": {
"type": "string",
- "const": "vae",
+ "const": "wan_t5_encoder",
"title": "Type",
- "default": "vae"
+ "default": "wan_t5_encoder"
},
- "base": {
+ "format": {
"type": "string",
- "const": "sd-1",
- "title": "Base",
- "default": "sd-1"
+ "const": "wan_t5_encoder",
+ "title": "Format",
+ "default": "wan_t5_encoder"
}
},
"type": "object",
@@ -72869,62 +78769,142 @@
"source_api_response",
"source_url",
"cover_image",
- "format",
- "repo_variant",
+ "base",
"type",
- "base"
+ "format"
],
- "title": "VAE_Diffusers_SD1_Config"
+ "title": "WanT5Encoder_WanT5Encoder_Config",
+ "description": "UMT5-XXL encoder in diffusers folder layout.\n\nAccepts either:\n- A directory containing ``text_encoder/`` (and typically ``tokenizer/``) \u2500 the\n shape produced by ``Wan-AI/Wan2.2-T2V-A14B::text_encoder+tokenizer``.\n- A bare ``text_encoder/`` directory whose own ``config.json`` declares\n ``model_type: umt5``."
},
- "VAE_Diffusers_SDXL_Config": {
+ "WanTI2VIdealDimensionsInvocation": {
+ "category": "video",
+ "class": "invocation",
+ "classification": "stable",
+ "description": "Ideal dimensions for the Wan 2.2 TI2V-5B model.\n\nUse this node for TI2V-5B only. For the A14B models (I2V-A14B / T2V-A14B) use\n\"Wan 2.2 I2V Ideal Dimensions\" instead \u2014 those need multiples of 16, and this\nnode's multiples-of-32 dims would overshoot their pixel grid.\n\nIdentical to the A14B node but snaps each dimension to a multiple of 32 instead\nof 16: the Wan 2.2-VAE used by TI2V-5B applies 16x spatial compression and the\ntransformer adds a 2x patch on top, so pixel dims must divide by 32 for the\npatchify step. Wire from ``Image Primitive``'s width/height outputs and into\nthe matching ``wan_denoise`` inputs.",
+ "node_pack": "invokeai",
"properties": {
- "key": {
- "type": "string",
- "title": "Key",
- "description": "A unique key for this model."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "hash": {
- "type": "string",
- "title": "Hash",
- "description": "The hash of the model file(s)."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "path": {
- "type": "string",
- "title": "Path",
- "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "file_size": {
- "type": "integer",
- "title": "File Size",
- "description": "The size of the model in bytes."
+ "width": {
+ "default": 1024,
+ "description": "Source image width in pixels.",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
},
- "name": {
+ "height": {
+ "default": 1024,
+ "description": "Source image height in pixels.",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 1024,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "target_resolution": {
+ "default": "720p",
+ "description": "Short-side resolution preset. 480p and 720p are Wan 2.2's native training resolutions; 1080p works but is extrapolation and costs ~2.25x the memory of 720p.",
+ "enum": ["480p", "720p", "1080p"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "720p",
+ "orig_required": false,
+ "title": "Target Resolution",
"type": "string",
- "title": "Name",
- "description": "Name of the model."
+ "ui_choice_labels": {
+ "1080p": "1080p (extrapolated \u2014 not a Wan training size)",
+ "480p": "480p (Wan native)",
+ "720p": "720p (Wan native, default)"
+ }
},
- "description": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "null"
- }
- ],
- "title": "Description",
- "description": "Model description"
+ "rounding": {
+ "default": "nearest",
+ "description": "How to snap each dimension to a multiple of 32. 'floor' rounds down \u2014 safest for VRAM, guaranteed not to exceed the unsnapped target. 'ceiling' rounds up. 'nearest' minimizes aspect-ratio drift (default).",
+ "enum": ["nearest", "floor", "ceiling"],
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": "nearest",
+ "orig_required": false,
+ "title": "Rounding",
+ "type": "string"
},
- "source": {
- "type": "string",
- "title": "Source",
- "description": "The original source of the model (path, URL or repo_id)."
+ "type": {
+ "const": "wan_ti2v_ideal_dimensions",
+ "default": "wan_ti2v_ideal_dimensions",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["wan", "video", "dimensions", "math"],
+ "title": "Wan 2.2 TI2V Ideal Dimensions (5B)",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/IdealSizeOutput"
+ }
+ },
+ "WanTextEncoderInvocation": {
+ "category": "conditioning",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Encodes a text prompt for Wan 2.2 using the UMT5-XXL encoder.\n\nOutput is the encoder's last hidden state (shape: [seq_len=226, 4096]) plus\nan attention mask marking valid (non-padding) tokens. The Wan transformer\nconsumes these directly as ``encoder_hidden_states``.",
+ "node_pack": "invokeai",
+ "properties": {
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "source_type": {
- "$ref": "#/components/schemas/ModelSourceType",
- "description": "The type of source"
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "source_api_response": {
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
+ },
+ "prompt": {
"anyOf": [
{
"type": "string"
@@ -72933,149 +78913,295 @@
"type": "null"
}
],
- "title": "Source Api Response",
- "description": "The original API response from the source, as stringified JSON."
+ "default": null,
+ "description": "Text prompt for Wan 2.2.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_required": true,
+ "title": "Prompt",
+ "ui_component": "textarea"
},
- "source_url": {
+ "wan_t5_encoder": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanT5EncoderField"
},
{
"type": "null"
}
],
- "title": "Source Url",
- "description": "Optional URL for the model (e.g. download page or model page)."
+ "default": null,
+ "description": "UMT5-XXL tokenizer and text encoder for Wan 2.2",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "UMT5-XXL Encoder"
},
- "cover_image": {
+ "type": {
+ "const": "wan_text_encoder",
+ "default": "wan_text_encoder",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
+ }
+ },
+ "required": ["type", "id"],
+ "tags": ["prompt", "conditioning", "wan"],
+ "title": "Prompt - Wan 2.2",
+ "type": "object",
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/WanConditioningOutput"
+ }
+ },
+ "WanTransformerField": {
+ "description": "Transformer field for Wan 2.2 models.\n\nWan 2.2 A14B is a Mixture-of-Experts model with two transformer experts:\na high-noise expert (active at large timesteps) and a low-noise expert\n(active at small timesteps). TI2V-5B is a single-transformer model and only\npopulates ``transformer``.\n\n``boundary_ratio`` matches Diffusers' ``WanPipeline`` semantics: it's the\nboundary timestep as a fraction of ``num_train_timesteps`` (typically 1000),\nso ``boundary_ratio=0.875`` means the high-noise expert handles t >= 875 and\nthe low-noise expert handles t < 875.",
+ "properties": {
+ "transformer": {
+ "$ref": "#/components/schemas/ModelIdentifierField",
+ "description": "Primary transformer submodel. For A14B this is the high-noise expert."
+ },
+ "transformer_low_noise": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/ModelIdentifierField"
},
{
"type": "null"
}
],
- "title": "Cover Image",
- "description": "Url for image to preview model"
- },
- "format": {
- "type": "string",
- "const": "diffusers",
- "title": "Format",
- "default": "diffusers"
- },
- "repo_variant": {
- "$ref": "#/components/schemas/ModelRepoVariant",
- "default": ""
- },
- "type": {
- "type": "string",
- "const": "vae",
- "title": "Type",
- "default": "vae"
+ "default": null,
+ "description": "Low-noise transformer expert (Wan 2.2 A14B only). None for TI2V-5B."
},
- "base": {
- "type": "string",
- "const": "sdxl",
- "title": "Base",
- "default": "sdxl"
- }
- },
- "type": "object",
- "required": [
- "key",
- "hash",
- "path",
- "file_size",
- "name",
- "description",
- "source",
- "source_type",
- "source_api_response",
- "source_url",
- "cover_image",
- "format",
- "repo_variant",
- "type",
- "base"
- ],
- "title": "VAE_Diffusers_SDXL_Config"
- },
- "ValidationError": {
- "properties": {
- "loc": {
+ "loras": {
+ "description": "LoRAs to apply to the primary transformer. For A14B applied to the high-noise expert.",
"items": {
- "anyOf": [
- {
- "type": "string"
- },
- {
- "type": "integer"
- }
- ]
+ "$ref": "#/components/schemas/LoRAField"
},
- "type": "array",
- "title": "Location"
+ "title": "Loras",
+ "type": "array"
},
- "msg": {
- "type": "string",
- "title": "Message"
+ "loras_low_noise": {
+ "description": "Optional separate LoRAs for the low-noise expert (Wan 2.2 A14B). If empty and transformer_low_noise is set, the primary 'loras' list is reused.",
+ "items": {
+ "$ref": "#/components/schemas/LoRAField"
+ },
+ "title": "Loras Low Noise",
+ "type": "array"
},
- "type": {
- "type": "string",
- "title": "Error Type"
+ "boundary_ratio": {
+ "default": 0.875,
+ "description": "Boundary timestep as a fraction of num_train_timesteps (Wan 2.2 A14B only). High-noise expert: t >= boundary_ratio * num_train_timesteps. Low-noise expert: t below. Ignored for TI2V-5B.",
+ "maximum": 1.0,
+ "minimum": 0.0,
+ "title": "Boundary Ratio",
+ "type": "number"
}
},
- "type": "object",
- "required": ["loc", "msg", "type"],
- "title": "ValidationError"
+ "required": ["transformer"],
+ "title": "WanTransformerField",
+ "type": "object"
},
- "VirtualSubBoardDTO": {
+ "WanVariantType": {
+ "type": "string",
+ "enum": ["t2v_a14b", "i2v_a14b", "ti2v_5b"],
+ "title": "WanVariantType",
+ "description": "Wan 2.2 model variants.\n\nAll variants are used for image generation at num_frames=1. The A14B family\nis a Mixture-of-Experts (high-noise + low-noise) totalling ~28B params; the\nT2V sub-variant takes text only, while the I2V sub-variant additionally\nconditions on a reference image (encoded by the VAE and concatenated to the\nnoise latents along the channel dim \u2014 its transformer has ``in_channels=36``\ninstead of ``16``). TI2V-5B is a single ~5B transformer with a\nhigher-compression VAE (z_dim=48)."
+ },
+ "WanVideoDenoiseInvocation": {
+ "category": "latents",
+ "class": "invocation",
+ "classification": "prototype",
+ "description": "Run the Wan 2.2 denoising loop on a multi-frame latent tensor.\n\nThe output is a 5D ``[1, C, T_lat, H/8, W/8]`` latent tensor ready for\n:class:`WanLatentsToVideoInvocation` to VAE-decode and encode as MP4.\n\nMirrors :class:`WanDenoiseInvocation` for the per-step logic (CFG, MoE\nexpert swap at the boundary timestep, LoRA patching, scheduler selection).\nDifferences from the image denoise:\n\n* The noise tensor has a real temporal dim built from ``num_frames``.\n* The I2V condition is built across all latent frames (frame 0\n conditioned, rest zero) via\n :func:`encode_reference_image_to_video_condition` upstream \u2014 the\n ``ref_image`` field on this node carries a tensor of shape\n ``[1, 20, T_lat, H_lat, W_lat]`` instead of ``[1, 20, 1, ...]``.\n* No ``denoising_start`` / ``denoising_end`` / initial-latents inputs.\n The image denoise node uses those for img2img (noise injection on an\n existing latent), but image-conditioned video generation flows through\n the reference-frame conditioning mechanism instead \u2014 the first frame\n drives subsequent frames at every step, so a partial-schedule run from\n an initial latent has no analogue here. Run the schedule from t=1\n to t=0 every time. The base ``WanDenoiseInvocation`` still handles\n the img2img case for stills.",
+ "node_pack": "invokeai",
"properties": {
- "virtual_board_id": {
- "type": "string",
- "title": "Virtual Board Id",
- "description": "The virtual board ID, e.g. 'by_date:2026-03-18'."
+ "id": {
+ "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.",
+ "field_kind": "node_attribute",
+ "title": "Id",
+ "type": "string"
},
- "board_name": {
- "type": "string",
- "title": "Board Name",
- "description": "The display name of the virtual sub-board, e.g. '2026-03-18'."
+ "is_intermediate": {
+ "default": false,
+ "description": "Whether or not this is an intermediate invocation.",
+ "field_kind": "node_attribute",
+ "input": "direct",
+ "orig_required": true,
+ "title": "Is Intermediate",
+ "type": "boolean",
+ "ui_hidden": false,
+ "ui_type": "IsIntermediate"
},
- "date": {
- "type": "string",
- "title": "Date",
- "description": "The ISO date string, e.g. '2026-03-18'."
+ "use_cache": {
+ "default": true,
+ "description": "Whether or not to use the cache",
+ "field_kind": "node_attribute",
+ "title": "Use Cache",
+ "type": "boolean"
},
- "image_count": {
- "type": "integer",
- "title": "Image Count",
- "description": "The number of general images for this date."
+ "transformer": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/WanTransformerField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Wan transformer field. Supported: T2V-A14B / I2V-A14B (dual-expert) and TI2V-5B (single-expert, handles both T2V and I2V). All three accept a Reference Image input for image-to-video; A14B uses the 36-channel concat scheme while TI2V-5B uses the expand_timesteps first-frame-mask blend.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true,
+ "title": "Transformer"
},
- "asset_count": {
- "type": "integer",
- "title": "Asset Count",
- "description": "The number of asset images for this date."
+ "positive_conditioning": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/WanConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Positive conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_required": true
},
- "cover_image_name": {
+ "negative_conditioning": {
"anyOf": [
{
- "type": "string"
+ "$ref": "#/components/schemas/WanConditioningField"
},
{
"type": "null"
}
],
- "title": "Cover Image Name",
- "description": "The most recent image name for this date."
+ "default": null,
+ "description": "Negative conditioning tensor",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false
+ },
+ "ref_image": {
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/WanRefImageConditioningField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "Reference-image (VAE-latent) conditioning for Wan 2.2 I2V.",
+ "field_kind": "input",
+ "input": "connection",
+ "orig_default": null,
+ "orig_required": false,
+ "title": "Reference Image"
+ },
+ "guidance_scale": {
+ "default": 5.0,
+ "description": "Classifier-free guidance scale. Wan 2.2 video reference uses 5.0 for the high-noise expert and 4.0 for the low-noise expert.",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 1.0,
+ "orig_default": 5.0,
+ "orig_required": false,
+ "title": "Guidance Scale",
+ "type": "number"
+ },
+ "guidance_scale_low_noise": {
+ "anyOf": [
+ {
+ "minimum": 0.0,
+ "type": "number"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": 4.0,
+ "description": "Optional separate CFG scale for the low-noise expert (Wan 2.2 A14B only). Values below 1.0 fall back to the primary 'Guidance Scale'.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 4.0,
+ "orig_required": false,
+ "title": "Guidance Scale (Low Noise)"
+ },
+ "width": {
+ "default": 832,
+ "description": "Width of the generated video.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 832,
+ "orig_required": false,
+ "title": "Width",
+ "type": "integer"
+ },
+ "height": {
+ "default": 480,
+ "description": "Height of the generated video.",
+ "field_kind": "input",
+ "input": "any",
+ "multipleOf": 16,
+ "orig_default": 480,
+ "orig_required": false,
+ "title": "Height",
+ "type": "integer"
+ },
+ "num_frames": {
+ "default": 81,
+ "description": "Number of output frames. Must satisfy (num_frames - 1) %% 4 == 0 so the latent temporal dim divides cleanly. Wan 2.2 was trained at 81 frames @ 16 FPS (~5 s).",
+ "field_kind": "input",
+ "input": "any",
+ "minimum": 5,
+ "orig_default": 81,
+ "orig_required": false,
+ "title": "Number of Frames",
+ "type": "integer"
+ },
+ "steps": {
+ "default": 40,
+ "description": "Number of denoising steps.",
+ "exclusiveMinimum": 0,
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 40,
+ "orig_required": false,
+ "title": "Steps",
+ "type": "integer"
+ },
+ "seed": {
+ "default": 0,
+ "description": "Randomness seed for reproducibility.",
+ "field_kind": "input",
+ "input": "any",
+ "orig_default": 0,
+ "orig_required": false,
+ "title": "Seed",
+ "type": "integer"
+ },
+ "type": {
+ "const": "wan_video_denoise",
+ "default": "wan_video_denoise",
+ "field_kind": "node_attribute",
+ "title": "type",
+ "type": "string"
}
},
+ "required": ["type", "id"],
+ "tags": ["video", "wan"],
+ "title": "Denoise Video - Wan 2.2",
"type": "object",
- "required": ["virtual_board_id", "board_name", "date", "image_count", "asset_count"],
- "title": "VirtualSubBoardDTO",
- "description": "A virtual sub-board computed from image metadata, not stored in the database."
+ "version": "1.0.0",
+ "output": {
+ "$ref": "#/components/schemas/LatentsOutput"
+ }
},
"Workflow": {
"properties": {
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 050af54fcf1..b2f4c49c59e 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -13,7 +13,8 @@
"toggleRightPanel": "Toggle Right Panel (G)",
"toggleLeftPanel": "Toggle Left Panel (T)",
"uploadImage": "Upload Image",
- "uploadImages": "Upload Images"
+ "uploadImages": "Upload Images",
+ "uploadMedia": "Upload Media"
},
"auth": {
"login": {
@@ -119,6 +120,7 @@
"archived": "Archived",
"autoAddBoard": "Auto-Add Board",
"boards": "Boards",
+ "byDate": "By Date",
"selectedForAutoAdd": "Selected for Auto-Add",
"bottomMessage": "Deleting images will reset any features currently using them.",
"cancel": "Cancel",
@@ -129,12 +131,17 @@
"restartRequired": "Restart required",
"resumeRefused": "Resume refused by server. Restart required.",
"changeBoard": "Change Board",
+ "changeBoardImage_one": "Move Image to Board",
+ "changeBoardImage_other": "Move {{count}} Images to Board",
+ "changeBoardVideo_one": "Move Video to Board",
+ "changeBoardVideo_other": "Move {{count}} Videos to Board",
"clearSearch": "Clear Search",
"deleteBoard": "Delete Board",
"deleteBoardAndImages": "Delete Board and Images",
+ "deleteBoardAndAssets": "Delete Board, Images & Videos",
"deleteBoardOnly": "Delete Board Only",
- "deletedBoardsCannotbeRestored": "Deleted boards and images cannot be restored. Selecting 'Delete Board Only' will move images to an uncategorized state.",
- "deletedPrivateBoardsCannotbeRestored": "Deleted boards and images cannot be restored. Selecting 'Delete Board Only' will move images to a private uncategorized state for the image's creator.",
+ "deletedBoardsCannotbeRestored": "Deleted boards, images, and videos cannot be restored. Selecting 'Delete Board Only' will move images and videos to an uncategorized state.",
+ "deletedPrivateBoardsCannotbeRestored": "Deleted boards, images, and videos cannot be restored. Selecting 'Delete Board Only' will move images and videos to a private uncategorized state for the original creator.",
"uncategorizedImages": "Uncategorized Images",
"deleteAllUncategorizedImages": "Delete All Uncategorized Images",
"deletedImagesCannotBeRestored": "Deleted images cannot be restored.",
@@ -161,6 +168,8 @@
"imagesWithCount_other": "{{count}} images",
"assetsWithCount_one": "{{count}} asset",
"assetsWithCount_other": "{{count}} assets",
+ "videosWithCount_one": "{{count}} video",
+ "videosWithCount_other": "{{count}} videos",
"updateBoardError": "Error updating board",
"setBoardVisibility": "Set Board Visibility",
"setVisibilityPrivate": "Set Private",
@@ -212,7 +221,9 @@
"copy": "Copy",
"copyError": "$t(gallery.copy) Error",
"clipboard": "Clipboard",
+ "collapse": "Collapse",
"collapseAll": "Collapse All",
+ "expand": "Expand",
"crop": "Crop",
"on": "On",
"off": "Off",
@@ -532,6 +543,12 @@
"deleteImage_one": "Delete Image",
"deleteImage_other": "Delete {{count}} Images",
"deleteImagePermanent": "Deleted images cannot be restored.",
+ "deleteVideo_one": "Delete Video",
+ "deleteVideo_other": "Delete {{count}} Videos",
+ "deleteVideoPermanent": "Deleted videos cannot be restored.",
+ "playVideo": "Play video",
+ "closeVideoPlayer": "Close video player",
+ "copyVideoFrame": "Copy Frame",
"displayBoardSearch": "Board Search",
"displaySearch": "Image Search",
"download": "Download",
@@ -557,7 +574,19 @@
"noImageSelected": "No Image Selected",
"noImagesInGallery": "No Images to Display",
"starImage": "Star",
+ "starImage_one": "Star Image",
+ "starImage_other": "Star {{count}} Images",
"unstarImage": "Unstar",
+ "unstarImage_one": "Unstar Image",
+ "unstarImage_other": "Unstar {{count}} Images",
+ "starVideo_one": "Star Video",
+ "starVideo_other": "Star {{count}} Videos",
+ "unstarVideo_one": "Unstar Video",
+ "unstarVideo_other": "Unstar {{count}} Videos",
+ "downloadImage_one": "Download Image",
+ "downloadImage_other": "Download {{count}} Images",
+ "downloadVideo_one": "Download Video",
+ "downloadVideo_other": "Download {{count}} Videos",
"unableToLoad": "Unable to load Gallery",
"deleteSelection": "Delete Selection",
"downloadSelection": "Download Selection",
@@ -1387,6 +1416,14 @@
"qwenImageQuantizationNone": "None (bf16)",
"qwenImageQuantizationInt8": "8-bit (int8)",
"qwenImageQuantizationNf4": "4-bit (nf4)",
+ "wanT5Encoder": "Wan2.2 T5 Encoder",
+ "wanT5EncoderPlaceholder": "From VAE/Encoder Source",
+ "wanVae": "VAE",
+ "wanVaePlaceholder": "From VAE/Encoder Source",
+ "wanComponentSource": "VAE/Encoder Source (Diffusers)",
+ "wanComponentSourcePlaceholder": "GGUF Wan models require a Diffusers Wan source for VAE + UMT5-XXL",
+ "wanTransformerLowNoise": "Transformer (Low Noise)",
+ "wanTransformerLowNoisePlaceholder": "Add for full detail",
"upcastAttention": "Upcast Attention",
"uploadImage": "Upload Image",
"urlOrLocalPath": "URL or Local Path",
@@ -1426,6 +1463,10 @@
"noCompatibleLoRAs": "No Compatible LoRAs"
},
"nodes": {
+ "extractVideoRange": {
+ "dropVideoPrompt": "Connect a video to preview the selected frame.",
+ "missingFps": "Cannot preview frames: input video has no fps metadata."
+ },
"arithmeticSequence": "Arithmetic Sequence",
"linearDistribution": "Linear Distribution",
"uniformRandomDistribution": "Uniform Random Distribution",
@@ -1694,6 +1735,7 @@
"noFlux2KleinVaeModelSelected": "No VAE selected. Non-diffusers FLUX.2 Klein models require a standalone VAE",
"noFlux2KleinQwen3EncoderModelSelected": "No Qwen3 Encoder selected. Non-diffusers FLUX.2 Klein models require a standalone Qwen3 Encoder",
"noQwenImageComponentSourceSelected": "GGUF Qwen Image models require a Diffusers Component Source for VAE/encoder",
+ "noWanComponentSourceSelected": "GGUF Wan 2.2 models require a Diffusers Component Source for VAE/encoder",
"noZImageVaeSourceSelected": "No VAE source: Select VAE (FLUX) or Qwen3 Source model",
"noZImageQwen3EncoderSourceSelected": "No Qwen3 Encoder source: Select Qwen3 Encoder or Qwen3 Source model",
"noAnimaVaeModelSelected": "No Anima VAE model selected",
@@ -1748,6 +1790,7 @@
"showOptionsPanel": "Show Side Panel (O or T)",
"shift": "Shift",
"shuffle": "Shuffle Seed",
+ "wanGuidanceScaleLowNoise": "CFG (Low)",
"steps": "Steps",
"strength": "Strength",
"symmetry": "Symmetry",
@@ -1936,6 +1979,8 @@
"imageSavingFailed": "Image Saving Failed",
"imageUploaded": "Image Uploaded",
"imageUploadFailed": "Image Upload Failed",
+ "videoUploaded": "Video Uploaded",
+ "videoUploadFailed": "Video Upload Failed",
"importFailed": "Import Failed",
"importSuccessful": "Import Successful",
"invalidUpload": "Invalid Upload",
@@ -1987,10 +2032,11 @@
"setNodeField": "Set as node field",
"somethingWentWrong": "Something Went Wrong",
"uploadFailed": "Upload failed",
- "imagesWillBeAddedTo": "Uploaded images will be added to board {{boardName}}'s assets.",
+ "itemsWillBeAddedTo": "Uploaded items will be added to board {{boardName}}'s assets.",
"uploadFailedInvalidUploadDesc_withCount_one": "Must be maximum of 1 PNG, JPEG or WEBP image.",
"uploadFailedInvalidUploadDesc_withCount_other": "Must be maximum of {{count}} PNG, JPEG or WEBP images.",
"uploadFailedInvalidUploadDesc": "Must be PNG, JPEG or WEBP images.",
+ "uploadFailedInvalidMediaUploadDesc": "Must be PNG, JPEG or WEBP images, or MP4 videos.",
"workflowLoaded": "Workflow Loaded",
"problemRetrievingWorkflow": "Problem Retrieving Workflow",
"workflowDeleted": "Workflow Deleted",
diff --git a/invokeai/frontend/web/src/app/components/GlobalImageHotkeys.tsx b/invokeai/frontend/web/src/app/components/GlobalImageHotkeys.tsx
index dd1595bdd74..f1bcf0c1088 100644
--- a/invokeai/frontend/web/src/app/components/GlobalImageHotkeys.tsx
+++ b/invokeai/frontend/web/src/app/components/GlobalImageHotkeys.tsx
@@ -8,6 +8,7 @@ import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
import { useRecallRemix } from 'features/gallery/hooks/useRecallRemix';
import { useRecallSeed } from 'features/gallery/hooks/useRecallSeed';
import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
+import { isVideoName } from 'features/gallery/store/types';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useImageDTO } from 'services/api/endpoints/images';
@@ -16,7 +17,11 @@ import type { ImageDTO } from 'services/api/types';
export const GlobalImageHotkeys = memo(() => {
useAssertSingleton('GlobalImageHotkeys');
const lastSelectedItem = useAppSelector(selectLastSelectedItem);
- const imageDTO = useImageDTO(lastSelectedItem ?? null);
+ // Recall-hotkeys are image-only; passing a video name through to useImageDTO fires a 404
+ // against /api/v1/images/i/.mp4 and emits a noisy "Image record not found" backend
+ // log on every video selection.
+ const imageName = lastSelectedItem && !isVideoName(lastSelectedItem) ? lastSelectedItem : null;
+ const imageDTO = useImageDTO(imageName);
if (!imageDTO) {
return null;
diff --git a/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx b/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx
index e5ec5ccc565..a3284e06166 100644
--- a/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx
+++ b/invokeai/frontend/web/src/app/components/GlobalModalIsolator.tsx
@@ -7,10 +7,12 @@ import { SaveCanvasProjectDialog } from 'features/controlLayers/components/SaveC
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { CropImageModal } from 'features/cropper/components/CropImageModal';
import { DeleteImageModal } from 'features/deleteImageModal/components/DeleteImageModal';
+import { DeleteVideoModal } from 'features/deleteVideoModal/components/DeleteVideoModal';
import { FullscreenDropzone } from 'features/dnd/FullscreenDropzone';
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
import { ImageContextMenu } from 'features/gallery/components/ContextMenu/ImageContextMenu';
+import { VideoContextMenu } from 'features/gallery/components/ContextMenu/VideoContextMenu';
import { WorkflowLibraryModal } from 'features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal';
import { CancelAllExceptCurrentQueueItemConfirmationAlertDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
import { ClearQueueConfirmationsAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
@@ -34,6 +36,7 @@ export const GlobalModalIsolator = memo(() => {
return (
<>
+
@@ -49,6 +52,7 @@ export const GlobalModalIsolator = memo(() => {
+
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts
index b1d60edc2dc..a5503d3286b 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts
@@ -5,7 +5,7 @@ import { setInfillMethod } from 'features/controlLayers/store/paramsSlice';
import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import { appInfoApi } from 'services/api/endpoints/appInfo';
-import { imagesApi } from 'services/api/endpoints/images';
+import { galleryApi } from 'services/api/endpoints/gallery';
export const appStarted = createAction('app/appStarted');
@@ -29,22 +29,27 @@ export const addAppStartedListener = (startAppListening: AppStartListening) => {
})
.catch(noop);
- // ensure an image is selected when we load the first board.
+ // Ensure a gallery item is selected when we load the first board. The grid is fed by the
+ // polymorphic `getGalleryItemNames` endpoint (image + video names interleaved by date),
+ // so that's what we wait on — the older `getImageNames` is no longer dispatched and would
+ // time out forever.
+ //
// The effect must be async and await take() so that RTK keeps the listener's AbortController
// alive until the query resolves; a synchronous effect causes the controller to be aborted
// immediately after the effect returns, before any network response arrives.
- const firstImageLoad = await take(imagesApi.endpoints.getImageNames.matchFulfilled, 5000);
- if (firstImageLoad === null) {
+ const firstLoad = await take(galleryApi.endpoints.getGalleryItemNames.matchFulfilled, 5000);
+ if (firstLoad === null) {
// timeout or cancelled
return;
}
- const [{ payload }] = firstImageLoad;
- const selectedImage = selectLastSelectedItem(getState());
- if (selectedImage) {
+ const [{ payload }] = firstLoad;
+ const selectedItem = selectLastSelectedItem(getState());
+ if (selectedItem) {
return;
}
- if (payload.image_names[0]) {
- dispatch(imageSelected(payload.image_names[0]));
+ const firstItem = payload.items[0];
+ if (firstItem) {
+ dispatch(imageSelected(firstItem.name));
}
},
});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardIdSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardIdSelected.ts
index 9fd777fb29b..e37da159527 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardIdSelected.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardIdSelected.ts
@@ -2,13 +2,13 @@ import { isAnyOf } from '@reduxjs/toolkit';
import type { AppStartListening } from 'app/store/store';
import { selectGetImageNamesQueryArgs, selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gallery/store/gallerySlice';
-import { imagesApi } from 'services/api/endpoints/images';
+import { galleryApi } from 'services/api/endpoints/gallery';
export const addBoardIdSelectedListener = (startAppListening: AppStartListening) => {
startAppListening({
matcher: isAnyOf(boardIdSelected, galleryViewChanged),
effect: async (action, { getState, dispatch, condition, cancelActiveListeners }) => {
- // Cancel any in-progress instances of this listener, we don't want to select an image from a previous board
+ // Cancel any in-progress instances of this listener, we don't want to select an item from a previous board
cancelActiveListeners();
if (boardIdSelected.match(action) && action.payload.select) {
@@ -20,11 +20,14 @@ export const addBoardIdSelectedListener = (startAppListening: AppStartListening)
const board_id = selectSelectedBoardId(state);
+ // The grid is now backed by the polymorphic getGalleryItemNames endpoint (the legacy
+ // getImageNames query is no longer dispatched), so the auto-select probe must read its
+ // cache or it will time out and clear the user's selection on every board switch.
const queryArgs = { ...selectGetImageNamesQueryArgs(state), board_id };
- // wait until the board has some images - maybe it already has some from a previous fetch
+ // wait until the board has some items - maybe it already has some from a previous fetch
// must use getState() to ensure we do not have stale state
const isSuccess = await condition(
- () => imagesApi.endpoints.getImageNames.select(queryArgs)(getState()).isSuccess,
+ () => galleryApi.endpoints.getGalleryItemNames.select(queryArgs)(getState()).isSuccess,
5000
);
@@ -33,12 +36,12 @@ export const addBoardIdSelectedListener = (startAppListening: AppStartListening)
return;
}
- // the board was just changed - we can select the first image
- const imageNames = imagesApi.endpoints.getImageNames.select(queryArgs)(getState()).data?.image_names;
+ // the board was just changed - we can select the first gallery item (image or video)
+ const items = galleryApi.endpoints.getGalleryItemNames.select(queryArgs)(getState()).data?.items;
- const imageToSelect = imageNames && imageNames.length > 0 ? imageNames[0] : null;
+ const itemToSelect = items && items.length > 0 ? (items[0]?.name ?? null) : null;
- dispatch(imageSelected(imageToSelect ?? null));
+ dispatch(imageSelected(itemToSelect));
},
});
};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
index 9e67e013946..8dacb040a15 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
@@ -17,6 +17,9 @@ import {
setZImageScheduler,
syncedToOptimalDimension,
vaeSelected,
+ wanComponentSourceSelected,
+ wanT5EncoderModelSelected,
+ wanVaeModelSelected,
zImageQwen3EncoderModelSelected,
zImageQwen3SourceModelSelected,
zImageVaeModelSelected,
@@ -36,6 +39,7 @@ import {
isAspectRatioID,
isFlux2ReferenceImageConfig,
isQwenImageReferenceImageConfig,
+ isWanReferenceImageConfig,
} from 'features/controlLayers/store/types';
import {
initialFlux2ReferenceImage,
@@ -43,6 +47,7 @@ import {
initialFLUXRedux,
initialIPAdapter,
initialQwenImageReferenceImage,
+ initialWanReferenceImage,
} from 'features/controlLayers/store/util';
import { SUPPORTS_REF_IMAGES_BASE_MODELS } from 'features/modelManagerV2/models';
import { zModelIdentifierField } from 'features/nodes/types/common';
@@ -61,6 +66,9 @@ import {
selectQwenImageVAEModels,
selectQwenVLEncoderModels,
selectRegionalRefImageModels,
+ selectWanDiffusersModels,
+ selectWanT5EncoderModels,
+ selectWanVAEModels,
selectZImageDiffusersModels,
} from 'services/api/hooks/modelsByType';
import type { FLUXKontextModelConfig, FLUXReduxModelConfig, IPAdapterModelConfig } from 'services/api/types';
@@ -302,6 +310,29 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
}
}
+ // handle Wan 2.2 component source / standalone VAE / standalone T5 encoder -
+ // clear when switching away. (Auto-default happens unconditionally outside
+ // this block so it fires when switching between Wan variants too.)
+ const {
+ wanComponentSource: wanComponentSourceOnLeave,
+ wanVaeModel: wanVaeModelOnLeave,
+ wanT5EncoderModel: wanT5EncoderModelOnLeave,
+ } = state.params;
+ if (newBase !== 'wan') {
+ if (wanComponentSourceOnLeave) {
+ dispatch(wanComponentSourceSelected(null));
+ modelsUpdatedDisabledOrCleared += 1;
+ }
+ if (wanVaeModelOnLeave) {
+ dispatch(wanVaeModelSelected(null));
+ modelsUpdatedDisabledOrCleared += 1;
+ }
+ if (wanT5EncoderModelOnLeave) {
+ dispatch(wanT5EncoderModelSelected(null));
+ modelsUpdatedDisabledOrCleared += 1;
+ }
+ }
+
if (newModel.base !== 'external' && SUPPORTS_REF_IMAGES_BASE_MODELS.includes(newModel.base)) {
// Handle incompatible reference image models - switch to first compatible model, with some smart logic
// to choose the best available model based on the new main model.
@@ -358,6 +389,22 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
continue;
}
+ if (newBase === 'wan') {
+ // Switching TO Wan - convert any non-wan configs to wan_reference_image.
+ // The Wan I2V graph builder consumes the first enabled ref image; T2V /
+ // TI2V variants ignore ref images entirely (matches Qwen-generate behavior).
+ if (!isWanReferenceImageConfig(entity.config)) {
+ dispatch(
+ refImageConfigChanged({
+ id: entity.id,
+ config: { ...initialWanReferenceImage },
+ })
+ );
+ modelsUpdatedDisabledOrCleared += 1;
+ }
+ continue;
+ }
+
if (isFlux2ReferenceImageConfig(entity.config)) {
// Switching AWAY from FLUX.2 - convert flux2_reference_image to the appropriate config type
let newConfig;
@@ -406,6 +453,29 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
continue;
}
+ if (isWanReferenceImageConfig(entity.config)) {
+ // Switching AWAY from Wan - convert to the appropriate config type for the new base.
+ let newConfig;
+ if (newGlobalRefImageModel) {
+ const parsedModel = zModelIdentifierField.parse(newGlobalRefImageModel);
+ if (newModel.base === 'flux' && newModel.name.toLowerCase().includes('kontext')) {
+ newConfig = { ...initialFluxKontextReferenceImage, model: parsedModel };
+ } else if (newGlobalRefImageModel.type === 'flux_redux') {
+ newConfig = { ...initialFLUXRedux, model: parsedModel };
+ } else {
+ newConfig = { ...initialIPAdapter, model: parsedModel };
+ if (parsedModel.base === 'flux') {
+ newConfig.clipVisionModel = 'ViT-L';
+ }
+ }
+ } else {
+ newConfig = { ...initialIPAdapter };
+ }
+ dispatch(refImageConfigChanged({ id: entity.id, config: newConfig }));
+ modelsUpdatedDisabledOrCleared += 1;
+ continue;
+ }
+
// Standard handling for non-flux2 configs
const shouldUpdateModel =
(entity.config.model && entity.config.model.base !== newBase) ||
@@ -461,6 +531,54 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
}
}
+ // Wan 2.2: auto-default Component Source / standalone VAE / standalone T5 encoder
+ // when the new model is Wan. Runs on every Wan selection (including same-base
+ // switches like Diffusers Wan → GGUF Wan) so the user doesn't have to dig into
+ // Advanced when picking a GGUF main. Only sets fields that are currently empty
+ // and only does it for GGUF mains — Diffusers mains carry everything themselves.
+ if (newBase === 'wan') {
+ const modelConfigsResult = selectModelConfigsQuery(state);
+ const newModelConfig = modelConfigsResult.data
+ ? modelConfigsAdapterSelectors.selectById(modelConfigsResult.data, newModel.key)
+ : null;
+ const isNewModelGGUF = newModelConfig?.type === 'main' && newModelConfig.format === 'gguf_quantized';
+ if (isNewModelGGUF) {
+ const { wanComponentSource, wanVaeModel, wanT5EncoderModel } = state.params;
+ // Match component source by variant family — A14B (t2v_a14b/i2v_a14b) and
+ // TI2V-5B use different VAEs (16-ch vs 48-ch); a mismatched component source
+ // would silently load the wrong VAE and produce broken images. The standalone
+ // VAE / encoder configs don't carry variant info, so those still go first-match.
+ const newVariant =
+ newModelConfig && 'variant' in newModelConfig && typeof newModelConfig.variant === 'string'
+ ? newModelConfig.variant
+ : null;
+ const a14bFamily = newVariant === 't2v_a14b' || newVariant === 'i2v_a14b';
+ if (!wanComponentSource) {
+ const availableWanDiffusers = selectWanDiffusersModels(state);
+ const matchingFamily = availableWanDiffusers.find((m) => {
+ const v = 'variant' in m && typeof m.variant === 'string' ? m.variant : null;
+ return a14bFamily ? v === 't2v_a14b' || v === 'i2v_a14b' : v === newVariant;
+ });
+ const diffusersModel = matchingFamily ?? availableWanDiffusers[0];
+ if (diffusersModel) {
+ dispatch(wanComponentSourceSelected(zModelIdentifierField.parse(diffusersModel)));
+ }
+ }
+ if (!wanVaeModel) {
+ const vae = selectWanVAEModels(state)[0];
+ if (vae) {
+ dispatch(wanVaeModelSelected(zModelIdentifierField.parse(vae)));
+ }
+ }
+ if (!wanT5EncoderModel) {
+ const encoder = selectWanT5EncoderModels(state)[0];
+ if (encoder) {
+ dispatch(wanT5EncoderModelSelected(zModelIdentifierField.parse(encoder)));
+ }
+ }
+ }
+ }
+
// Handle FLUX.2 Klein model changes within the same base (different variants need different encoders)
// Clear the Qwen3 encoder only when switching between different Klein variants
// (e.g., klein_4b needs qwen3_4b, klein_9b needs qwen3_8b)
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploadFailedDescription.test.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploadFailedDescription.test.ts
new file mode 100644
index 00000000000..589ea5e3384
--- /dev/null
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploadFailedDescription.test.ts
@@ -0,0 +1,32 @@
+/**
+ * Regression test for silent video upload failures (PR #9163 review).
+ *
+ * The bug: uploadVideos() aggregated with Promise.allSettled and dropped rejections, and
+ * no uploadVideo.matchRejected listener existed — so a rejected MP4 vanished with no
+ * error feedback, and a partially successful batch gave no indication of which files
+ * failed. The rejected listener now toasts per failed mutation; these tests pin that the
+ * failed file's name reaches the user-visible description.
+ */
+import { describe, expect, it } from 'vitest';
+
+import { getVideoUploadFailedDescription } from './videoUploadFailedDescription';
+
+describe('getVideoUploadFailedDescription', () => {
+ it('names the failed file so mixed-outcome batches are attributable', () => {
+ expect(getVideoUploadFailedDescription('clip.mp4', 'Request failed with status code 500')).toBe(
+ 'clip.mp4: Request failed with status code 500'
+ );
+ });
+
+ it('still names the file when no error message is available', () => {
+ expect(getVideoUploadFailedDescription('clip.mp4', undefined)).toBe('clip.mp4');
+ });
+
+ it('falls back to the error message alone for pasted blobs without a name', () => {
+ expect(getVideoUploadFailedDescription(undefined, 'Network Error')).toBe('Network Error');
+ });
+
+ it('returns undefined when there is nothing to show (toast falls back to its title)', () => {
+ expect(getVideoUploadFailedDescription(undefined, undefined)).toBeUndefined();
+ });
+});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploadFailedDescription.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploadFailedDescription.ts
new file mode 100644
index 00000000000..d1e1c0cea84
--- /dev/null
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploadFailedDescription.ts
@@ -0,0 +1,18 @@
+/**
+ * Builds the description for the video-upload-failed toast (PR #9163 review).
+ *
+ * Batch video uploads are aggregated with Promise.allSettled, so a partially failed
+ * batch resolves "successfully" at the call site — the per-mutation rejected listener is
+ * where each failure surfaces, and it must name the file so the user can tell which of
+ * their videos disappeared from a mixed-outcome batch.
+ *
+ * Kept in its own module (rather than inline in the listener) so it can be unit tested:
+ * the listener itself needs a live store and is exercised only at runtime.
+ */
+export const getVideoUploadFailedDescription = (
+ fileName: string | undefined,
+ errorMessage: string | undefined
+): string | undefined => {
+ const parts = [fileName, errorMessage].filter(Boolean);
+ return parts.length > 0 ? parts.join(': ') : undefined;
+};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploaded.ts
new file mode 100644
index 00000000000..30ed978e55a
--- /dev/null
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/videoUploaded.ts
@@ -0,0 +1,99 @@
+import { logger } from 'app/logging/logger';
+import type { AppStartListening, RootState } from 'app/store/store';
+import { omit } from 'es-toolkit/compat';
+import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
+import { boardIdSelected, galleryViewChanged } from 'features/gallery/store/gallerySlice';
+import { toast } from 'features/toast/toast';
+import { t } from 'i18next';
+import { boardsApi } from 'services/api/endpoints/boards';
+import { videosApi } from 'services/api/endpoints/videos';
+import type { VideoDTO } from 'services/api/types';
+
+import { getVideoUploadFailedDescription } from './videoUploadFailedDescription';
+
+const log = logger('gallery');
+
+/**
+ * Mirrors getUploadedToastDescription in imageUploaded.ts: names the board the video
+ * landed on so the user knows where to find it.
+ */
+const getUploadedToastDescription = (boardId: string, state: RootState) => {
+ if (boardId === 'none') {
+ return t('toast.addedToUncategorized');
+ }
+ const queryArgs = selectListBoardsQueryArgs(state);
+ const { data } = boardsApi.endpoints.listAllBoards.select(queryArgs)(state);
+ const board = data?.find((b) => b.board_id === boardId);
+
+ return t('toast.addedToBoard', { name: board?.board_name ?? boardId });
+};
+
+let lastUploadedToastTimeout: number | null = null;
+
+/**
+ * Success and failure feedback for video uploads, mirroring the image upload listeners.
+ * Because the batch helpers (uploadVideos) aggregate with Promise.allSettled, this
+ * per-mutation listener is the layer where an individual rejected MP4 becomes visible
+ * to the user — including which file failed.
+ */
+export const addVideoUploadedListeners = (startAppListening: AppStartListening) => {
+ startAppListening({
+ matcher: videosApi.endpoints.uploadVideo.matchFulfilled,
+ effect: (action, { dispatch, getState }) => {
+ const videoDTO: VideoDTO = action.payload;
+ const silent = action.meta.arg.originalArgs.silent;
+ const isFirstUploadOfBatch = action.meta.arg.originalArgs.isFirstUploadOfBatch ?? true;
+
+ if (silent || videoDTO.is_intermediate) {
+ // If the video is silent or intermediate, we don't want to show a toast
+ return;
+ }
+
+ const state = getState();
+
+ log.debug({ videoDTO }, 'Video uploaded');
+
+ const boardId = videoDTO.board_id ?? 'none';
+
+ if (lastUploadedToastTimeout !== null) {
+ window.clearTimeout(lastUploadedToastTimeout);
+ }
+ const toastApi = toast({
+ id: 'VIDEO_UPLOADED',
+ title: t('toast.videoUploaded'),
+ description: getUploadedToastDescription(boardId, state),
+ status: 'success',
+ duration: null, // we will close the toast manually
+ });
+ lastUploadedToastTimeout = window.setTimeout(() => {
+ toastApi.close();
+ }, 3000);
+
+ // Only navigate the gallery on the first upload of a batch — see the matching
+ // comment in imageUploaded.ts for the board-hijacking failure mode this avoids.
+ if (isFirstUploadOfBatch) {
+ dispatch(boardIdSelected({ boardId }));
+ dispatch(galleryViewChanged('assets'));
+ }
+ },
+ });
+
+ startAppListening({
+ matcher: videosApi.endpoints.uploadVideo.matchRejected,
+ effect: (action) => {
+ const fileName = action.meta.arg.originalArgs.file.name;
+ const sanitizedData = {
+ arg: {
+ ...omit(action.meta.arg.originalArgs, ['file']),
+ file: ``,
+ },
+ };
+ log.error({ ...sanitizedData }, 'Video upload failed');
+ toast({
+ title: t('toast.videoUploadFailed'),
+ description: getVideoUploadFailedDescription(fileName, action.error.message),
+ status: 'error',
+ });
+ },
+ });
+};
diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts
index f24d2d0105c..626ac899f2f 100644
--- a/invokeai/frontend/web/src/app/store/store.ts
+++ b/invokeai/frontend/web/src/app/store/store.ts
@@ -56,6 +56,7 @@ import { stateSanitizer } from './middleware/devtools/stateSanitizer';
import { addArchivedOrDeletedBoardListener } from './middleware/listenerMiddleware/listeners/addArchivedOrDeletedBoardListener';
import { addPBRFilterListener } from './middleware/listenerMiddleware/listeners/addPBRFilterListener';
import { addImageUploadedFulfilledListener } from './middleware/listenerMiddleware/listeners/imageUploaded';
+import { addVideoUploadedListeners } from './middleware/listenerMiddleware/listeners/videoUploaded';
const listenerMiddleware = createListenerMiddleware();
@@ -257,6 +258,7 @@ export const addAppListener = addListener.withTypes();
// To avoid circular dependencies, all listener middleware listeners are added here in the main store setup file.
const startAppListening = listenerMiddleware.startListening as AppStartListening;
addImageUploadedFulfilledListener(startAppListening);
+addVideoUploadedListeners(startAppListening);
// Image deleted
addDeleteBoardAndImagesFulfilledListener(startAppListening);
diff --git a/invokeai/frontend/web/src/common/hooks/useGalleryItemDTO.tsx b/invokeai/frontend/web/src/common/hooks/useGalleryItemDTO.tsx
new file mode 100644
index 00000000000..05dfdf056bb
--- /dev/null
+++ b/invokeai/frontend/web/src/common/hooks/useGalleryItemDTO.tsx
@@ -0,0 +1,32 @@
+import { isVideoName } from 'features/gallery/store/types';
+import { useImageDTO } from 'services/api/endpoints/images';
+import { useVideoDTO } from 'services/api/endpoints/videos';
+import type { ImageDTO, VideoDTO } from 'services/api/types';
+
+/**
+ * Resolves either an ImageDTO or a VideoDTO based on a polymorphic name. The kind is derived
+ * from the filename extension — the backend names images `.png` and videos `.mp4`,
+ * so we can dispatch without an extra fetch.
+ *
+ * Both underlying RTK Query hooks are always called (React rule-of-hooks), but only the relevant
+ * one is given a real name; the other receives `null` and short-circuits via `skipToken`.
+ */
+/** @knipignore Re-exported for callers that destructure the hook return into named locals. */
+export type GalleryItemDTO = { kind: 'image'; dto: ImageDTO } | { kind: 'video'; dto: VideoDTO };
+
+export const useGalleryItemDTO = (name: string | null | undefined): GalleryItemDTO | null => {
+ const isVideo = name ? isVideoName(name) : false;
+ const imageName = name && !isVideo ? name : null;
+ const videoName = name && isVideo ? name : null;
+
+ const imageDTO = useImageDTO(imageName);
+ const videoDTO = useVideoDTO(videoName);
+
+ if (!name) {
+ return null;
+ }
+ if (isVideo) {
+ return videoDTO ? { kind: 'video', dto: videoDTO } : null;
+ }
+ return imageDTO ? { kind: 'image', dto: imageDTO } : null;
+};
diff --git a/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx b/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx
index fc173de979f..cc119103967 100644
--- a/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx
+++ b/invokeai/frontend/web/src/common/hooks/useImageUploadButton.tsx
@@ -2,42 +2,44 @@ import type { ButtonProps, IconButtonProps, SystemStyleObject } from '@invoke-ai
import { Button, IconButton } from '@invoke-ai/ui-library';
import { logger } from 'app/logging/logger';
import { useAppSelector } from 'app/store/storeHooks';
+import { trackAsyncTask } from 'common/util/trackAsyncTask';
+import { getUploadDropzoneAccept, partitionUploadFiles } from 'common/util/uploadMediaAccept';
import { selectAutoAddBoardId } from 'features/gallery/store/gallerySelectors';
import { toast } from 'features/toast/toast';
-import { memo, useCallback } from 'react';
-import type { Accept, FileRejection } from 'react-dropzone';
+import { memo, useCallback, useRef, useState } from 'react';
+import type { FileRejection } from 'react-dropzone';
import { useDropzone } from 'react-dropzone';
import { useTranslation } from 'react-i18next';
import { PiUploadBold } from 'react-icons/pi';
import { uploadImages, useUploadImageMutation } from 'services/api/endpoints/images';
-import type { ImageDTO } from 'services/api/types';
+import { uploadVideos, useUploadVideoMutation } from 'services/api/endpoints/videos';
+import type { ImageDTO, VideoDTO } from 'services/api/types';
import { assert } from 'tsafe';
import type { SetOptional } from 'type-fest';
-const addUpperCaseReducer = (acc: string[], ext: string) => {
- acc.push(ext);
- acc.push(ext.toUpperCase());
- return acc;
-};
-
-export const dropzoneAccept: Accept = {
- 'image/png': ['.png'].reduce(addUpperCaseReducer, [] as string[]),
- 'image/jpeg': ['.jpg', '.jpeg', '.png'].reduce(addUpperCaseReducer, [] as string[]),
- 'image/webp': ['.webp'].reduce(addUpperCaseReducer, [] as string[]),
-};
-
type UseImageUploadButtonArgs =
| {
isDisabled?: boolean;
allowMultiple: false;
+ /**
+ * Opt-in for video uploads. The backend only stores videos in the gallery, so a
+ * consumer that wants an image (board covers, ref images, etc.) must NOT set this —
+ * otherwise a selected MP4 would be uploaded to the gallery and its DTO silently
+ * discarded while the requested image action goes nowhere.
+ */
+ allowVideos?: boolean;
onUpload?: (imageDTO: ImageDTO) => void;
+ /** Called when a single dropped file is a video (parallel to onUpload for images). */
+ onUploadVideo?: (videoDTO: VideoDTO) => void;
onUploadStarted?: (files: File) => void;
onError?: (error: unknown) => void;
}
| {
isDisabled?: boolean;
allowMultiple: true;
+ allowVideos?: boolean;
onUpload?: (imageDTOs: ImageDTO[]) => void;
+ onUploadVideo?: (videoDTOs: VideoDTO[]) => void;
onUploadStarted?: (files: File[]) => void;
onError?: (error: unknown) => void;
};
@@ -65,17 +67,39 @@ const log = logger('gallery');
*/
export const useImageUploadButton = ({
onUpload,
+ onUploadVideo,
isDisabled,
allowMultiple,
+ allowVideos = false,
onUploadStarted,
onError,
}: UseImageUploadButtonArgs) => {
const autoAddBoardId = useAppSelector(selectAutoAddBoardId);
- const [uploadImage, request] = useUploadImageMutation();
+ const [uploadImage, imageRequest] = useUploadImageMutation();
+ const [uploadVideo, videoRequest] = useUploadVideoMutation();
+ const [isBatchUploading, setIsBatchUploading] = useState(false);
+ const pendingBatchUploads = useRef(0);
const { t } = useTranslation();
+ const onBatchLoadingChanged = useCallback((isLoading: boolean) => {
+ pendingBatchUploads.current += isLoading ? 1 : -1;
+ setIsBatchUploading(pendingBatchUploads.current > 0);
+ }, []);
const onDropAccepted = useCallback(
async (files: File[]) => {
+ // The accept map already excludes videos for image-only consumers, but the file
+ // dialog can bypass it (e.g. "All Files"), so partition again at runtime.
+ const { imageFiles, videoFiles, rejectedFiles } = partitionUploadFiles(files, allowVideos);
+ if (rejectedFiles.length > 0) {
+ log.error({ files: rejectedFiles.map((f) => f.name) }, 'Videos are not accepted by this upload field');
+ toast({
+ id: 'UPLOAD_FAILED',
+ title: t('toast.uploadFailed'),
+ description: t('toast.uploadFailedInvalidUploadDesc'),
+ status: 'error',
+ });
+ return;
+ }
try {
if (!allowMultiple) {
if (files.length > 1) {
@@ -90,44 +114,96 @@ export const useImageUploadButton = ({
const file = files[0];
assert(file !== undefined); // should never happen
onUploadStarted?.(file);
- const imageDTO = await uploadImage({
- file,
- image_category: 'user',
- is_intermediate: false,
- board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
- silent: true,
- }).unwrap();
- if (onUpload) {
- onUpload(imageDTO);
- }
- } else {
- onUploadStarted?.(files);
- let imageDTOs: ImageDTO[] = [];
- imageDTOs = await uploadImages(
- files.map((file, i) => ({
+ if (videoFiles.length > 0) {
+ const videoDTO = await uploadVideo({
+ file,
+ video_category: 'user',
+ is_intermediate: false,
+ board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
+ silent: true,
+ }).unwrap();
+ // Cast: TS narrows onUploadVideo by the allowMultiple discriminator above.
+ (onUploadVideo as ((dto: VideoDTO) => void) | undefined)?.(videoDTO);
+ } else {
+ const imageDTO = await uploadImage({
file,
image_category: 'user',
is_intermediate: false,
board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
- silent: false,
- isFirstUploadOfBatch: i === 0,
- }))
- );
- if (onUpload) {
- onUpload(imageDTOs);
+ silent: true,
+ }).unwrap();
+ (onUpload as ((dto: ImageDTO) => void) | undefined)?.(imageDTO);
}
+ } else {
+ onUploadStarted?.(files);
+ await trackAsyncTask(async () => {
+ let imageDTOs: ImageDTO[] = [];
+ if (imageFiles.length > 0) {
+ imageDTOs = await uploadImages(
+ imageFiles.map((file, i) => ({
+ file,
+ image_category: 'user',
+ is_intermediate: false,
+ board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
+ silent: false,
+ isFirstUploadOfBatch: i === 0,
+ }))
+ );
+ }
+
+ let videoDTOs: VideoDTO[] = [];
+ if (videoFiles.length > 0) {
+ videoDTOs = await uploadVideos(
+ videoFiles.map((file, i) => ({
+ file,
+ video_category: 'user',
+ is_intermediate: false,
+ board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
+ silent: false,
+ isFirstUploadOfBatch: i === 0,
+ }))
+ );
+ }
+
+ if (imageDTOs.length > 0) {
+ (onUpload as ((dtos: ImageDTO[]) => void) | undefined)?.(imageDTOs);
+ }
+ if (videoDTOs.length > 0) {
+ (onUploadVideo as ((dtos: VideoDTO[]) => void) | undefined)?.(videoDTOs);
+ }
+ }, onBatchLoadingChanged);
}
} catch (error) {
onError?.(error);
+ // Name the media that actually failed — a failed MP4 upload should not claim an
+ // image upload failed. Mixed batches get the media-neutral title.
+ const title =
+ videoFiles.length > 0 && imageFiles.length > 0
+ ? t('toast.uploadFailed')
+ : videoFiles.length > 0
+ ? t('toast.videoUploadFailed')
+ : t('toast.imageUploadFailed');
toast({
id: 'UPLOAD_FAILED',
- title: t('toast.imageUploadFailed'),
+ title,
status: 'error',
});
}
},
- [allowMultiple, onUploadStarted, uploadImage, autoAddBoardId, onUpload, onError, t]
+ [
+ allowMultiple,
+ allowVideos,
+ onUploadStarted,
+ uploadImage,
+ uploadVideo,
+ autoAddBoardId,
+ onUpload,
+ onUploadVideo,
+ onError,
+ onBatchLoadingChanged,
+ t,
+ ]
);
const onDropRejected = useCallback(
@@ -158,7 +234,7 @@ export const useImageUploadButton = ({
getInputProps: getUploadInputProps,
open: openUploader,
} = useDropzone({
- accept: dropzoneAccept,
+ accept: getUploadDropzoneAccept(allowVideos),
onDropAccepted,
onDropRejected,
disabled: isDisabled,
@@ -166,7 +242,11 @@ export const useImageUploadButton = ({
multiple: allowMultiple,
});
- return { getUploadButtonProps, getUploadInputProps, openUploader, request };
+ // Uploads run through separate image and video mutations; loading state must cover both
+ // or an in-flight MP4 upload would show idle controls and allow double submission.
+ const isUploading = imageRequest.isLoading || videoRequest.isLoading || isBatchUploading;
+
+ return { getUploadButtonProps, getUploadInputProps, openUploader, isUploading };
};
const sx = {
@@ -197,7 +277,7 @@ export const UploadImageIconButton = memo(
sx={sx}
data-error={isError}
icon={ }
- isLoading={uploadApi.request.isLoading}
+ isLoading={uploadApi.isUploading}
{...rest}
{...uploadApi.getUploadButtonProps()}
/>
@@ -225,7 +305,7 @@ const UploadImageButton = memo((props: UploadImageButtonProps) => {
sx={sx}
data-error={isError}
rightIcon={ }
- isLoading={uploadApi.request.isLoading}
+ isLoading={uploadApi.isUploading}
{...rest}
{...uploadApi.getUploadButtonProps()}
>
@@ -256,7 +336,7 @@ export const UploadMultipleImageButton = ({
sx={sx}
data-error={isError}
icon={ }
- isLoading={uploadApi.request.isLoading}
+ isLoading={uploadApi.isUploading}
{...rest}
{...uploadApi.getUploadButtonProps()}
/>
diff --git a/invokeai/frontend/web/src/common/util/trackAsyncTask.test.ts b/invokeai/frontend/web/src/common/util/trackAsyncTask.test.ts
new file mode 100644
index 00000000000..2b09bf4e005
--- /dev/null
+++ b/invokeai/frontend/web/src/common/util/trackAsyncTask.test.ts
@@ -0,0 +1,27 @@
+import { describe, expect, it, vi } from 'vitest';
+
+import { trackAsyncTask } from './trackAsyncTask';
+
+describe('trackAsyncTask', () => {
+ it('reports loading until the tracked task settles', async () => {
+ let resolveTask: (() => void) | undefined;
+ const task = new Promise((resolve) => {
+ resolveTask = resolve;
+ });
+ const onLoadingChanged = vi.fn();
+
+ const tracked = trackAsyncTask(() => task, onLoadingChanged);
+ expect(onLoadingChanged).toHaveBeenCalledWith(true);
+ expect(onLoadingChanged).not.toHaveBeenCalledWith(false);
+
+ resolveTask?.();
+ await tracked;
+ expect(onLoadingChanged).toHaveBeenLastCalledWith(false);
+ });
+
+ it('clears loading when the task rejects', async () => {
+ const onLoadingChanged = vi.fn();
+ await expect(trackAsyncTask(() => Promise.reject(new Error('failed')), onLoadingChanged)).rejects.toThrow('failed');
+ expect(onLoadingChanged.mock.calls).toEqual([[true], [false]]);
+ });
+});
diff --git a/invokeai/frontend/web/src/common/util/trackAsyncTask.ts b/invokeai/frontend/web/src/common/util/trackAsyncTask.ts
new file mode 100644
index 00000000000..090f3375142
--- /dev/null
+++ b/invokeai/frontend/web/src/common/util/trackAsyncTask.ts
@@ -0,0 +1,8 @@
+export const trackAsyncTask = async (task: () => Promise, onLoadingChanged: (isLoading: boolean) => void) => {
+ onLoadingChanged(true);
+ try {
+ return await task();
+ } finally {
+ onLoadingChanged(false);
+ }
+};
diff --git a/invokeai/frontend/web/src/common/util/uploadMediaAccept.test.ts b/invokeai/frontend/web/src/common/util/uploadMediaAccept.test.ts
new file mode 100644
index 00000000000..de07dc833aa
--- /dev/null
+++ b/invokeai/frontend/web/src/common/util/uploadMediaAccept.test.ts
@@ -0,0 +1,102 @@
+/**
+ * Regression tests for client-side upload acceptance (PR #9163 review).
+ *
+ * The bug: the dropzone accept map advertised `.webm`/`.mov` and `isVideoFile` treated
+ * `.webm`/`.mov`/`.mkv` as uploadable videos, but the video upload router accepts MP4 only
+ * (`invokeai/app/api/routers/videos.py`: `ACCEPTED_VIDEO_MIME_PREFIXES = ("video/mp4",)`,
+ * `ACCEPTED_VIDEO_EXTENSIONS = (".mp4",)`). Dropping a WebM or QuickTime file started an
+ * upload the server was guaranteed to reject with 415.
+ *
+ * These tests pin the client lists to the backend contract: if a new container is ever
+ * accepted server-side, update `uploadMediaAccept.ts` and these expectations together.
+ */
+import { describe, expect, it } from 'vitest';
+
+import {
+ ACCEPTED_VIDEO_EXTENSIONS,
+ ACCEPTED_VIDEO_TYPES,
+ getUploadDropzoneAccept,
+ imageAndVideoDropzoneAccept,
+ imageDropzoneAccept,
+ isAcceptedUploadFile,
+ isImageFile,
+ isVideoFile,
+ partitionUploadFiles,
+} from './uploadMediaAccept';
+
+// `isVideoFile` only reads `type` and `name`, so a plain object stands in for a DOM File.
+const fakeFile = (name: string, type: string): File => ({ name, type }) as File;
+
+describe('video acceptance matches the backend (MP4 only)', () => {
+ it('accepts only MIME types and extensions the video upload router supports', () => {
+ expect(ACCEPTED_VIDEO_TYPES).toEqual(['video/mp4']);
+ expect(ACCEPTED_VIDEO_EXTENSIONS).toEqual(['.mp4']);
+ });
+
+ it('advertises only backend-supported video entries in the gallery dropzone accept map', () => {
+ const videoEntries = Object.entries(imageAndVideoDropzoneAccept).filter(([mime]) => mime.startsWith('video/'));
+ expect(videoEntries).toEqual([['video/mp4', ['.mp4', '.MP4']]]);
+ });
+
+ it('does not advertise any video entries for image-only upload fields', () => {
+ expect(Object.keys(imageDropzoneAccept).some((mime) => mime.startsWith('video/'))).toBe(false);
+ });
+});
+
+describe('isVideoFile', () => {
+ it('recognizes MP4 by MIME type and by extension', () => {
+ expect(isVideoFile(fakeFile('clip.mp4', 'video/mp4'))).toBe(true);
+ expect(isVideoFile(fakeFile('CLIP.MP4', ''))).toBe(true);
+ });
+
+ it('rejects video containers the backend does not accept', () => {
+ expect(isVideoFile(fakeFile('clip.webm', 'video/webm'))).toBe(false);
+ expect(isVideoFile(fakeFile('clip.mov', 'video/quicktime'))).toBe(false);
+ expect(isVideoFile(fakeFile('clip.mkv', 'video/x-matroska'))).toBe(false);
+ });
+
+ it('rejects images', () => {
+ expect(isVideoFile(fakeFile('image.png', 'image/png'))).toBe(false);
+ });
+});
+
+describe('isImageFile / isAcceptedUploadFile', () => {
+ it('accepts a file when either the MIME type or the extension is recognized', () => {
+ // Browsers sometimes supply an empty File.type — the extension must suffice on its own.
+ expect(isAcceptedUploadFile(fakeFile('clip.mp4', ''))).toBe(true);
+ expect(isAcceptedUploadFile(fakeFile('image.png', ''))).toBe(true);
+ // And a recognized MIME type must suffice without a matching extension.
+ expect(isAcceptedUploadFile(fakeFile('pasted-blob', 'image/png'))).toBe(true);
+ expect(isAcceptedUploadFile(fakeFile('pasted-blob', 'video/mp4'))).toBe(true);
+ });
+
+ it('rejects unsupported media', () => {
+ expect(isImageFile(fakeFile('vector.svg', 'image/svg+xml'))).toBe(false);
+ expect(isAcceptedUploadFile(fakeFile('clip.webm', 'video/webm'))).toBe(false);
+ expect(isAcceptedUploadFile(fakeFile('notes.txt', 'text/plain'))).toBe(false);
+ });
+});
+
+describe('video uploads are opt-in per consumer', () => {
+ const mp4 = fakeFile('clip.mp4', 'video/mp4');
+ const png = fakeFile('image.png', 'image/png');
+
+ it('image-only consumers do not advertise video MIME types', () => {
+ expect(Object.keys(getUploadDropzoneAccept(false)).some((mime) => mime.startsWith('video/'))).toBe(false);
+ expect(getUploadDropzoneAccept(true)).toBe(imageAndVideoDropzoneAccept);
+ });
+
+ it('rejects an MP4 submitted to an image-only consumer instead of uploading it', () => {
+ const { imageFiles, videoFiles, rejectedFiles } = partitionUploadFiles([png, mp4], false);
+ expect(imageFiles).toEqual([png]);
+ expect(videoFiles).toEqual([]); // nothing to hand to the video uploader
+ expect(rejectedFiles).toEqual([mp4]);
+ });
+
+ it('routes an MP4 to the video batch when the consumer opted in', () => {
+ const { imageFiles, videoFiles, rejectedFiles } = partitionUploadFiles([png, mp4], true);
+ expect(imageFiles).toEqual([png]);
+ expect(videoFiles).toEqual([mp4]);
+ expect(rejectedFiles).toEqual([]);
+ });
+});
diff --git a/invokeai/frontend/web/src/common/util/uploadMediaAccept.ts b/invokeai/frontend/web/src/common/util/uploadMediaAccept.ts
new file mode 100644
index 00000000000..5b186f31b88
--- /dev/null
+++ b/invokeai/frontend/web/src/common/util/uploadMediaAccept.ts
@@ -0,0 +1,92 @@
+import type { Accept } from 'react-dropzone';
+
+/**
+ * Single source of truth for which media files the client accepts for upload.
+ *
+ * The video lists must stay in sync with the video upload router
+ * (`invokeai/app/api/routers/videos.py`: `ACCEPTED_VIDEO_MIME_PREFIXES` /
+ * `ACCEPTED_VIDEO_EXTENSIONS`). The backend accepts MP4 only — it stores uploads under a
+ * `.mp4` name without transcoding — so advertising other containers client-side just
+ * produces a guaranteed 415 after the bytes have been uploaded.
+ */
+const ACCEPTED_IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'];
+const ACCEPTED_IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp'];
+export const ACCEPTED_VIDEO_TYPES = ['video/mp4'];
+export const ACCEPTED_VIDEO_EXTENSIONS = ['.mp4'];
+
+const addUpperCaseReducer = (acc: string[], ext: string) => {
+ acc.push(ext);
+ acc.push(ext.toUpperCase());
+ return acc;
+};
+
+/** react-dropzone accept map for image-only upload fields (board covers, style presets, etc). */
+export const imageDropzoneAccept: Accept = {
+ 'image/png': ['.png'].reduce(addUpperCaseReducer, [] as string[]),
+ 'image/jpeg': ['.jpg', '.jpeg', '.png'].reduce(addUpperCaseReducer, [] as string[]),
+ 'image/webp': ['.webp'].reduce(addUpperCaseReducer, [] as string[]),
+};
+
+/** react-dropzone accept map for the gallery uploader, which also takes videos. */
+export const imageAndVideoDropzoneAccept: Accept = {
+ ...imageDropzoneAccept,
+ 'video/mp4': ['.mp4'].reduce(addUpperCaseReducer, [] as string[]),
+};
+
+/** Returns true when the file is an uploadable video (by MIME or by extension). */
+export const isVideoFile = (file: File): boolean => {
+ if (file.type && ACCEPTED_VIDEO_TYPES.includes(file.type.toLowerCase())) {
+ return true;
+ }
+ const lower = file.name.toLowerCase();
+ return ACCEPTED_VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
+};
+
+/** Returns true when the file is an uploadable image (by MIME or by extension). */
+export const isImageFile = (file: File): boolean => {
+ if (file.type && ACCEPTED_IMAGE_TYPES.includes(file.type.toLowerCase())) {
+ return true;
+ }
+ const lower = file.name.toLowerCase();
+ return ACCEPTED_IMAGE_EXTENSIONS.some((ext) => lower.endsWith(ext));
+};
+
+/**
+ * Returns true when the file is uploadable media of either kind. MIME and extension each
+ * suffice on their own: browsers sometimes supply an empty or generic `File.type` (e.g. for
+ * a pasted or drag-dropped MP4), and the backend upload routes likewise accept either signal.
+ */
+export const isAcceptedUploadFile = (file: File): boolean => isImageFile(file) || isVideoFile(file);
+
+/** The dropzone accept map for an uploader: videos are opt-in, images always accepted. */
+export const getUploadDropzoneAccept = (allowVideos: boolean): Accept =>
+ allowVideos ? imageAndVideoDropzoneAccept : imageDropzoneAccept;
+
+type UploadPartition = {
+ imageFiles: File[];
+ videoFiles: File[];
+ /** Videos submitted to an uploader that did not opt into videos. */
+ rejectedFiles: File[];
+};
+
+/**
+ * Splits dropped files into per-media upload batches. Videos are only uploadable when the
+ * consumer explicitly opted in — an image-only consumer (e.g. a board cover or ref-image
+ * field) must reject an MP4 outright rather than upload it to the gallery and silently
+ * discard the resulting VideoDTO.
+ */
+export const partitionUploadFiles = (files: File[], allowVideos: boolean): UploadPartition => {
+ const imageFiles: File[] = [];
+ const videoFiles: File[] = [];
+ const rejectedFiles: File[] = [];
+ for (const file of files) {
+ if (!isVideoFile(file)) {
+ imageFiles.push(file);
+ } else if (allowVideos) {
+ videoFiles.push(file);
+ } else {
+ rejectedFiles.push(file);
+ }
+ }
+ return { imageFiles, videoFiles, rejectedFiles };
+};
diff --git a/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx b/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx
index 5ac6ffcb7c9..62cb8eacbf6 100644
--- a/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx
+++ b/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx
@@ -14,6 +14,7 @@ import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import { useAddImagesToBoardMutation, useRemoveImagesFromBoardMutation } from 'services/api/endpoints/images';
+import { useAddVideoToBoardMutation, useRemoveVideoFromBoardMutation } from 'services/api/endpoints/videos';
import type { BoardDTO } from 'services/api/types';
const selectImagesToChange = createSelector(
@@ -21,6 +22,11 @@ const selectImagesToChange = createSelector(
(changeBoardModal) => changeBoardModal.image_names
);
+const selectVideosToChange = createSelector(
+ selectChangeBoardModalSlice,
+ (changeBoardModal) => changeBoardModal.video_names
+);
+
const selectIsModalOpen = createSelector(
selectChangeBoardModalSlice,
(changeBoardModal) => changeBoardModal.isModalOpen
@@ -35,8 +41,11 @@ const ChangeBoardModal = () => {
const { data: boards, isFetching } = useListAllBoardsQuery({ include_archived: true });
const isModalOpen = useAppSelector(selectIsModalOpen);
const imagesToChange = useAppSelector(selectImagesToChange);
+ const videosToChange = useAppSelector(selectVideosToChange);
const [addImagesToBoard] = useAddImagesToBoardMutation();
const [removeImagesFromBoard] = useRemoveImagesFromBoardMutation();
+ const [addVideoToBoard] = useAddVideoToBoardMutation();
+ const [removeVideoFromBoard] = useRemoveVideoFromBoardMutation();
const { t } = useTranslation();
// Returns true if the current user can write images to the given board.
@@ -70,7 +79,7 @@ const ChangeBoardModal = () => {
}, [dispatch]);
const handleChangeBoard = useCallback(() => {
- if (!selectedBoardId || imagesToChange.length === 0) {
+ if (!selectedBoardId || (imagesToChange.length === 0 && videosToChange.length === 0)) {
return;
}
@@ -84,8 +93,30 @@ const ChangeBoardModal = () => {
});
}
}
+
+ if (videosToChange.length) {
+ // The video board endpoints take one video at a time; the context menu acts on a single
+ // selection, so this is normally a one-iteration loop.
+ for (const video_name of videosToChange) {
+ if (selectedBoardId === 'none') {
+ removeVideoFromBoard({ video_name });
+ } else {
+ addVideoToBoard({ board_id: selectedBoardId, video_name });
+ }
+ }
+ }
+
dispatch(changeBoardReset());
- }, [addImagesToBoard, dispatch, imagesToChange, removeImagesFromBoard, selectedBoardId]);
+ }, [
+ addImagesToBoard,
+ addVideoToBoard,
+ dispatch,
+ imagesToChange,
+ removeImagesFromBoard,
+ removeVideoFromBoard,
+ selectedBoardId,
+ videosToChange,
+ ]);
const onChange = useCallback((v) => {
if (!v) {
@@ -107,7 +138,7 @@ const ChangeBoardModal = () => {
{t('boards.movingImagesToBoard', {
- count: imagesToChange.length,
+ count: imagesToChange.length + videosToChange.length,
})}
diff --git a/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts b/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts
index 3f72720a420..dadf7a43b36 100644
--- a/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts
+++ b/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts
@@ -7,6 +7,7 @@ import z from 'zod';
const zChangeBoardModalState = z.object({
isModalOpen: z.boolean().default(false),
image_names: z.array(z.string()).default(() => []),
+ video_names: z.array(z.string()).default(() => []),
});
type ChangeBoardModalState = z.infer;
@@ -21,15 +22,21 @@ const slice = createSlice({
},
imagesToChangeSelected: (state, action: PayloadAction) => {
state.image_names = action.payload;
+ state.video_names = [];
+ },
+ videosToChangeSelected: (state, action: PayloadAction) => {
+ state.video_names = action.payload;
+ state.image_names = [];
},
changeBoardReset: (state) => {
state.image_names = [];
+ state.video_names = [];
state.isModalOpen = false;
},
},
});
-export const { isModalOpenChanged, imagesToChangeSelected, changeBoardReset } = slice.actions;
+export const { isModalOpenChanged, imagesToChangeSelected, videosToChangeSelected, changeBoardReset } = slice.actions;
export const selectChangeBoardModalSlice = (state: RootState) => state.changeBoardModal;
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageSettings.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageSettings.tsx
index 54b345361d5..3edf9594b79 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageSettings.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageSettings.tsx
@@ -39,6 +39,7 @@ import {
isFLUXReduxConfig,
isIPAdapterConfig,
isQwenImageReferenceImageConfig,
+ isWanReferenceImageConfig,
} from 'features/controlLayers/store/types';
import type { SetGlobalReferenceImageDndTargetData } from 'features/dnd/dnd';
import { setGlobalReferenceImageDndTarget } from 'features/dnd/dnd';
@@ -129,9 +130,12 @@ const RefImageSettingsContent = memo(() => {
const isFLUX = useAppSelector(selectIsFLUX);
const isExternalModel = !!mainModelConfig && isExternalApiModelConfig(mainModelConfig);
- // FLUX.2 Klein, Qwen Image Edit and external API models do not require a ref image model selection.
+ // FLUX.2 Klein, Qwen Image Edit, Wan 2.2 and external API models do not require a ref image model selection.
const showModelSelector =
- !isFlux2ReferenceImageConfig(config) && !isQwenImageReferenceImageConfig(config) && !isExternalModel;
+ !isFlux2ReferenceImageConfig(config) &&
+ !isQwenImageReferenceImageConfig(config) &&
+ !isWanReferenceImageConfig(config) &&
+ !isExternalModel;
return (
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
index 2027ff41741..10603191df6 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts
@@ -32,6 +32,7 @@ import type {
QwenImageReferenceImageConfig,
RegionalGuidanceIPAdapterConfig,
T2IAdapterConfig,
+ WanReferenceImageConfig,
} from 'features/controlLayers/store/types';
import {
initialControlNet,
@@ -41,6 +42,7 @@ import {
initialQwenImageReferenceImage,
initialRegionalGuidanceIPAdapter,
initialT2IAdapter,
+ initialWanReferenceImage,
} from 'features/controlLayers/store/util';
import { zModelIdentifierField } from 'features/nodes/types/common';
import { useCallback } from 'react';
@@ -80,7 +82,12 @@ export const selectDefaultControlAdapter = createSelector(
export const getDefaultRefImageConfig = (
getState: AppGetState
-): IPAdapterConfig | FluxKontextReferenceImageConfig | Flux2ReferenceImageConfig | QwenImageReferenceImageConfig => {
+):
+ | IPAdapterConfig
+ | FluxKontextReferenceImageConfig
+ | Flux2ReferenceImageConfig
+ | QwenImageReferenceImageConfig
+ | WanReferenceImageConfig => {
const state = getState();
const mainModelConfig = selectMainModelConfig(state);
@@ -98,6 +105,11 @@ export const getDefaultRefImageConfig = (
return deepClone(initialQwenImageReferenceImage);
}
+ // Wan 2.2 I2V uses the main model's own VAE - no adapter model needed
+ if (base === 'wan') {
+ return deepClone(initialWanReferenceImage);
+ }
+
if (base === 'flux' && mainModelConfig?.name?.toLowerCase().includes('kontext')) {
const config = deepClone(initialFluxKontextReferenceImage);
config.model = zModelIdentifierField.parse(mainModelConfig);
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
index b7b1a6ef1c0..1797949356a 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
@@ -304,6 +304,37 @@ const slice = createSlice({
qwenImageShiftChanged: (state, action: PayloadAction) => {
state.qwenImageShift = action.payload;
},
+ wanTransformerLowNoiseSelected: (state, action: PayloadAction) => {
+ const result = zParamsState.shape.wanTransformerLowNoise.safeParse(action.payload);
+ if (!result.success) {
+ return;
+ }
+ state.wanTransformerLowNoise = result.data;
+ },
+ wanComponentSourceSelected: (state, action: PayloadAction) => {
+ const result = zParamsState.shape.wanComponentSource.safeParse(action.payload);
+ if (!result.success) {
+ return;
+ }
+ state.wanComponentSource = result.data;
+ },
+ wanVaeModelSelected: (state, action: PayloadAction) => {
+ const result = zParamsState.shape.wanVaeModel.safeParse(action.payload);
+ if (!result.success) {
+ return;
+ }
+ state.wanVaeModel = result.data;
+ },
+ wanT5EncoderModelSelected: (state, action: PayloadAction<{ key: string; name: string; base: string } | null>) => {
+ const result = zParamsState.shape.wanT5EncoderModel.safeParse(action.payload);
+ if (!result.success) {
+ return;
+ }
+ state.wanT5EncoderModel = result.data;
+ },
+ wanGuidanceScaleLowNoiseChanged: (state, action: PayloadAction) => {
+ state.wanGuidanceScaleLowNoise = action.payload;
+ },
vaePrecisionChanged: (state, action: PayloadAction) => {
state.vaePrecision = action.payload;
},
@@ -636,6 +667,11 @@ const resetState = (state: ParamsState): ParamsState => {
newState.qwenImageQwenVLEncoderModel = oldState.qwenImageQwenVLEncoderModel;
newState.qwenImageQuantization = oldState.qwenImageQuantization;
newState.qwenImageShift = oldState.qwenImageShift;
+ newState.wanTransformerLowNoise = oldState.wanTransformerLowNoise;
+ newState.wanComponentSource = oldState.wanComponentSource;
+ newState.wanVaeModel = oldState.wanVaeModel;
+ newState.wanT5EncoderModel = oldState.wanT5EncoderModel;
+ newState.wanGuidanceScaleLowNoise = oldState.wanGuidanceScaleLowNoise;
return newState;
};
@@ -688,6 +724,11 @@ export const {
qwenImageQwenVLEncoderModelSelected,
qwenImageQuantizationChanged,
qwenImageShiftChanged,
+ wanTransformerLowNoiseSelected,
+ wanComponentSourceSelected,
+ wanVaeModelSelected,
+ wanT5EncoderModelSelected,
+ wanGuidanceScaleLowNoiseChanged,
setClipSkip,
shouldUseCpuNoiseChanged,
setColorCompensation,
@@ -779,6 +820,7 @@ export const selectIsAnima = createParamsSelector((params) => params.model?.base
export const selectIsFlux2 = createParamsSelector((params) => params.model?.base === 'flux2');
export const selectIsExternal = createParamsSelector((params) => params.model?.base === 'external');
export const selectIsQwenImage = createParamsSelector((params) => params.model?.base === 'qwen-image');
+export const selectIsWan = createParamsSelector((params) => params.model?.base === 'wan');
export const selectIsFluxKontext = createParamsSelector((params) => {
if (params.model?.base === 'flux' && params.model?.name.toLowerCase().includes('kontext')) {
return true;
@@ -811,6 +853,11 @@ export const selectQwenImageVaeModel = createParamsSelector((params) => params.q
export const selectQwenImageQwenVLEncoderModel = createParamsSelector((params) => params.qwenImageQwenVLEncoderModel);
export const selectQwenImageQuantization = createParamsSelector((params) => params.qwenImageQuantization);
export const selectQwenImageShift = createParamsSelector((params) => params.qwenImageShift);
+export const selectWanTransformerLowNoise = createParamsSelector((params) => params.wanTransformerLowNoise);
+export const selectWanComponentSource = createParamsSelector((params) => params.wanComponentSource);
+export const selectWanVaeModel = createParamsSelector((params) => params.wanVaeModel);
+export const selectWanT5EncoderModel = createParamsSelector((params) => params.wanT5EncoderModel);
+export const selectWanGuidanceScaleLowNoise = createParamsSelector((params) => params.wanGuidanceScaleLowNoise);
export const selectCFGScale = createParamsSelector((params) => params.cfgScale);
export const selectGuidance = createParamsSelector((params) => params.guidance);
@@ -870,7 +917,16 @@ export const selectModelSupportsRefImages = createSelector(selectModel, selectMo
if (model.base === 'external') {
return false;
}
- return SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base);
+ if (!SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base)) {
+ return false;
+ }
+ // Wan: only the I2V variant of A14B consumes a reference image. T2V and
+ // TI2V-5B ignore ref images, so hide the panel for those.
+ if (model.base === 'wan') {
+ const variant = modelConfig && 'variant' in modelConfig ? modelConfig.variant : null;
+ return variant === 'i2v_a14b';
+ }
+ return true;
});
export const selectModelSupportsOptimizedDenoising = createSelector(
selectModel,
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts
index b7026b586a8..6c364e51e88 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts
@@ -23,6 +23,7 @@ import {
isFLUXReduxConfig,
isIPAdapterConfig,
isQwenImageReferenceImageConfig,
+ isWanReferenceImageConfig,
zRefImagesState,
} from './types';
import { getReferenceImageState, initialFluxKontextReferenceImage, initialFLUXRedux, initialIPAdapter } from './util';
@@ -144,8 +145,12 @@ const slice = createSlice({
return;
}
- // FLUX.2 and Qwen Image Edit reference images don't have a model field - they use built-in support
- if (isFlux2ReferenceImageConfig(entity.config) || isQwenImageReferenceImageConfig(entity.config)) {
+ // FLUX.2, Qwen Image Edit and Wan reference images don't have a model field - they use built-in support
+ if (
+ isFlux2ReferenceImageConfig(entity.config) ||
+ isQwenImageReferenceImageConfig(entity.config) ||
+ isWanReferenceImageConfig(entity.config)
+ ) {
return;
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
index 43ea53d13f3..081b9055819 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
@@ -418,6 +418,15 @@ const zQwenImageReferenceImageConfig = z.object({
});
export type QwenImageReferenceImageConfig = z.infer;
+// Wan 2.2 I2V uses the model's own VAE to encode a single reference image -
+// no separate adapter model needed. Only consumed by the I2V variant of Wan
+// 2.2 (A14B). T2V / TI2V variants ignore the ref image at graph build time.
+const zWanReferenceImageConfig = z.object({
+ type: z.literal('wan_reference_image'),
+ image: zCroppableImageWithDims.nullable(),
+});
+export type WanReferenceImageConfig = z.infer;
+
const zCanvasEntityBase = z.object({
id: zId,
name: zName,
@@ -434,6 +443,7 @@ export const zRefImageState = z.object({
zFluxKontextReferenceImageConfig,
zFlux2ReferenceImageConfig,
zQwenImageReferenceImageConfig,
+ zWanReferenceImageConfig,
]),
});
export type RefImageState = z.infer;
@@ -455,6 +465,9 @@ export const isQwenImageReferenceImageConfig = (
config: RefImageState['config']
): config is QwenImageReferenceImageConfig => config.type === 'qwen_image_reference_image';
+export const isWanReferenceImageConfig = (config: RefImageState['config']): config is WanReferenceImageConfig =>
+ config.type === 'wan_reference_image';
+
const zFillStyle = z.enum(['solid', 'grid', 'crosshatch', 'diagonal', 'horizontal', 'vertical']);
export type FillStyle = z.infer;
export const isFillStyle = (v: unknown): v is FillStyle => zFillStyle.safeParse(v).success;
@@ -861,6 +874,13 @@ export const zParamsState = z.object({
qwenImageQwenVLEncoderModel: zModelIdentifierField.nullable(), // Optional: Standalone Qwen2.5-VL encoder
qwenImageQuantization: z.enum(['none', 'int8', 'nf4']), // BitsAndBytes quantization for Qwen VL encoder
qwenImageShift: z.number().nullable(), // Sigma schedule shift override (e.g. 3.0 for Lightning LoRAs)
+ // Wan 2.2 model components — A14B GGUF needs a paired second-expert transformer
+ // plus a Diffusers source for VAE/T5 unless standalone VAE/encoder models are wired.
+ wanTransformerLowNoise: zParameterModel.nullable(), // A14B GGUF only: second-expert transformer
+ wanComponentSource: zParameterModel.nullable(), // Diffusers Wan model providing VAE + UMT5-XXL
+ wanVaeModel: zParameterVAEModel.nullable(), // Optional: Standalone Wan VAE checkpoint
+ wanT5EncoderModel: zModelIdentifierField.nullable(), // Optional: Standalone UMT5-XXL encoder
+ wanGuidanceScaleLowNoise: z.number().nullable(), // Optional: separate CFG for low-noise expert (A14B). null = same as primary
// Z-Image Seed Variance Enhancer settings
zImageSeedVarianceEnabled: z.boolean(),
zImageSeedVarianceStrength: z.number().min(0).max(2),
@@ -947,6 +967,11 @@ export const getInitialParamsState = (): ParamsState => ({
qwenImageQwenVLEncoderModel: null,
qwenImageQuantization: 'none' as const,
qwenImageShift: null,
+ wanTransformerLowNoise: null,
+ wanComponentSource: null,
+ wanVaeModel: null,
+ wanT5EncoderModel: null,
+ wanGuidanceScaleLowNoise: null,
zImageSeedVarianceEnabled: false,
zImageSeedVarianceStrength: 0.1,
zImageSeedVarianceRandomizePercent: 50,
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/util.ts b/invokeai/frontend/web/src/features/controlLayers/store/util.ts
index b28d74d7b54..a0dae2145d0 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/util.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/util.ts
@@ -22,6 +22,7 @@ import type {
RegionalGuidanceIPAdapterConfig,
RgbColor,
T2IAdapterConfig,
+ WanReferenceImageConfig,
ZImageControlConfig,
} from 'features/controlLayers/store/types';
import type { ImageDTO } from 'services/api/types';
@@ -123,6 +124,10 @@ export const initialQwenImageReferenceImage: QwenImageReferenceImageConfig = {
type: 'qwen_image_reference_image',
image: null,
};
+export const initialWanReferenceImage: WanReferenceImageConfig = {
+ type: 'wan_reference_image',
+ image: null,
+};
export const initialT2IAdapter: T2IAdapterConfig = {
type: 't2i_adapter',
model: null,
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts b/invokeai/frontend/web/src/features/controlLayers/store/validators.ts
index 504280861e6..46e88786a40 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/validators.ts
@@ -148,8 +148,12 @@ export const getGlobalReferenceImageWarnings = (
const { config } = entity;
- // FLUX.2 and Qwen Image Edit reference images don't require a model - it's built-in
- if (config.type !== 'flux2_reference_image' && config.type !== 'qwen_image_reference_image') {
+ // FLUX.2, Qwen Image Edit and Wan reference images don't require a model - it's built-in
+ if (
+ config.type !== 'flux2_reference_image' &&
+ config.type !== 'qwen_image_reference_image' &&
+ config.type !== 'wan_reference_image'
+ ) {
if (!('model' in config) || !config.model) {
// No model selected
warnings.push(WARNINGS.IP_ADAPTER_NO_MODEL_SELECTED);
@@ -160,8 +164,10 @@ export const getGlobalReferenceImageWarnings = (
}
if (!entity.config.image) {
- // No image selected - for Qwen Image Edit, an image is optional (txt2img works without one)
- if (config.type !== 'qwen_image_reference_image') {
+ // No image selected - for Qwen Image Edit and Wan, an image is optional at the
+ // entity level. Wan I2V *requires* one but enforcement happens at graph-build
+ // time so the warning doesn't fire on T2V/TI2V variants that ignore ref images.
+ if (config.type !== 'qwen_image_reference_image' && config.type !== 'wan_reference_image') {
warnings.push(WARNINGS.IP_ADAPTER_NO_IMAGE_SELECTED);
}
}
diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/state.ts b/invokeai/frontend/web/src/features/deleteImageModal/store/state.ts
index c50aa9465f5..2df56133f89 100644
--- a/invokeai/frontend/web/src/features/deleteImageModal/store/state.ts
+++ b/invokeai/frontend/web/src/features/deleteImageModal/store/state.ts
@@ -11,8 +11,12 @@ import {
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
import type { CanvasState, RefImagesState } from 'features/controlLayers/store/types';
import type { ImageUsage } from 'features/deleteImageModal/store/types';
-import { selectGetImageNamesQueryArgs } from 'features/gallery/store/gallerySelectors';
+import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
import { imageSelected } from 'features/gallery/store/gallerySlice';
+import {
+ pickSelectionAfterDelete,
+ selectCachedGalleryItemNames,
+} from 'features/gallery/store/selectCachedGalleryItemNames';
import { fieldImageCollectionValueChanged, fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { selectNodesSlice } from 'features/nodes/store/selectors';
import type { NodesState } from 'features/nodes/store/types';
@@ -80,22 +84,25 @@ const handleDeletions = async (image_names: string[], store: AppStore) => {
try {
const { dispatch, getState } = store;
const state = getState();
- const { data } = imagesApi.endpoints.getImageNames.select(selectGetImageNamesQueryArgs(state))(state);
- const index = data?.image_names.findIndex((name) => name === image_names[0]);
- const { deleted_images } = await dispatch(
- imagesApi.endpoints.deleteImages.initiate({ image_names }, { track: false })
- ).unwrap();
- const newImageNames = data?.image_names.filter((name) => !deleted_images.includes(name)) || [];
- const newSelectedImage = newImageNames[index ?? 0] || null;
+ // Snapshot the polymorphic gallery list (images + videos, in display order) and the
+ // currently-displayed item *before* the delete fires. Once the request resolves the
+ // cache will have shifted, so the index computed afterwards would be wrong.
+ const galleryItemNames = selectCachedGalleryItemNames(state);
+ const lastSelected = selectLastSelectedItem(state);
+ const lastSelectedIndex =
+ lastSelected && image_names.includes(lastSelected) ? galleryItemNames.indexOf(lastSelected) : -1;
+
+ await dispatch(imagesApi.endpoints.deleteImages.initiate({ image_names }, { track: false })).unwrap();
if (intersection(state.gallery.selection, image_names).length > 0) {
- if (newSelectedImage) {
- // Some selected images were deleted, clear selection
- dispatch(imageSelected(newSelectedImage));
- } else {
- dispatch(imageSelected(null));
- }
+ // Advance to a still-living neighbour (prev > next) so the Viewer keeps a real
+ // selection. May pick a video — the polymorphic list intentionally allows that.
+ const replacement =
+ lastSelectedIndex >= 0
+ ? pickSelectionAfterDelete(galleryItemNames, lastSelectedIndex, new Set(image_names))
+ : null;
+ dispatch(imageSelected(replacement));
}
// We need to reset the features where the image is in use - none of these work if their image(s) don't exist
diff --git a/invokeai/frontend/web/src/features/deleteVideoModal/components/DeleteVideoModal.tsx b/invokeai/frontend/web/src/features/deleteVideoModal/components/DeleteVideoModal.tsx
new file mode 100644
index 00000000000..38c2d7828ca
--- /dev/null
+++ b/invokeai/frontend/web/src/features/deleteVideoModal/components/DeleteVideoModal.tsx
@@ -0,0 +1,49 @@
+import { ConfirmationAlertDialog, Flex, FormControl, FormLabel, Switch, Text } from '@invoke-ai/ui-library';
+import { useAppSelector, useAppStore } from 'app/store/storeHooks';
+import { useDeleteVideoModalApi, useDeleteVideoModalState } from 'features/deleteVideoModal/store/state';
+import { selectSystemShouldConfirmOnDelete, setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
+import type { ChangeEvent } from 'react';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+/**
+ * Confirmation dialog for deleting videos. Mirrors DeleteImageModal so the experience is
+ * consistent: same "don't ask me again" toggle (shared system setting), same accept/cancel
+ * buttons, same destructive styling. Skips the image dialog's usage analysis because
+ * videos don't show up as canvas layers / node fields / ref images.
+ */
+export const DeleteVideoModal = memo(() => {
+ const state = useDeleteVideoModalState();
+ const api = useDeleteVideoModalApi();
+ const { dispatch } = useAppStore();
+ const { t } = useTranslation();
+ const shouldConfirmOnDelete = useAppSelector(selectSystemShouldConfirmOnDelete);
+
+ const handleChangeShouldConfirmOnDelete = useCallback(
+ (e: ChangeEvent) => dispatch(setShouldConfirmOnDelete(!e.target.checked)),
+ [dispatch]
+ );
+
+ return (
+
+
+ {t('gallery.deleteVideoPermanent')}
+ {t('common.areYouSure')}
+
+ {t('common.dontAskMeAgain')}
+
+
+
+
+ );
+});
+DeleteVideoModal.displayName = 'DeleteVideoModal';
diff --git a/invokeai/frontend/web/src/features/deleteVideoModal/store/state.test.ts b/invokeai/frontend/web/src/features/deleteVideoModal/store/state.test.ts
new file mode 100644
index 00000000000..04c4dc6ff3e
--- /dev/null
+++ b/invokeai/frontend/web/src/features/deleteVideoModal/store/state.test.ts
@@ -0,0 +1,124 @@
+/**
+ * Regression tests for the post-delete selection logic in the video delete flow.
+ *
+ * The bug (PR #9163 review): ``handleDeletions`` advanced the gallery selection as if every
+ * requested video was deleted, even when individual ``deleteVideo`` calls failed (403/500).
+ * The failed names were included in the "deleted" set passed to ``pickSelectionAfterDelete``,
+ * so the Viewer could jump away from a video that still exists — and a surviving neighbour
+ * could be skipped as a replacement candidate.
+ *
+ * The fix tracks which deletions actually resolved and only treats those as deleted.
+ */
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+vi.mock('services/api/endpoints/videos', () => ({
+ videosApi: {
+ endpoints: {
+ deleteVideo: {
+ initiate: vi.fn((arg: { video_name: string }) => ({
+ type: 'videosApi/deleteVideo',
+ video_name: arg.video_name,
+ })),
+ },
+ },
+ },
+}));
+
+vi.mock('features/gallery/store/gallerySelectors', () => ({
+ selectLastSelectedItem: vi.fn(),
+}));
+
+vi.mock('features/gallery/store/gallerySlice', () => ({
+ imageSelected: vi.fn((payload: string | null) => ({ type: 'gallery/imageSelected', payload })),
+}));
+
+vi.mock('features/system/store/systemSlice', () => ({
+ selectSystemShouldConfirmOnDelete: vi.fn(() => false),
+}));
+
+// Keep the real pickSelectionAfterDelete (its neighbour-picking is part of the behavior under
+// test) but stub the cache selector, which would otherwise need a live RTK Query store.
+vi.mock('features/gallery/store/selectCachedGalleryItemNames', async (importOriginal) => {
+ const actual = await importOriginal();
+ return { ...actual, selectCachedGalleryItemNames: vi.fn() };
+});
+
+import type { AppStore } from 'app/store/store';
+import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
+import { imageSelected } from 'features/gallery/store/gallerySlice';
+import { selectCachedGalleryItemNames } from 'features/gallery/store/selectCachedGalleryItemNames';
+
+import { handleDeletions } from './state';
+
+const buildStore = (selection: string[], failingNames: Set) => {
+ const dispatched: unknown[] = [];
+ const dispatch = vi.fn((action: unknown) => {
+ dispatched.push(action);
+ const typed = action as { type?: string; video_name?: string };
+ if (typed?.type === 'videosApi/deleteVideo') {
+ return {
+ unwrap: () =>
+ typed.video_name && failingNames.has(typed.video_name)
+ ? Promise.reject(new Error('delete failed'))
+ : Promise.resolve(undefined),
+ };
+ }
+ return action;
+ });
+ const getState = vi.fn(() => ({ gallery: { selection } }));
+ return { store: { dispatch, getState } as unknown as AppStore, dispatched };
+};
+
+const getSelectionChange = (dispatched: unknown[]) =>
+ dispatched.find(
+ (action): action is { type: string; payload: string | null } =>
+ !!action && typeof action === 'object' && (action as { type?: string }).type === 'gallery/imageSelected'
+ );
+
+describe('handleDeletions selection behavior on partial failure', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ vi.mocked(selectCachedGalleryItemNames).mockReturnValue(['a.mp4', 'b.mp4', 'c.png']);
+ });
+
+ it('does not move the selection when the displayed video fails to delete', async () => {
+ vi.mocked(selectLastSelectedItem).mockReturnValue('a.mp4');
+ const { store, dispatched } = buildStore(['a.mp4'], new Set(['a.mp4']));
+
+ await handleDeletions(['a.mp4'], store);
+
+ expect(getSelectionChange(dispatched), 'a failed delete must not advance the selection').toBeUndefined();
+ });
+
+ it('keeps a surviving (failed-delete) neighbour as the replacement candidate', async () => {
+ vi.mocked(selectLastSelectedItem).mockReturnValue('a.mp4');
+ // Batch delete of a + b: a (the displayed item) deletes fine, b fails and still exists.
+ const { store, dispatched } = buildStore(['a.mp4'], new Set(['b.mp4']));
+
+ await handleDeletions(['a.mp4', 'b.mp4'], store);
+
+ // Before the fix, b.mp4 was excluded as "deleted" and the selection skipped to c.png.
+ expect(getSelectionChange(dispatched)?.payload).toBe('b.mp4');
+ });
+
+ it('keeps viewing the displayed video when its delete fails but another selected video was deleted', async () => {
+ vi.mocked(selectLastSelectedItem).mockReturnValue('a.mp4');
+ const { store, dispatched } = buildStore(['a.mp4', 'b.mp4'], new Set(['a.mp4']));
+
+ await handleDeletions(['a.mp4', 'b.mp4'], store);
+
+ // The multi-selection contained a deleted item (b), so the selection is pruned — but it
+ // must land on the still-existing displayed video, not jump to a neighbour.
+ expect(getSelectionChange(dispatched)?.payload).toBe('a.mp4');
+ });
+
+ it('advances to the nearest surviving neighbour when everything requested is deleted', async () => {
+ vi.mocked(selectLastSelectedItem).mockReturnValue('b.mp4');
+ const { store, dispatched } = buildStore(['b.mp4'], new Set());
+
+ await handleDeletions(['b.mp4'], store);
+
+ expect(getSelectionChange(dispatched)?.payload).toBe('a.mp4');
+ expect(imageSelected).toHaveBeenCalledWith('a.mp4');
+ });
+});
diff --git a/invokeai/frontend/web/src/features/deleteVideoModal/store/state.ts b/invokeai/frontend/web/src/features/deleteVideoModal/store/state.ts
new file mode 100644
index 00000000000..d59d886f829
--- /dev/null
+++ b/invokeai/frontend/web/src/features/deleteVideoModal/store/state.ts
@@ -0,0 +1,126 @@
+import { useStore } from '@nanostores/react';
+import type { AppStore } from 'app/store/store';
+import { useAppStore } from 'app/store/storeHooks';
+import { intersection } from 'es-toolkit/compat';
+import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
+import { imageSelected } from 'features/gallery/store/gallerySlice';
+import {
+ pickSelectionAfterDelete,
+ selectCachedGalleryItemNames,
+} from 'features/gallery/store/selectCachedGalleryItemNames';
+import { selectSystemShouldConfirmOnDelete } from 'features/system/store/systemSlice';
+import { atom } from 'nanostores';
+import { useMemo } from 'react';
+import { videosApi } from 'services/api/endpoints/videos';
+
+// Parallel of features/deleteImageModal/store/state.ts but trimmed: videos don't show up
+// on canvas layers, node fields, ref-image entities, or upscale inputs the way images do,
+// so there is no "usage" analysis to compute. The dialog is a straight confirm.
+
+type DeleteVideosModalState = {
+ video_names: string[];
+ isOpen: boolean;
+ resolve?: () => void;
+ reject?: (reason?: string) => void;
+};
+
+const getInitialState = (): DeleteVideosModalState => ({
+ video_names: [],
+ isOpen: false,
+});
+
+const $deleteModalState = atom(getInitialState());
+
+const deleteVideosWithDialog = async (video_names: string[], store: AppStore): Promise => {
+ const { getState } = store;
+ const shouldConfirmOnDelete = selectSystemShouldConfirmOnDelete(getState());
+
+ if (!shouldConfirmOnDelete) {
+ await handleDeletions(video_names, store);
+ return;
+ }
+
+ return new Promise((resolve, reject) => {
+ $deleteModalState.set({
+ video_names,
+ isOpen: true,
+ resolve,
+ reject,
+ });
+ });
+};
+
+export const handleDeletions = async (video_names: string[], store: AppStore) => {
+ const { dispatch, getState } = store;
+
+ // Snapshot the polymorphic gallery list and the currently-displayed item *before* the
+ // delete fires; once the network call resolves the cache will already have shifted.
+ const stateBefore = getState();
+ const galleryItemNames = selectCachedGalleryItemNames(stateBefore);
+ const lastSelected = selectLastSelectedItem(stateBefore);
+ const lastSelectedIndex =
+ lastSelected && video_names.includes(lastSelected) ? galleryItemNames.indexOf(lastSelected) : -1;
+
+ // The backend exposes single-video DELETE today; loop here so the API surface for callers
+ // stays "give me a list, I'll handle it" and a future bulk endpoint can be slotted in
+ // without touching call sites.
+ const deletedNames = new Set();
+ for (const video_name of video_names) {
+ try {
+ await dispatch(videosApi.endpoints.deleteVideo.initiate({ video_name }, { track: false })).unwrap();
+ deletedNames.add(video_name);
+ } catch {
+ // Continue with the rest of the batch — partial failures shouldn't leave the user
+ // with a broken modal state.
+ }
+ }
+
+ // If anything in the active selection was actually deleted, advance to a still-living
+ // neighbour (prev > next) so the Viewer doesn't drop to its empty-state placeholder.
+ // Failed deletions are excluded: those videos still exist, so the selection must not
+ // jump away from them, and they remain valid replacement candidates.
+ const stateAfter = getState();
+ if (intersection(stateAfter.gallery.selection, [...deletedNames]).length > 0) {
+ if (lastSelected && !deletedNames.has(lastSelected)) {
+ // The displayed item survived (its delete failed) — keep viewing it and just prune
+ // the deleted items from the multi-selection.
+ dispatch(imageSelected(lastSelected));
+ } else {
+ const replacement =
+ lastSelectedIndex >= 0 ? pickSelectionAfterDelete(galleryItemNames, lastSelectedIndex, deletedNames) : null;
+ dispatch(imageSelected(replacement));
+ }
+ }
+};
+
+const confirmDeletion = async (store: AppStore) => {
+ const state = $deleteModalState.get();
+ await handleDeletions(state.video_names, store);
+ state.resolve?.();
+ closeSilently();
+};
+
+const cancelDeletion = () => {
+ const state = $deleteModalState.get();
+ state.reject?.('User canceled');
+ closeSilently();
+};
+
+const closeSilently = () => {
+ $deleteModalState.set(getInitialState());
+};
+
+export const useDeleteVideoModalState = () => useStore($deleteModalState);
+
+export const useDeleteVideoModalApi = () => {
+ const store = useAppStore();
+ return useMemo(
+ () => ({
+ delete: (video_names: string[]) => deleteVideosWithDialog(video_names, store),
+ confirm: () => confirmDeletion(store),
+ cancel: cancelDeletion,
+ close: closeSilently,
+ }),
+ [store]
+ );
+};
diff --git a/invokeai/frontend/web/src/features/dnd/FullscreenDropzone.tsx b/invokeai/frontend/web/src/features/dnd/FullscreenDropzone.tsx
index e5d7df68f28..939c04135a4 100644
--- a/invokeai/frontend/web/src/features/dnd/FullscreenDropzone.tsx
+++ b/invokeai/frontend/web/src/features/dnd/FullscreenDropzone.tsx
@@ -7,8 +7,10 @@ import { Box, Flex, Heading } from '@invoke-ai/ui-library';
import { getStore } from 'app/store/nanostores/store';
import { useAppSelector } from 'app/store/storeHooks';
import { getFocusedRegion } from 'common/hooks/focus';
+import { isVideoFile } from 'common/util/uploadMediaAccept';
import { setFileToPaste } from 'features/controlLayers/components/CanvasPasteModal';
import { DndDropOverlay } from 'features/dnd/DndDropOverlay';
+import { zUploadFile } from 'features/dnd/fullscreenDropzoneAccept';
import type { DndTargetState } from 'features/dnd/types';
import { selectAutoAddBoardId } from 'features/gallery/store/gallerySelectors';
import { toast } from 'features/toast/toast';
@@ -16,40 +18,11 @@ import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { uploadImages } from 'services/api/endpoints/images';
+import { uploadVideos } from 'services/api/endpoints/videos';
import { useBoardName } from 'services/api/hooks/useBoardName';
-import type { UploadImageArg } from 'services/api/types';
+import type { UploadImageArg, UploadVideoArg } from 'services/api/types';
import { z } from 'zod';
-const ACCEPTED_IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'];
-const ACCEPTED_FILE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.webp'];
-
-// const MAX_IMAGE_SIZE = 4; //In MegaBytes
-// const sizeInMB = (sizeInBytes: number, decimalsNum = 2) => {
-// const result = sizeInBytes / (1024 * 1024);
-// return +result.toFixed(decimalsNum);
-// };
-
-const zUploadFile = z
- .custom()
- // .refine(
- // (file) => {
- // return sizeInMB(file.size) <= MAX_IMAGE_SIZE;
- // },
- // () => ({ message: `The maximum image size is ${MAX_IMAGE_SIZE}MB` })
- // )
- .refine(
- (file) => {
- return ACCEPTED_IMAGE_TYPES.includes(file.type.toLowerCase());
- },
- { message: `File type is not supported` }
- )
- .refine(
- (file) => {
- return ACCEPTED_FILE_EXTENSIONS.some((ext) => file.name.toLowerCase().endsWith(ext));
- },
- { message: `File extension is not supported` }
- );
-
const sx = {
position: 'absolute',
top: 2,
@@ -73,7 +46,9 @@ export const FullscreenDropzone = memo(() => {
const parseResult = z.array(zUploadFile).safeParse(files);
if (!parseResult.success) {
- const description = t('toast.uploadFailedInvalidUploadDesc');
+ // The fullscreen surface accepts videos too, so its invalid-upload message must
+ // mention MP4 (unlike image-only upload fields, which keep the image-only text).
+ const description = t('toast.uploadFailedInvalidMediaUploadDesc');
toast({
id: 'UPLOAD_FAILED',
@@ -86,25 +61,49 @@ export const FullscreenDropzone = memo(() => {
const focusedRegion = getFocusedRegion();
- // While on the canvas tab and when pasting a single image, canvas may want to create a new layer. Let it handle
- // the paste event.
- const [firstImageFile] = files;
- if (focusedRegion === 'canvas' && activeTab === 'canvas' && files.length === 1 && firstImageFile) {
- setFileToPaste(firstImageFile);
+ // While on the canvas tab and when pasting a single image (not a video — the canvas can't
+ // host videos), canvas may want to create a new layer. Let it handle the paste event.
+ const [firstFile] = files;
+ if (
+ focusedRegion === 'canvas' &&
+ activeTab === 'canvas' &&
+ files.length === 1 &&
+ firstFile &&
+ !isVideoFile(firstFile)
+ ) {
+ setFileToPaste(firstFile);
return;
}
const autoAddBoardId = selectAutoAddBoardId(getState());
+ const boardId = autoAddBoardId === 'none' ? undefined : autoAddBoardId;
+
+ // Split files by media type so each batch goes through its own uploader. Image and video
+ // uploaders are independent — they each update their own RTK cache + invalidate the gallery.
+ const imageFiles = files.filter((f) => !isVideoFile(f));
+ const videoFiles = files.filter((f) => isVideoFile(f));
+
+ if (imageFiles.length > 0) {
+ const imageUploadArgs: UploadImageArg[] = imageFiles.map((file, i) => ({
+ file,
+ image_category: 'user',
+ is_intermediate: false,
+ board_id: boardId,
+ isFirstUploadOfBatch: i === 0,
+ }));
+ uploadImages(imageUploadArgs);
+ }
- const uploadArgs: UploadImageArg[] = files.map((file, i) => ({
- file,
- image_category: 'user',
- is_intermediate: false,
- board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId,
- isFirstUploadOfBatch: i === 0,
- }));
-
- uploadImages(uploadArgs);
+ if (videoFiles.length > 0) {
+ const videoUploadArgs: UploadVideoArg[] = videoFiles.map((file, i) => ({
+ file,
+ video_category: 'user',
+ is_intermediate: false,
+ board_id: boardId,
+ isFirstUploadOfBatch: i === 0,
+ }));
+ uploadVideos(videoUploadArgs);
+ }
},
[activeTab, t]
);
@@ -180,7 +179,7 @@ const DropLabel = memo(() => {
return (
{t('gallery.dropToUpload')}
- {t('toast.imagesWillBeAddedTo', { boardName })}
+ {t('toast.itemsWillBeAddedTo', { boardName })}
);
});
diff --git a/invokeai/frontend/web/src/features/dnd/dnd.ts b/invokeai/frontend/web/src/features/dnd/dnd.ts
index 8ed60799407..d5494592694 100644
--- a/invokeai/frontend/web/src/features/dnd/dnd.ts
+++ b/invokeai/frontend/web/src/features/dnd/dnd.ts
@@ -9,20 +9,25 @@ import { selectComparisonImages } from 'features/gallery/components/ImageViewer/
import type { BoardId } from 'features/gallery/store/types';
import {
addImagesToBoard,
+ addVideosToBoard,
+ addVideoToBoard,
createNewCanvasEntityFromImage,
newCanvasFromImage,
removeImagesFromBoard,
+ removeVideoFromBoard,
+ removeVideosFromBoard,
replaceCanvasEntityObjectsWithImage,
setComparisonImage,
setGlobalReferenceImage,
setNodeImageFieldImage,
+ setNodeVideoFieldVideo,
setRegionalGuidanceReferenceImage,
setUpscaleInitialImage,
} from 'features/imageActions/actions';
import { fieldImageCollectionValueChanged } from 'features/nodes/store/nodesSlice';
import { selectFieldInputInstanceSafe, selectNodesSlice } from 'features/nodes/store/selectors';
import { type FieldIdentifier, isImageFieldCollectionInputInstance } from 'features/nodes/types/field';
-import type { ImageDTO } from 'services/api/types';
+import type { ImageDTO, VideoDTO } from 'services/api/types';
import type { JsonObject } from 'type-fest';
const log = logger('dnd');
@@ -83,12 +88,26 @@ export const singleImageDndSource: DndSource = {
};
//#endregion
+//#region Single Video
+const _singleVideo = buildTypeAndKey('single-video');
+type SingleVideoDndSourceData = DndData;
+export const singleVideoDndSource: DndSource = {
+ ..._singleVideo,
+ typeGuard: buildTypeGuard(_singleVideo.key),
+ getData: buildGetData(_singleVideo.key, _singleVideo.type),
+};
+//#endregion
+
//#region Multiple Image
+// `image_names` is the primary payload (used by the drag preview heading and the image-field
+// collection drop target). `video_names` rides along so that mixed selections dragged from an
+// image thumbnail still move the videos to the board — the board drop handler dispatches both
+// mutations when both arrays are populated.
const _multipleImage = buildTypeAndKey('multiple-image');
export type MultipleImageDndSourceData = DndData<
typeof _multipleImage.type,
typeof _multipleImage.key,
- { image_names: string[]; board_id: BoardId }
+ { image_names: string[]; video_names: string[]; board_id: BoardId }
>;
export const multipleImageDndSource: DndSource = {
..._multipleImage,
@@ -97,6 +116,22 @@ export const multipleImageDndSource: DndSource = {
};
//#endregion
+//#region Multiple Video
+// Symmetric to MultipleImageDndSourceData: `video_names` is the primary payload, `image_names`
+// rides along for mixed selections dragged from a video thumbnail.
+const _multipleVideo = buildTypeAndKey('multiple-video');
+type MultipleVideoDndSourceData = DndData<
+ typeof _multipleVideo.type,
+ typeof _multipleVideo.key,
+ { video_names: string[]; image_names: string[]; board_id: BoardId }
+>;
+export const multipleVideoDndSource: DndSource = {
+ ..._multipleVideo,
+ typeGuard: buildTypeGuard(_multipleVideo.key),
+ getData: buildGetData(_multipleVideo.key, _multipleVideo.type),
+};
+//#endregion
+
//#region Single Reference Image (reorder)
const _singleRefImage = buildTypeAndKey('single-ref-image');
type SingleRefImageDndSourceData = DndData;
@@ -273,6 +308,32 @@ export const setNodeImageFieldImageDndTarget: DndTarget;
+export const setNodeVideoFieldVideoDndTarget: DndTarget =
+ {
+ ..._setNodeVideoFieldVideo,
+ typeGuard: buildTypeGuard(_setNodeVideoFieldVideo.key),
+ getData: buildGetData(_setNodeVideoFieldVideo.key, _setNodeVideoFieldVideo.type),
+ isValid: ({ sourceData }) => {
+ if (singleVideoDndSource.typeGuard(sourceData)) {
+ return true;
+ }
+ return false;
+ },
+ handler: ({ sourceData, targetData, dispatch }) => {
+ const { videoDTO } = sourceData.payload;
+ const { fieldIdentifier } = targetData.payload;
+ setNodeVideoFieldVideo({ fieldIdentifier, videoDTO, dispatch });
+ },
+ };
+//#endregion
+
//#region Add Images to Image Collection Node Field
const _addImagesToNodeImageFieldCollection = buildTypeAndKey('add-images-to-image-collection-node-field');
export type AddImagesToNodeImageFieldCollection = DndData<
@@ -495,7 +556,7 @@ export type AddImageToBoardDndTargetData = DndData<
>;
export const addImageToBoardDndTarget: DndTarget<
AddImageToBoardDndTargetData,
- SingleImageDndSourceData | MultipleImageDndSourceData
+ SingleImageDndSourceData | MultipleImageDndSourceData | SingleVideoDndSourceData | MultipleVideoDndSourceData
> = {
..._addToBoard,
typeGuard: buildTypeGuard(_addToBoard.key),
@@ -518,6 +579,26 @@ export const addImageToBoardDndTarget: DndTarget<
}
return canMoveFromSourceBoard(currentBoard, getState);
}
+ if (singleVideoDndSource.typeGuard(sourceData)) {
+ const currentBoard = sourceData.payload.videoDTO.board_id ?? 'none';
+ const destinationBoard = targetData.payload.boardId;
+ if (currentBoard === destinationBoard) {
+ return false;
+ }
+ // Same source-board permission check as images. Backend additionally
+ // enforces _assert_video_direct_owner — a stricter check the client can't
+ // perform without each video's owner, so we let those failures bubble up
+ // through the mutation rather than blocking the drop preemptively.
+ return canMoveFromSourceBoard(currentBoard, getState);
+ }
+ if (multipleVideoDndSource.typeGuard(sourceData)) {
+ const currentBoard = sourceData.payload.board_id;
+ const destinationBoard = targetData.payload.boardId;
+ if (currentBoard === destinationBoard) {
+ return false;
+ }
+ return canMoveFromSourceBoard(currentBoard, getState);
+ }
return false;
},
handler: ({ sourceData, targetData, dispatch }) => {
@@ -528,9 +609,31 @@ export const addImageToBoardDndTarget: DndTarget<
}
if (multipleImageDndSource.typeGuard(sourceData)) {
- const { image_names } = sourceData.payload;
+ const { image_names, video_names } = sourceData.payload;
const { boardId } = targetData.payload;
- addImagesToBoard({ image_names, boardId, dispatch });
+ if (image_names.length > 0) {
+ addImagesToBoard({ image_names, boardId, dispatch });
+ }
+ if (video_names.length > 0) {
+ addVideosToBoard({ video_names, boardId, dispatch });
+ }
+ }
+
+ if (singleVideoDndSource.typeGuard(sourceData)) {
+ const { videoDTO } = sourceData.payload;
+ const { boardId } = targetData.payload;
+ addVideoToBoard({ video_name: videoDTO.video_name, boardId, dispatch });
+ }
+
+ if (multipleVideoDndSource.typeGuard(sourceData)) {
+ const { video_names, image_names } = sourceData.payload;
+ const { boardId } = targetData.payload;
+ if (video_names.length > 0) {
+ addVideosToBoard({ video_names, boardId, dispatch });
+ }
+ if (image_names.length > 0) {
+ addImagesToBoard({ image_names, boardId, dispatch });
+ }
}
},
};
@@ -546,7 +649,7 @@ export type RemoveImageFromBoardDndTargetData = DndData<
>;
export const removeImageFromBoardDndTarget: DndTarget<
RemoveImageFromBoardDndTargetData,
- SingleImageDndSourceData | MultipleImageDndSourceData
+ SingleImageDndSourceData | MultipleImageDndSourceData | SingleVideoDndSourceData | MultipleVideoDndSourceData
> = {
..._removeFromBoard,
typeGuard: buildTypeGuard(_removeFromBoard.key),
@@ -569,6 +672,22 @@ export const removeImageFromBoardDndTarget: DndTarget<
return canMoveFromSourceBoard(currentBoard, getState);
}
+ if (singleVideoDndSource.typeGuard(sourceData)) {
+ const currentBoard = sourceData.payload.videoDTO.board_id ?? 'none';
+ if (currentBoard === 'none') {
+ return false;
+ }
+ return canMoveFromSourceBoard(currentBoard, getState);
+ }
+
+ if (multipleVideoDndSource.typeGuard(sourceData)) {
+ const currentBoard = sourceData.payload.board_id;
+ if (currentBoard === 'none') {
+ return false;
+ }
+ return canMoveFromSourceBoard(currentBoard, getState);
+ }
+
return false;
},
handler: ({ sourceData, dispatch }) => {
@@ -578,8 +697,28 @@ export const removeImageFromBoardDndTarget: DndTarget<
}
if (multipleImageDndSource.typeGuard(sourceData)) {
- const { image_names } = sourceData.payload;
- removeImagesFromBoard({ image_names, dispatch });
+ const { image_names, video_names } = sourceData.payload;
+ if (image_names.length > 0) {
+ removeImagesFromBoard({ image_names, dispatch });
+ }
+ if (video_names.length > 0) {
+ removeVideosFromBoard({ video_names, dispatch });
+ }
+ }
+
+ if (singleVideoDndSource.typeGuard(sourceData)) {
+ const { videoDTO } = sourceData.payload;
+ removeVideoFromBoard({ video_name: videoDTO.video_name, dispatch });
+ }
+
+ if (multipleVideoDndSource.typeGuard(sourceData)) {
+ const { video_names, image_names } = sourceData.payload;
+ if (video_names.length > 0) {
+ removeVideosFromBoard({ video_names, dispatch });
+ }
+ if (image_names.length > 0) {
+ removeImagesFromBoard({ image_names, dispatch });
+ }
}
},
};
@@ -592,6 +731,7 @@ export const dndTargets = [
setRegionalGuidanceReferenceImageDndTarget,
setUpscaleInitialImageDndTarget,
setNodeImageFieldImageDndTarget,
+ setNodeVideoFieldVideoDndTarget,
addImagesToNodeImageFieldCollectionDndTarget,
setComparisonImageDndTarget,
newCanvasEntityFromImageDndTarget,
diff --git a/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.test.ts b/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.test.ts
new file mode 100644
index 00000000000..ec6723a94ba
--- /dev/null
+++ b/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.test.ts
@@ -0,0 +1,34 @@
+/**
+ * Regression tests for the fullscreen drag-drop / paste validator (PR #9163 review).
+ *
+ * The bug: the validator required BOTH an accepted MIME type AND an accepted extension, so a
+ * valid `clip.mp4` whose browser supplied an empty or generic `File.type` was rejected before
+ * upload even though the backend accepts MP4 by extension alone.
+ */
+import { describe, expect, it } from 'vitest';
+
+import { zUploadFile } from './fullscreenDropzoneAccept';
+
+// The validator only reads `type` and `name`, so a plain object stands in for a DOM File.
+const fakeFile = (name: string, type: string): File => ({ name, type }) as File;
+
+describe('zUploadFile', () => {
+ it('accepts an MP4 with an empty MIME type by its extension', () => {
+ expect(zUploadFile.safeParse(fakeFile('clip.mp4', '')).success).toBe(true);
+ });
+
+ it('accepts supported media by MIME type alone', () => {
+ expect(zUploadFile.safeParse(fakeFile('pasted-blob', 'image/png')).success).toBe(true);
+ expect(zUploadFile.safeParse(fakeFile('pasted-blob', 'video/mp4')).success).toBe(true);
+ });
+
+ it('rejects video containers the backend does not accept', () => {
+ expect(zUploadFile.safeParse(fakeFile('clip.webm', 'video/webm')).success).toBe(false);
+ expect(zUploadFile.safeParse(fakeFile('clip.mov', 'video/quicktime')).success).toBe(false);
+ expect(zUploadFile.safeParse(fakeFile('clip.mkv', 'video/x-matroska')).success).toBe(false);
+ });
+
+ it('rejects non-media files', () => {
+ expect(zUploadFile.safeParse(fakeFile('notes.txt', 'text/plain')).success).toBe(false);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.ts b/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.ts
new file mode 100644
index 00000000000..0587b9dd1a0
--- /dev/null
+++ b/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.ts
@@ -0,0 +1,12 @@
+import { isAcceptedUploadFile } from 'common/util/uploadMediaAccept';
+import { z } from 'zod';
+
+/**
+ * Validates files entering via the fullscreen drag-drop / paste path.
+ *
+ * MIME type and filename extension each suffice on their own (`isAcceptedUploadFile`):
+ * browsers sometimes supply an empty or generic `File.type` — e.g. a `clip.mp4` dragged
+ * from some file managers — and requiring both signals used to reject files the backend
+ * upload routes would happily accept.
+ */
+export const zUploadFile = z.custom().refine(isAcceptedUploadFile, { message: 'File type is not supported' });
diff --git a/invokeai/frontend/web/src/features/dnd/useDndMonitor.ts b/invokeai/frontend/web/src/features/dnd/useDndMonitor.ts
index 24d6bea1680..94a875bfe35 100644
--- a/invokeai/frontend/web/src/features/dnd/useDndMonitor.ts
+++ b/invokeai/frontend/web/src/features/dnd/useDndMonitor.ts
@@ -4,7 +4,13 @@ import { logger } from 'app/logging/logger';
import { getStore } from 'app/store/nanostores/store';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { parseify } from 'common/util/serialize';
-import { dndTargets, multipleImageDndSource, singleImageDndSource } from 'features/dnd/dnd';
+import {
+ dndTargets,
+ multipleImageDndSource,
+ multipleVideoDndSource,
+ singleImageDndSource,
+ singleVideoDndSource,
+} from 'features/dnd/dnd';
import { useEffect } from 'react';
const log = logger('dnd');
@@ -18,8 +24,15 @@ export const useDndMonitor = () => {
canMonitor: ({ source }) => {
const sourceData = source.data;
- // Check for allowed sources
- if (!singleImageDndSource.typeGuard(sourceData) && !multipleImageDndSource.typeGuard(sourceData)) {
+ // Check for allowed sources. Without multipleVideoDndSource here, multi-video
+ // drags would be ignored at the global monitor level — the drop would silently
+ // do nothing because the handler never fires.
+ if (
+ !singleImageDndSource.typeGuard(sourceData) &&
+ !multipleImageDndSource.typeGuard(sourceData) &&
+ !singleVideoDndSource.typeGuard(sourceData) &&
+ !multipleVideoDndSource.typeGuard(sourceData)
+ ) {
return false;
}
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx
index 8877b22612f..b4118d08934 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx
@@ -2,12 +2,14 @@ import { Flex, Image, Text } from '@invoke-ai/ui-library';
import { skipToken } from '@reduxjs/toolkit/query';
import { useTranslation } from 'react-i18next';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
+import { useGetVideoDTOQuery } from 'services/api/endpoints/videos';
import type { BoardDTO } from 'services/api/types';
type Props = {
board: BoardDTO | null;
boardCounts: {
image_count: number;
+ video_count: number;
asset_count: number;
};
};
@@ -15,13 +17,18 @@ type Props = {
export const BoardTooltip = ({ board, boardCounts }: Props) => {
const { t } = useTranslation();
- const { currentData: coverImage } = useGetImageDTOQuery(board?.cover_image_name ?? skipToken);
+ // Backend picks a single cover — either an image or a video. Prefer the video when set.
+ const { currentData: coverVideo } = useGetVideoDTOQuery(board?.cover_video_name ?? skipToken);
+ const { currentData: coverImage } = useGetImageDTOQuery(
+ board?.cover_video_name ? skipToken : (board?.cover_image_name ?? skipToken)
+ );
+ const thumbnailUrl = coverVideo?.thumbnail_url ?? coverImage?.thumbnail_url;
return (
- {coverImage && (
+ {thumbnailUrl && (
{
{board && {board.board_name} }
{t('boards.imagesWithCount', { count: boardCounts.image_count })},{' '}
+ {t('boards.videosWithCount', { count: boardCounts.video_count })},{' '}
{t('boards.assetsWithCount', { count: boardCounts.asset_count })}
{board?.archived && ({t('boards.archived')}) }
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx
index 10fbe618322..22fd7bd8561 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx
@@ -20,6 +20,7 @@ import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArchiveBold, PiGlobeBold, PiImageSquare, PiShareNetworkBold } from 'react-icons/pi';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
+import { useGetVideoDTOQuery } from 'services/api/endpoints/videos';
import { useBoardAccess } from 'services/api/hooks/useBoardAccess';
import type { BoardDTO } from 'services/api/types';
@@ -53,9 +54,13 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
[board.board_id]
);
+ // The tooltip gets split counts so videos aren't mislabeled as images; the headline
+ // number below stays combined (images + videos) so a video-only board doesn't read
+ // as empty in the gallery list.
const boardCounts = useMemo(
() => ({
image_count: board.image_count,
+ video_count: board.video_count ?? 0,
asset_count: board.asset_count,
}),
[board]
@@ -118,7 +123,7 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
)}
- {board.image_count} | {board.asset_count}
+ {board.image_count + (board.video_count ?? 0)} | {board.asset_count}
@@ -138,12 +143,19 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
export default memo(GalleryBoard);
const CoverImage = ({ board }: { board: BoardDTO }) => {
- const { currentData: coverImage } = useGetImageDTOQuery(board.cover_image_name ?? skipToken);
+ // Backend picks a single cover — either an image or a video — so at most one of these
+ // queries fires (the other is `skipToken`). The video case takes precedence when set.
+ const { currentData: coverVideo } = useGetVideoDTOQuery(board.cover_video_name ?? skipToken);
+ const { currentData: coverImage } = useGetImageDTOQuery(
+ board.cover_video_name ? skipToken : (board.cover_image_name ?? skipToken)
+ );
+
+ const thumbnailUrl = coverVideo?.thumbnail_url ?? coverImage?.thumbnail_url;
- if (coverImage) {
+ if (thumbnailUrl) {
return (
{
return { imagesTotal: data?.total ?? 0 };
},
});
+ const { videosTotal } = useGetBoardVideosTotalQuery('none', {
+ selectFromResult: ({ data }) => {
+ return { videosTotal: data?.total ?? 0 };
+ },
+ });
const { assetsTotal } = useGetBoardAssetsTotalQuery('none', {
selectFromResult: ({ data }) => {
return { assetsTotal: data?.total ?? 0 };
},
});
+ // Videos share the "images" headline count so a board with only videos doesn't read as empty.
+ const imagesAndVideosTotal = imagesTotal + videosTotal;
const autoAddBoardId = useAppSelector(selectAutoAddBoardId);
const autoAssignBoardOnClick = useAppSelector(selectAutoAssignBoardOnClick);
const boardSearchText = useAppSelector(selectBoardSearchText);
@@ -62,7 +73,12 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
{(ref) => (
}
+ label={
+
+ }
openDelay={1000}
placement="right"
closeOnScroll
@@ -103,7 +119,7 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
{autoAddBoardId === 'none' && }
- {imagesTotal} | {assetsTotal}
+ {imagesAndVideosTotal} | {assetsTotal}
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx
index d85c90f7dc1..46c3b33e6ae 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx
@@ -5,8 +5,10 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import { boardIdSelected } from 'features/gallery/store/gallerySlice';
import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
import { PiCalendarBold, PiImageSquare } from 'react-icons/pi';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
+import { useGetVideoDTOQuery } from 'services/api/endpoints/videos';
import type { VirtualSubBoard } from 'services/api/endpoints/virtual_boards';
const _hover: SystemStyleObject = {
@@ -19,6 +21,7 @@ interface VirtualBoardItemProps {
const VirtualBoardItem = ({ board }: VirtualBoardItemProps) => {
const dispatch = useAppDispatch();
+ const { t } = useTranslation();
const selectedBoardId = useAppSelector(selectSelectedBoardId);
const isSelected = selectedBoardId === board.virtual_board_id;
@@ -28,15 +31,14 @@ const VirtualBoardItem = ({ board }: VirtualBoardItemProps) => {
}
}, [selectedBoardId, board.virtual_board_id, dispatch]);
+ const tooltip = `${board.date} — ${t('boards.imagesWithCount', { count: board.image_count })}, ${t(
+ 'boards.videosWithCount',
+ { count: board.video_count }
+ )}, ${t('boards.assetsWithCount', { count: board.asset_count })}`;
+
return (
-
+
{
w="full"
h="full"
>
-
+
{board.board_name}
@@ -60,7 +62,7 @@ const VirtualBoardItem = ({ board }: VirtualBoardItemProps) => {
- {board.image_count} | {board.asset_count}
+ {board.image_count + board.video_count} | {board.asset_count}
@@ -71,13 +73,24 @@ const VirtualBoardItem = ({ board }: VirtualBoardItemProps) => {
export default memo(VirtualBoardItem);
-const CoverImage = ({ coverImageName }: { coverImageName: string | null }) => {
- const { currentData: coverImage } = useGetImageDTOQuery(coverImageName ?? skipToken);
+const CoverImage = ({
+ coverImageName,
+ coverVideoName,
+}: {
+ coverImageName: string | null;
+ coverVideoName: string | null;
+}) => {
+ // Backend picks a single cover — the newest item of the date, image or video — so at most
+ // one of these queries fires (the other is `skipToken`).
+ const { currentData: coverVideo } = useGetVideoDTOQuery(coverVideoName ?? skipToken);
+ const { currentData: coverImage } = useGetImageDTOQuery(coverVideoName ? skipToken : (coverImageName ?? skipToken));
+
+ const thumbnailUrl = coverVideo?.thumbnail_url ?? coverImage?.thumbnail_url;
- if (coverImage) {
+ if (thumbnailUrl) {
return (
{
+ const { t } = useTranslation();
const dispatch = useAppDispatch();
const showVirtualBoards = useAppSelector(selectShowVirtualBoards);
const isOpen = useAppSelector(selectVirtualBoardsSectionOpen);
@@ -37,13 +39,13 @@ export const VirtualBoardSection = memo(() => {
- By Date
+ {t('boards.byDate')}
: }
onClick={toggleOpen}
/>
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx
index b7d99301e7a..069d9df29d4 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx
@@ -151,8 +151,11 @@ const DeleteBoardModal = () => {
bottomMessage={t('boards.bottomMessage')}
/>
)}
- {boardToDelete !== 'none' && {t('boards.deletedBoardsCannotbeRestored')} }
- {t('gallery.deleteImagePermanent')}
+ {boardToDelete !== 'none' ? (
+ {t('boards.deletedBoardsCannotbeRestored')}
+ ) : (
+ {t('gallery.deleteImagePermanent')}
+ )}
@@ -167,7 +170,7 @@ const DeleteBoardModal = () => {
)}
{boardToDelete !== 'none' && (
- {t('boards.deleteBoardAndImages')}
+ {t('boards.deleteBoardAndAssets')}
)}
{boardToDelete === 'none' && (
diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo.tsx b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo.tsx
new file mode 100644
index 00000000000..b7298a87431
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo.tsx
@@ -0,0 +1,26 @@
+import { MenuItem } from '@invoke-ai/ui-library';
+import { useAppDispatch } from 'app/store/storeHooks';
+import { isModalOpenChanged, videosToChangeSelected } from 'features/changeBoardModal/store/slice';
+import { useVideoDTOContext } from 'features/gallery/contexts/VideoDTOContext';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiFoldersBold } from 'react-icons/pi';
+
+export const ContextMenuItemChangeBoardVideo = memo(() => {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const videoDTO = useVideoDTOContext();
+
+ const onClick = useCallback(() => {
+ dispatch(videosToChangeSelected([videoDTO.video_name]));
+ dispatch(isModalOpenChanged(true));
+ }, [dispatch, videoDTO.video_name]);
+
+ return (
+ } onClickCapture={onClick}>
+ {t('boards.changeBoard')}
+
+ );
+});
+
+ContextMenuItemChangeBoardVideo.displayName = 'ContextMenuItemChangeBoardVideo';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo.tsx b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo.tsx
new file mode 100644
index 00000000000..15339e07473
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo.tsx
@@ -0,0 +1,44 @@
+import { MenuItem } from '@invoke-ai/ui-library';
+import { useAppSelector } from 'app/store/storeHooks';
+import { useDeleteVideoModalApi } from 'features/deleteVideoModal/store/state';
+import { useVideoDTOContext } from 'features/gallery/contexts/VideoDTOContext';
+import { selectSelection } from 'features/gallery/store/gallerySelectors';
+import { isVideoName } from 'features/gallery/store/types';
+import { memo, useCallback, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiTrashSimpleBold } from 'react-icons/pi';
+
+export const ContextMenuItemDeleteVideo = memo(() => {
+ const { t } = useTranslation();
+ const videoDTO = useVideoDTOContext();
+ const deleteVideoModal = useDeleteVideoModalApi();
+ const selection = useAppSelector(selectSelection);
+
+ // When the right-clicked video is part of an active multi-selection, delete every
+ // selected video in one shot. Image names mixed into the selection are skipped —
+ // right-clicking an image surfaces ImageContextMenu, which owns that flow.
+ const targetVideoNames = useMemo(() => {
+ if (selection.length > 1 && selection.includes(videoDTO.video_name)) {
+ return selection.filter(isVideoName);
+ }
+ return [videoDTO.video_name];
+ }, [selection, videoDTO.video_name]);
+
+ const label = t('gallery.deleteVideo', { count: targetVideoNames.length });
+
+ const onClick = useCallback(async () => {
+ try {
+ await deleteVideoModal.delete(targetVideoNames);
+ } catch {
+ // noop — user canceled the confirm dialog.
+ }
+ }, [deleteVideoModal, targetVideoNames]);
+
+ return (
+ } onClickCapture={onClick}>
+ {label}
+
+ );
+});
+
+ContextMenuItemDeleteVideo.displayName = 'ContextMenuItemDeleteVideo';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo.tsx b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo.tsx
new file mode 100644
index 00000000000..5a667b3e96f
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo.tsx
@@ -0,0 +1,24 @@
+import { MenuItem } from '@invoke-ai/ui-library';
+import { useDownloadItem } from 'common/hooks/useDownloadImage';
+import { useVideoDTOContext } from 'features/gallery/contexts/VideoDTOContext';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiDownloadSimpleBold } from 'react-icons/pi';
+
+export const ContextMenuItemDownloadVideo = memo(() => {
+ const { t } = useTranslation();
+ const videoDTO = useVideoDTOContext();
+ const { downloadItem } = useDownloadItem();
+
+ const onClick = useCallback(() => {
+ downloadItem(videoDTO.video_url, videoDTO.video_name);
+ }, [downloadItem, videoDTO]);
+
+ return (
+ } onClickCapture={onClick}>
+ {t('gallery.download')}
+
+ );
+});
+
+ContextMenuItemDownloadVideo.displayName = 'ContextMenuItemDownloadVideo';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo.tsx b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo.tsx
new file mode 100644
index 00000000000..aae84d0e3f0
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo.tsx
@@ -0,0 +1,21 @@
+import { MenuItem } from '@invoke-ai/ui-library';
+import { useVideoDTOContext } from 'features/gallery/contexts/VideoDTOContext';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiArrowSquareOutBold } from 'react-icons/pi';
+
+export const ContextMenuItemOpenInNewTabVideo = memo(() => {
+ const { t } = useTranslation();
+ const videoDTO = useVideoDTOContext();
+ const onClick = useCallback(() => {
+ window.open(videoDTO.video_url, '_blank', 'noopener,noreferrer');
+ }, [videoDTO.video_url]);
+
+ return (
+ } onClickCapture={onClick}>
+ {t('common.openInNewTab')}
+
+ );
+});
+
+ContextMenuItemOpenInNewTabVideo.displayName = 'ContextMenuItemOpenInNewTabVideo';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx
index ee3c8e4e985..a2e7392c564 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx
@@ -2,7 +2,9 @@ import { MenuDivider, MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
import { useDeleteImageModalApi } from 'features/deleteImageModal/store/state';
-import { memo, useCallback } from 'react';
+import { selectSelection } from 'features/gallery/store/gallerySelectors';
+import { isVideoName } from 'features/gallery/store/types';
+import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiDownloadSimpleBold, PiFoldersBold, PiStarBold, PiStarFill, PiTrashSimpleBold } from 'react-icons/pi';
import {
@@ -16,7 +18,7 @@ import { useSelectedBoard } from 'services/api/hooks/useSelectedBoard';
const MultipleSelectionMenuItems = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
- const selection = useAppSelector((s) => s.gallery.selection);
+ const selection = useAppSelector(selectSelection);
const deleteImageModal = useDeleteImageModalApi();
const selectedBoard = useSelectedBoard();
const { canWriteImages } = useBoardAccess(selectedBoard);
@@ -25,49 +27,56 @@ const MultipleSelectionMenuItems = () => {
const [unstarImages] = useUnstarImagesMutation();
const [bulkDownload] = useBulkDownloadImagesMutation();
+ // The gallery selection can contain mixed image+video names. Each menu only acts on its
+ // own kind so the action is unambiguous; right-clicking a video surfaces the video
+ // equivalent of this menu.
+ const imageNames = useMemo(() => selection.filter((name) => !isVideoName(name)), [selection]);
+ const count = imageNames.length;
+ const hasImages = count > 0;
+
const handleChangeBoard = useCallback(() => {
- dispatch(imagesToChangeSelected(selection));
+ dispatch(imagesToChangeSelected(imageNames));
dispatch(isModalOpenChanged(true));
- }, [dispatch, selection]);
+ }, [dispatch, imageNames]);
const handleDeleteSelection = useCallback(() => {
- deleteImageModal.delete(selection);
- }, [deleteImageModal, selection]);
+ deleteImageModal.delete(imageNames);
+ }, [deleteImageModal, imageNames]);
const handleStarSelection = useCallback(() => {
- starImages({ image_names: selection });
- }, [starImages, selection]);
+ starImages({ image_names: imageNames });
+ }, [starImages, imageNames]);
const handleUnstarSelection = useCallback(() => {
- unstarImages({ image_names: selection });
- }, [unstarImages, selection]);
+ unstarImages({ image_names: imageNames });
+ }, [unstarImages, imageNames]);
const handleBulkDownload = useCallback(() => {
- bulkDownload({ image_names: selection });
- }, [selection, bulkDownload]);
+ bulkDownload({ image_names: imageNames });
+ }, [imageNames, bulkDownload]);
return (
<>
- } onClickCapture={handleUnstarSelection}>
- Unstar All
+ } onClickCapture={handleUnstarSelection} isDisabled={!hasImages}>
+ {t('gallery.unstarImage', { count })}
- } onClickCapture={handleStarSelection}>
- Star All
+ } onClickCapture={handleStarSelection} isDisabled={!hasImages}>
+ {t('gallery.starImage', { count })}
- } onClickCapture={handleBulkDownload}>
- {t('gallery.downloadSelection')}
+ } onClickCapture={handleBulkDownload} isDisabled={!hasImages}>
+ {t('gallery.downloadImage', { count })}
- } onClickCapture={handleChangeBoard} isDisabled={!canWriteImages}>
- {t('boards.changeBoard')}
+ } onClickCapture={handleChangeBoard} isDisabled={!hasImages || !canWriteImages}>
+ {t('boards.changeBoardImage', { count })}
}
onClickCapture={handleDeleteSelection}
- isDisabled={!canWriteImages}
+ isDisabled={!hasImages || !canWriteImages}
>
- {t('gallery.deleteSelection')}
+ {t('gallery.deleteImage', { count })}
>
);
diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos.tsx b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos.tsx
new file mode 100644
index 00000000000..45018b72f89
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos.tsx
@@ -0,0 +1,97 @@
+import { MenuDivider, MenuItem } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { useDownloadItem } from 'common/hooks/useDownloadImage';
+import { isModalOpenChanged, videosToChangeSelected } from 'features/changeBoardModal/store/slice';
+import { useDeleteVideoModalApi } from 'features/deleteVideoModal/store/state';
+import { selectSelection } from 'features/gallery/store/gallerySelectors';
+import { isVideoName } from 'features/gallery/store/types';
+import { memo, useCallback, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiDownloadSimpleBold, PiFoldersBold, PiStarBold, PiStarFill, PiTrashSimpleBold } from 'react-icons/pi';
+import { getVideoDTOSafe, useStarVideosMutation, useUnstarVideosMutation } from 'services/api/endpoints/videos';
+import { useBoardAccess } from 'services/api/hooks/useBoardAccess';
+import { useSelectedBoard } from 'services/api/hooks/useSelectedBoard';
+
+/**
+ * Multi-selection menu surfaced by `VideoContextMenu` when the gallery selection has more
+ * than one item. Mirrors `MultipleSelectionMenuItems` (the image equivalent) feature-for-feature
+ * where the video API supports it. Filters the polymorphic gallery selection down to videos —
+ * mixed selections that also contain images are handled by the image-side menu when the user
+ * right-clicks an image.
+ */
+const MultipleSelectionMenuItemsVideos = () => {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const selection = useAppSelector(selectSelection);
+ const deleteVideoModal = useDeleteVideoModalApi();
+ const selectedBoard = useSelectedBoard();
+ // Boards use one write permission for both kinds — videos inherit from `canWriteImages`.
+ const { canWriteImages } = useBoardAccess(selectedBoard);
+
+ const [starVideos] = useStarVideosMutation();
+ const [unstarVideos] = useUnstarVideosMutation();
+ const { downloadItem } = useDownloadItem();
+
+ const videoNames = useMemo(() => selection.filter(isVideoName), [selection]);
+ const count = videoNames.length;
+ const hasVideos = count > 0;
+
+ const handleChangeBoard = useCallback(() => {
+ dispatch(videosToChangeSelected(videoNames));
+ dispatch(isModalOpenChanged(true));
+ }, [dispatch, videoNames]);
+
+ const handleDeleteSelection = useCallback(() => {
+ deleteVideoModal.delete(videoNames).catch(() => {
+ // user cancelled the confirmation dialog
+ });
+ }, [deleteVideoModal, videoNames]);
+
+ const handleStarSelection = useCallback(() => {
+ starVideos({ video_names: videoNames });
+ }, [starVideos, videoNames]);
+
+ const handleUnstarSelection = useCallback(() => {
+ unstarVideos({ video_names: videoNames });
+ }, [unstarVideos, videoNames]);
+
+ const handleBulkDownload = useCallback(async () => {
+ // No zip-bundle endpoint exists for videos, so we loop the per-video download helper.
+ // Modern browsers prompt once for "allow multiple file downloads", then proceed silently.
+ for (const video_name of videoNames) {
+ const dto = await getVideoDTOSafe(video_name);
+ if (!dto) {
+ continue;
+ }
+ await downloadItem(dto.video_url, dto.video_name);
+ }
+ }, [downloadItem, videoNames]);
+
+ return (
+ <>
+ } onClickCapture={handleUnstarSelection} isDisabled={!hasVideos}>
+ {t('gallery.unstarVideo', { count })}
+
+ } onClickCapture={handleStarSelection} isDisabled={!hasVideos}>
+ {t('gallery.starVideo', { count })}
+
+ } onClickCapture={handleBulkDownload} isDisabled={!hasVideos}>
+ {t('gallery.downloadVideo', { count })}
+
+ } onClickCapture={handleChangeBoard} isDisabled={!hasVideos || !canWriteImages}>
+ {t('boards.changeBoardVideo', { count })}
+
+
+ }
+ onClickCapture={handleDeleteSelection}
+ isDisabled={!hasVideos || !canWriteImages}
+ >
+ {t('gallery.deleteVideo', { count })}
+
+ >
+ );
+};
+
+export default memo(MultipleSelectionMenuItemsVideos);
diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/VideoContextMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/VideoContextMenu.tsx
new file mode 100644
index 00000000000..f50e883e9cf
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ContextMenu/VideoContextMenu.tsx
@@ -0,0 +1,238 @@
+import type { ChakraProps } from '@invoke-ai/ui-library';
+import { Menu, MenuButton, MenuDivider, MenuList, Portal, useGlobalMenuClose } from '@invoke-ai/ui-library';
+import { useStore } from '@nanostores/react';
+import { useAppSelector } from 'app/store/storeHooks';
+import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
+import { ContextMenuItemChangeBoardVideo } from 'features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo';
+import { ContextMenuItemDeleteVideo } from 'features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo';
+import { ContextMenuItemDownloadVideo } from 'features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo';
+import { ContextMenuItemOpenInNewTabVideo } from 'features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo';
+import MultipleSelectionMenuItemsVideos from 'features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos';
+import { VideoDTOContextProvider } from 'features/gallery/contexts/VideoDTOContext';
+import { selectSelectionCount } from 'features/gallery/store/gallerySelectors';
+import { map } from 'nanostores';
+import type { RefObject } from 'react';
+import { memo, useCallback, useEffect, useRef } from 'react';
+import type { VideoDTO } from 'services/api/types';
+
+// Mirror of ImageContextMenu, but pared down to the three actions the video item supports today:
+// delete, change-board, download. Long-press on touch devices opens the menu the same way.
+
+const LONGPRESS_DELAY_MS = 500;
+const LONGPRESS_MOVE_THRESHOLD_PX = 10;
+
+const $videoContextMenuState = map<{
+ isOpen: boolean;
+ videoDTO: VideoDTO | null;
+ position: { x: number; y: number };
+}>({
+ isOpen: false,
+ videoDTO: null,
+ position: { x: -1, y: -1 },
+});
+
+const onClose = () => {
+ $videoContextMenuState.setKey('isOpen', false);
+};
+
+const elToVideoMap = new Map();
+
+const getVideoDTOFromMap = (target: Node): VideoDTO | undefined => {
+ const entry = Array.from(elToVideoMap.entries()).find((entry) => entry[0].contains(target));
+ return entry?.[1];
+};
+
+/**
+ * Register a context menu for a video DTO on a target element. Mirrors useImageContextMenu.
+ */
+export const useVideoContextMenu = (videoDTO: VideoDTO, ref: RefObject | (HTMLElement | null)) => {
+ useEffect(() => {
+ if (ref === null) {
+ return;
+ }
+ const el = ref instanceof HTMLElement ? ref : ref.current;
+ if (!el) {
+ return;
+ }
+ elToVideoMap.set(el, videoDTO);
+ return () => {
+ elToVideoMap.delete(el);
+ };
+ }, [videoDTO, ref]);
+};
+
+const _hover: ChakraProps['_hover'] = { bg: 'transparent' };
+
+export const VideoContextMenu = memo(() => {
+ useAssertSingleton('VideoContextMenu');
+ const state = useStore($videoContextMenuState);
+ useGlobalMenuClose(onClose);
+
+ return (
+
+
+
+
+
+
+
+ );
+});
+
+VideoContextMenu.displayName = 'VideoContextMenu';
+
+const MenuContent = memo(() => {
+ const state = useStore($videoContextMenuState);
+ const selectionCount = useAppSelector(selectSelectionCount);
+ if (!state.videoDTO) {
+ return null;
+ }
+ if (selectionCount > 1) {
+ return (
+
+
+
+ );
+ }
+ return (
+
+
+
+
+
+
+
+
+
+ );
+});
+
+MenuContent.displayName = 'VideoContextMenuContent';
+
+/**
+ * Logical component that listens for context-menu events and dispatches to the singleton's state.
+ * Split out from the visible menu to keep re-renders cheap.
+ */
+const VideoContextMenuEventLogical = memo(() => {
+ const lastPositionRef = useRef<{ x: number; y: number }>({ x: -1, y: -1 });
+ const longPressTimeoutRef = useRef(0);
+ const animationTimeoutRef = useRef(0);
+
+ const onContextMenu = useCallback((e: MouseEvent | PointerEvent) => {
+ if (e.shiftKey) {
+ // shift+right-click opens the native context menu
+ onClose();
+ return;
+ }
+
+ const videoDTO = getVideoDTOFromMap(e.target as Node);
+ if (!videoDTO) {
+ // Not over a registered video item — let ImageContextMenu handle it (or close).
+ onClose();
+ return;
+ }
+
+ window.clearTimeout(animationTimeoutRef.current);
+ e.preventDefault();
+
+ if (lastPositionRef.current.x !== e.pageX || lastPositionRef.current.y !== e.pageY) {
+ if ($videoContextMenuState.get().isOpen) {
+ onClose();
+ }
+ animationTimeoutRef.current = window.setTimeout(() => {
+ $videoContextMenuState.set({
+ isOpen: true,
+ position: { x: e.pageX, y: e.pageY },
+ videoDTO,
+ });
+ }, 100);
+ } else {
+ $videoContextMenuState.set({
+ isOpen: true,
+ position: { x: e.pageX, y: e.pageY },
+ videoDTO,
+ });
+ }
+
+ lastPositionRef.current = { x: e.pageX, y: e.pageY };
+ }, []);
+
+ const onPointerDown = useCallback(
+ (e: PointerEvent) => {
+ if (e.pointerType === 'mouse') {
+ return;
+ }
+ longPressTimeoutRef.current = window.setTimeout(() => {
+ onContextMenu(e);
+ }, LONGPRESS_DELAY_MS);
+ lastPositionRef.current = { x: e.pageX, y: e.pageY };
+ },
+ [onContextMenu]
+ );
+
+ const onPointerMove = useCallback((e: PointerEvent) => {
+ if (e.pointerType === 'mouse') {
+ return;
+ }
+ if (longPressTimeoutRef.current === null) {
+ return;
+ }
+ const distance = Math.hypot(e.pageX - lastPositionRef.current.x, e.pageY - lastPositionRef.current.y);
+ if (distance > LONGPRESS_MOVE_THRESHOLD_PX) {
+ clearTimeout(longPressTimeoutRef.current);
+ }
+ }, []);
+
+ const onPointerUp = useCallback((e: PointerEvent) => {
+ if (e.pointerType === 'mouse') {
+ return;
+ }
+ if (longPressTimeoutRef.current) {
+ clearTimeout(longPressTimeoutRef.current);
+ }
+ }, []);
+
+ const onPointerCancel = useCallback((e: PointerEvent) => {
+ if (e.pointerType === 'mouse') {
+ return;
+ }
+ if (longPressTimeoutRef.current) {
+ clearTimeout(longPressTimeoutRef.current);
+ }
+ }, []);
+
+ useEffect(() => {
+ const controller = new AbortController();
+ window.addEventListener('contextmenu', onContextMenu, { signal: controller.signal });
+ window.addEventListener('pointerdown', onPointerDown, { signal: controller.signal });
+ window.addEventListener('pointerup', onPointerUp, { signal: controller.signal });
+ window.addEventListener('pointercancel', onPointerCancel, { signal: controller.signal });
+ window.addEventListener('pointermove', onPointerMove, { signal: controller.signal });
+ return () => {
+ controller.abort();
+ };
+ }, [onContextMenu, onPointerCancel, onPointerDown, onPointerMove, onPointerUp]);
+
+ useEffect(
+ () => () => {
+ window.clearTimeout(animationTimeoutRef.current);
+ window.clearTimeout(longPressTimeoutRef.current);
+ },
+ []
+ );
+
+ return null;
+});
+
+VideoContextMenuEventLogical.displayName = 'VideoContextMenuEventLogical';
diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryImageGrid.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryImageGrid.tsx
index 43e7d2aca92..b62a8b01c8d 100644
--- a/invokeai/frontend/web/src/features/gallery/components/GalleryImageGrid.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/GalleryImageGrid.tsx
@@ -1,8 +1,8 @@
-import { Box, Flex, forwardRef, Grid, GridItem, Spinner, Text } from '@invoke-ai/ui-library';
+import { Box, Flex, forwardRef, Grid, GridItem, IconButton, Spinner, Text } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { getFocusedRegion, useIsRegionFocused } from 'common/hooks/focus';
-import { useRangeBasedImageFetching } from 'features/gallery/hooks/useRangeBasedImageFetching';
+import { getVideoPrefetchOptions, useRangeBasedImageFetching } from 'features/gallery/hooks/useRangeBasedImageFetching';
import type { selectGetImageNamesQueryArgs } from 'features/gallery/store/gallerySelectors';
import {
selectGalleryImageMinimumWidth,
@@ -12,12 +12,14 @@ import {
selectSelectionCount,
} from 'features/gallery/store/gallerySelectors';
import { imageToCompareChanged, selectionChanged } from 'features/gallery/store/gallerySlice';
+import { isVideoName } from 'features/gallery/store/types';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { VIEWER_PANEL_ID } from 'features/ui/layouts/shared';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
+import { PiArrowCounterClockwiseBold } from 'react-icons/pi';
import type {
GridComponents,
GridComputeItemKey,
@@ -28,12 +30,14 @@ import type {
} from 'react-virtuoso';
import { VirtuosoGrid } from 'react-virtuoso';
import { imagesApi, useImageDTO, useStarImagesMutation, useUnstarImagesMutation } from 'services/api/endpoints/images';
+import { useStarVideosMutation, useUnstarVideosMutation, useVideoDTO, videosApi } from 'services/api/endpoints/videos';
import { useDebounce } from 'use-debounce';
import { getItemIndex } from './getItemIndex';
import { getItemsPerRow } from './getItemsPerRow';
import { GalleryImage, GalleryImagePlaceholder } from './ImageGrid/GalleryImage';
import { GallerySelectionCountTag } from './ImageGrid/GallerySelectionCountTag';
+import { GalleryVideoItem } from './ImageGrid/GalleryVideoItem';
import { scrollIntoView } from './scrollIntoView';
import { useGalleryImageNames } from './use-gallery-image-names';
import { useScrollableGallery } from './useScrollableGallery';
@@ -46,35 +50,60 @@ type GridContext = {
};
/**
- * Wraps an image - either the placeholder as it is being loaded or the loaded image
+ * Wraps a gallery item — either the placeholder as it is being loaded, or the loaded image / video.
+ *
+ * Names are polymorphic: image names end in `.png`, video names in `.mp4` (see SimpleNameService).
+ * `isVideoName` discriminates so we can subscribe to the right query and render the right component.
+ *
+ * We rely on `useRangeBasedImageFetching` to fetch all DTOs into the RTK Query cache. Here we just
+ * consume the cache — `useQuerySubscription` with `skip: isUninitialized` subscribes only after the
+ * fetch has populated data (see https://github.com/reduxjs/redux-toolkit/discussions/4213).
*/
const ImageAtPosition = memo(({ imageName }: { index: number; imageName: string }) => {
- /*
- * We rely on the useRangeBasedImageFetching to fetch all image DTOs, caching them with RTK Query.
- *
- * In this component, we just want to consume that cache. Unforutnately, RTK Query does not provide a way to
- * subscribe to a query without triggering a new fetch.
- *
- * There is a hack, though:
- * - https://github.com/reduxjs/redux-toolkit/discussions/4213
- *
- * This essentially means "subscribe to the query once it has some data".
- *
- * One issue with this approach. When an item DTO is already cached - for example, because it is selected and
- * rendered in the viewer - it will show up in the grid before the other items have loaded. This is most
- * noticeable when first loading a board. The first item in the board is selected and rendered immediately in
- * the viewer, caching the DTO. The gallery grid renders, and that first item displays as a thumbnail while the
- * others are still placeholders. After a moment, the rest of the items load up and display as thumbnails.
- */
-
- // Use `currentData` instead of `data` to prevent a flash of previous image rendered at this index
- const { currentData: imageDTO, isUninitialized } = imagesApi.endpoints.getImageDTO.useQueryState(imageName);
- imagesApi.endpoints.getImageDTO.useQuerySubscription(imageName, { skip: isUninitialized });
+ const isVideo = isVideoName(imageName);
+ const store = useAppStore();
+ const { t } = useTranslation();
+ const retryVideo = useCallback(() => {
+ store.dispatch(videosApi.endpoints.getVideoDTO.initiate(imageName, getVideoPrefetchOptions()));
+ }, [imageName, store]);
+
+ // Always call both hooks (React rules of hooks) — the irrelevant one is just a no-op subscription.
+ const imageState = imagesApi.endpoints.getImageDTO.useQueryState(isVideo ? '' : imageName);
+ imagesApi.endpoints.getImageDTO.useQuerySubscription(isVideo ? '' : imageName, {
+ skip: isVideo || imageState.isUninitialized,
+ });
+ const videoState = videosApi.endpoints.getVideoDTO.useQueryState(isVideo ? imageName : '');
+ videosApi.endpoints.getVideoDTO.useQuerySubscription(isVideo ? imageName : '', {
+ skip: !isVideo || videoState.isUninitialized,
+ });
+
+ if (isVideo) {
+ if (videoState.isError) {
+ return (
+
+ } />
+
+ );
+ }
+ const videoDTO = videoState.currentData;
+ if (!videoDTO) {
+ return ;
+ }
+ return ;
+ }
+ const imageDTO = imageState.currentData;
if (!imageDTO) {
return ;
}
-
return ;
});
ImageAtPosition.displayName = 'ImageAtPosition';
@@ -309,30 +338,47 @@ const useStarImageHotkey = () => {
const lastSelectedItem = useAppSelector(selectLastSelectedItem);
const selectionCount = useAppSelector(selectSelectionCount);
const isGalleryFocused = useIsRegionFocused('gallery');
- const imageDTO = useImageDTO(lastSelectedItem);
+ const isVideo = lastSelectedItem ? isVideoName(lastSelectedItem) : false;
+ const imageDTO = useImageDTO(isVideo ? null : lastSelectedItem);
+ const videoDTO = useVideoDTO(isVideo ? lastSelectedItem : null);
const [starImages] = useStarImagesMutation();
const [unstarImages] = useUnstarImagesMutation();
+ const [starVideos] = useStarVideosMutation();
+ const [unstarVideos] = useUnstarVideosMutation();
+
+ const dto = isVideo ? videoDTO : imageDTO;
const handleStarHotkey = useCallback(() => {
- if (!imageDTO) {
- return;
- }
if (!isGalleryFocused) {
return;
}
- if (imageDTO.starred) {
- unstarImages({ image_names: [imageDTO.image_name] });
+ if (isVideo) {
+ if (!videoDTO) {
+ return;
+ }
+ if (videoDTO.starred) {
+ unstarVideos({ video_names: [videoDTO.video_name] });
+ } else {
+ starVideos({ video_names: [videoDTO.video_name] });
+ }
} else {
- starImages({ image_names: [imageDTO.image_name] });
+ if (!imageDTO) {
+ return;
+ }
+ if (imageDTO.starred) {
+ unstarImages({ image_names: [imageDTO.image_name] });
+ } else {
+ starImages({ image_names: [imageDTO.image_name] });
+ }
}
- }, [imageDTO, isGalleryFocused, starImages, unstarImages]);
+ }, [isGalleryFocused, isVideo, imageDTO, videoDTO, starImages, unstarImages, starVideos, unstarVideos]);
useRegisteredHotkeys({
id: 'starImage',
category: 'gallery',
callback: handleStarHotkey,
- options: { enabled: !!imageDTO && selectionCount === 1 && isGalleryFocused },
- dependencies: [imageDTO, selectionCount, isGalleryFocused, handleStarHotkey],
+ options: { enabled: !!dto && selectionCount === 1 && isGalleryFocused },
+ dependencies: [dto, selectionCount, isGalleryFocused, handleStarHotkey],
});
};
diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryUploadButton.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryUploadButton.tsx
index af3b980947e..5134a883e7b 100644
--- a/invokeai/frontend/web/src/features/gallery/components/GalleryUploadButton.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/GalleryUploadButton.tsx
@@ -4,7 +4,8 @@ import { t } from 'i18next';
import { memo } from 'react';
import { PiUploadBold } from 'react-icons/pi';
-const UPLOAD_OPTIONS: Parameters[0] = { allowMultiple: true };
+// The gallery uploader is the one place videos are accepted — every other consumer wants an image.
+const UPLOAD_OPTIONS: Parameters[0] = { allowMultiple: true, allowVideos: true };
export const GalleryUploadButton = memo(() => {
const uploadApi = useImageUploadButton(UPLOAD_OPTIONS);
@@ -14,9 +15,10 @@ export const GalleryUploadButton = memo(() => {
size="sm"
alignSelf="stretch"
variant="link"
- aria-label={t('accessibility.uploadImages')}
- tooltip={t('accessibility.uploadImages')}
+ aria-label={t('accessibility.uploadMedia')}
+ tooltip={t('accessibility.uploadMedia')}
icon={ }
+ isLoading={uploadApi.isUploading}
{...uploadApi.getUploadButtonProps()}
/>
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx
index d03a884a094..1e0cf5de9f3 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx
@@ -15,18 +15,15 @@ import { createSingleImageDragPreview, setSingleImageDragPreview } from 'feature
import { firefoxDndFix } from 'features/dnd/util';
import { useImageContextMenu } from 'features/gallery/components/ContextMenu/ImageContextMenu';
import { GalleryItemHoverIcons } from 'features/gallery/components/ImageGrid/GalleryItemHoverIcons';
-import {
- selectGetImageNamesQueryArgs,
- selectSelectedBoardId,
- selectSelection,
-} from 'features/gallery/store/gallerySelectors';
+import { selectSelectedBoardId, selectSelection } from 'features/gallery/store/gallerySelectors';
import { imageToCompareChanged, selectGallerySlice, selectionChanged } from 'features/gallery/store/gallerySlice';
+import { selectCachedGalleryItemNames } from 'features/gallery/store/selectCachedGalleryItemNames';
+import { isVideoName } from 'features/gallery/store/types';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { VIEWER_PANEL_ID } from 'features/ui/layouts/shared';
import type { MouseEvent, MouseEventHandler } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { PiImageBold } from 'react-icons/pi';
-import { imagesApi } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
import { galleryItemContainerSX } from './galleryItemContainerSX';
@@ -39,13 +36,14 @@ const buildOnClick =
(imageName: string, dispatch: AppDispatch, getState: AppGetState) => (e: MouseEvent) => {
const { shiftKey, ctrlKey, metaKey, altKey } = e;
const state = getState();
- const queryArgs = selectGetImageNamesQueryArgs(state);
- const imageNames = imagesApi.endpoints.getImageNames.select(queryArgs)(state).data?.image_names ?? [];
-
- // If we don't have the image names cached, we can't perform selection operations
- // This can happen if the user clicks on an image before the names are loaded
- if (imageNames.length === 0) {
- // For basic click without modifiers, we can still set selection
+ // Read from the polymorphic getGalleryItemNames cache (the source of truth for grid order)
+ // rather than the legacy image-only getImageNames cache, which is no longer populated for
+ // the grid. Without this, shift- and ctrl-click would silently no-op because the legacy
+ // list comes back empty.
+ const itemNames = selectCachedGalleryItemNames(state);
+
+ if (itemNames.length === 0) {
+ // Without an ordered list we can still honor a plain single-click.
if (!shiftKey && !ctrlKey && !metaKey && !altKey) {
dispatch(selectionChanged([imageName]));
}
@@ -63,13 +61,12 @@ const buildOnClick =
} else if (shiftKey) {
const rangeEndImageName = imageName;
const lastSelectedImage = selection.at(-1);
- const lastClickedIndex = imageNames.findIndex((name) => name === lastSelectedImage);
- const currentClickedIndex = imageNames.findIndex((name) => name === rangeEndImageName);
+ const lastClickedIndex = itemNames.findIndex((name) => name === lastSelectedImage);
+ const currentClickedIndex = itemNames.findIndex((name) => name === rangeEndImageName);
if (lastClickedIndex > -1 && currentClickedIndex > -1) {
- // We have a valid range!
const start = Math.min(lastClickedIndex, currentClickedIndex);
const end = Math.max(lastClickedIndex, currentClickedIndex);
- const imagesToSelect = imageNames.slice(start, end + 1);
+ const imagesToSelect = itemNames.slice(start, end + 1);
if (currentClickedIndex < lastClickedIndex) {
imagesToSelect.reverse();
}
@@ -136,11 +133,17 @@ export const GalleryImage = memo(({ imageDTO }: Props) => {
const selection = selectSelection(store.getState());
const boardId = selectSelectedBoardId(store.getState());
- // When we have multiple images selected, and the dragged image is part of the selection, initiate a
- // multi-image drag.
+ // When we have multiple items selected, and the dragged image is part of the
+ // selection, initiate a multi-drag. Mixed selections (images + videos) ride along in
+ // the same payload: the board drop handler splits them and dispatches both mutations.
+ // Without filtering, video names would land in `image_names` and the image router
+ // would 404 on each one.
if (selection.length > 1 && selection.some((n) => n === imageDTO.image_name)) {
+ const image_names = selection.filter((n) => !isVideoName(n));
+ const video_names = selection.filter(isVideoName);
return multipleImageDndSource.getData({
- image_names: selection,
+ image_names,
+ video_names,
board_id: boardId,
});
}
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemPlayBadge.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemPlayBadge.tsx
new file mode 100644
index 00000000000..3ea9b00ad50
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemPlayBadge.tsx
@@ -0,0 +1,34 @@
+import { Flex, Icon } from '@invoke-ai/ui-library';
+import { memo } from 'react';
+import { PiPlayFill } from 'react-icons/pi';
+
+/**
+ * Centered play-button badge laid over a video thumbnail in the gallery grid. Purely visual —
+ * the gallery item itself owns click selection; the play action lives in the viewer.
+ */
+export const GalleryItemPlayBadge = memo(() => {
+ return (
+
+
+
+ );
+});
+
+GalleryItemPlayBadge.displayName = 'GalleryItemPlayBadge';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx
index 71607ff3ecb..cca87de2da6 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx
@@ -1,5 +1,6 @@
import { DndImageIcon } from 'features/dnd/DndImageIcon';
import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
import { PiStarBold, PiStarFill } from 'react-icons/pi';
import { useStarImagesMutation, useUnstarImagesMutation } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
@@ -9,6 +10,7 @@ type Props = {
};
export const GalleryItemStarIconButton = memo(({ imageDTO }: Props) => {
+ const { t } = useTranslation();
const [starImages] = useStarImagesMutation();
const [unstarImages] = useUnstarImagesMutation();
@@ -24,7 +26,7 @@ export const GalleryItemStarIconButton = memo(({ imageDTO }: Props) => {
: }
- tooltip={imageDTO.starred ? 'Unstar' : 'Star'}
+ tooltip={imageDTO.starred ? t('gallery.unstarImage') : t('gallery.starImage')}
position="absolute"
top={2}
insetInlineEnd={2}
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemVideoStarIconButton.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemVideoStarIconButton.tsx
new file mode 100644
index 00000000000..c9894f53656
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemVideoStarIconButton.tsx
@@ -0,0 +1,37 @@
+import { DndImageIcon } from 'features/dnd/DndImageIcon';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiStarBold, PiStarFill } from 'react-icons/pi';
+import { useStarVideosMutation, useUnstarVideosMutation } from 'services/api/endpoints/videos';
+import type { VideoDTO } from 'services/api/types';
+
+type Props = {
+ videoDTO: VideoDTO;
+};
+
+export const GalleryItemVideoStarIconButton = memo(({ videoDTO }: Props) => {
+ const { t } = useTranslation();
+ const [starVideos] = useStarVideosMutation();
+ const [unstarVideos] = useUnstarVideosMutation();
+
+ const toggleStarredState = useCallback(() => {
+ if (videoDTO.starred) {
+ unstarVideos({ video_names: [videoDTO.video_name] });
+ } else {
+ starVideos({ video_names: [videoDTO.video_name] });
+ }
+ }, [starVideos, unstarVideos, videoDTO]);
+
+ return (
+ : }
+ tooltip={videoDTO.starred ? t('gallery.unstarVideo', { count: 1 }) : t('gallery.starVideo', { count: 1 })}
+ position="absolute"
+ top={2}
+ insetInlineEnd={2}
+ />
+ );
+});
+
+GalleryItemVideoStarIconButton.displayName = 'GalleryItemVideoStarIconButton';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoItem.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoItem.tsx
new file mode 100644
index 00000000000..0f07650b725
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoItem.tsx
@@ -0,0 +1,182 @@
+import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
+import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
+import { Flex, Image } from '@invoke-ai/ui-library';
+import { createSelector } from '@reduxjs/toolkit';
+import type { AppDispatch, AppGetState } from 'app/store/store';
+import { useAppSelector, useAppStore } from 'app/store/storeHooks';
+import { uniq } from 'es-toolkit';
+import { multipleVideoDndSource, singleVideoDndSource } from 'features/dnd/dnd';
+import { firefoxDndFix } from 'features/dnd/util';
+import { useVideoContextMenu } from 'features/gallery/components/ContextMenu/VideoContextMenu';
+import {
+ selectAlwaysShouldImageSizeBadge,
+ selectSelectedBoardId,
+ selectSelection,
+} from 'features/gallery/store/gallerySelectors';
+import { selectGallerySlice, selectionChanged } from 'features/gallery/store/gallerySlice';
+import { selectCachedGalleryItemNames } from 'features/gallery/store/selectCachedGalleryItemNames';
+import { isVideoName } from 'features/gallery/store/types';
+import { navigationApi } from 'features/ui/layouts/navigation-api';
+import { VIEWER_PANEL_ID } from 'features/ui/layouts/shared';
+import type { MouseEvent, MouseEventHandler } from 'react';
+import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import type { VideoDTO } from 'services/api/types';
+
+import { galleryItemContainerSX } from './galleryItemContainerSX';
+import { GalleryItemPlayBadge } from './GalleryItemPlayBadge';
+import { GalleryItemSizeBadge } from './GalleryItemSizeBadge';
+import { GalleryItemVideoStarIconButton } from './GalleryItemVideoStarIconButton';
+
+interface Props {
+ videoDTO: VideoDTO;
+}
+
+/**
+ * Click handler for selection. Mirrors the image grid's logic but reads the polymorphic
+ * /gallery/items/names cache to know the full ordered list (since a shift-range across a
+ * mixed image+video gallery has to include both kinds).
+ *
+ * Video items do not participate in alt-click comparison (comparison is image-only).
+ */
+const buildOnClick =
+ (videoName: string, dispatch: AppDispatch, getState: AppGetState) => (e: MouseEvent) => {
+ const { shiftKey, ctrlKey, metaKey, altKey } = e;
+ // We need the same query args the gallery grid used to fetch its name list. The grid
+ // calls `useGalleryItemNames` which forwards the args to the polymorphic gallery endpoint.
+ // Pull the most recent cached entry to recover the ordering.
+ const state = getState();
+ const itemNames = selectCachedGalleryItemNames(state);
+
+ if (itemNames.length === 0) {
+ // Without an ordered list, only basic single-click selection is possible.
+ if (!shiftKey && !ctrlKey && !metaKey && !altKey) {
+ dispatch(selectionChanged([videoName]));
+ }
+ return;
+ }
+
+ const selection = state.gallery.selection;
+
+ if (altKey) {
+ // Alt-click is image-only (comparison view). Quietly treat as a normal click for videos.
+ dispatch(selectionChanged([videoName]));
+ } else if (shiftKey) {
+ const lastSelectedItem = selection.at(-1);
+ const lastClickedIndex = itemNames.findIndex((name) => name === lastSelectedItem);
+ const currentClickedIndex = itemNames.findIndex((name) => name === videoName);
+ if (lastClickedIndex > -1 && currentClickedIndex > -1) {
+ const start = Math.min(lastClickedIndex, currentClickedIndex);
+ const end = Math.max(lastClickedIndex, currentClickedIndex);
+ const itemsToSelect = itemNames.slice(start, end + 1);
+ if (currentClickedIndex < lastClickedIndex) {
+ itemsToSelect.reverse();
+ }
+ dispatch(selectionChanged(uniq(selection.concat(itemsToSelect))));
+ }
+ } else if (ctrlKey || metaKey) {
+ if (selection.some((n) => n === videoName) && selection.length > 1) {
+ dispatch(selectionChanged(uniq(selection.filter((n) => n !== videoName))));
+ } else {
+ dispatch(selectionChanged(uniq(selection.concat(videoName))));
+ }
+ } else {
+ dispatch(selectionChanged([videoName]));
+ }
+ };
+
+export const GalleryVideoItem = memo(({ videoDTO }: Props) => {
+ const store = useAppStore();
+ const ref = useRef(null);
+ const [isHovered, setIsHovered] = useState(false);
+ const alwaysShowSizeBadge = useAppSelector(selectAlwaysShouldImageSizeBadge);
+
+ const selectIsSelected = useMemo(
+ () => createSelector(selectGallerySlice, (gallery) => gallery.selection.some((n) => n === videoDTO.video_name)),
+ [videoDTO.video_name]
+ );
+ const isSelected = useAppSelector(selectIsSelected);
+
+ const onMouseOver = useCallback(() => setIsHovered(true), []);
+ const onMouseOut = useCallback(() => setIsHovered(false), []);
+
+ const onClick = useMemo(() => buildOnClick(videoDTO.video_name, store.dispatch, store.getState), [videoDTO, store]);
+
+ const onDoubleClick = useCallback>(() => {
+ navigationApi.focusPanelInActiveTab(VIEWER_PANEL_ID);
+ }, []);
+
+ // Reuse the image item's size-badge component — its only inputs are width/height.
+ const sizeBadgeImageStandIn = useMemo(
+ () => ({ width: videoDTO.width, height: videoDTO.height }),
+ [videoDTO.width, videoDTO.height]
+ );
+
+ // Right-click / long-press context menu (delete, change board, download).
+ useVideoContextMenu(videoDTO, ref);
+
+ // Register the item as a drag source so users can drop videos onto node fields,
+ // ref-image inputs, etc. — mirrors DndImage for image gallery items.
+ useEffect(() => {
+ const element = ref.current;
+ if (!element) {
+ return;
+ }
+ return combine(
+ firefoxDndFix(element),
+ draggable({
+ element,
+ getInitialData: () => {
+ // When the dragged video is part of a multi-selection, send the whole selection so a
+ // bulk move-to-board fires for every selected item. Mixed selections (videos + images)
+ // ride along in the same payload: the board drop handler splits them and dispatches
+ // both mutations. Without this, only the single dragged video would move.
+ const state = store.getState();
+ const selection = selectSelection(state);
+ const boardId = selectSelectedBoardId(state);
+ if (selection.length > 1 && selection.includes(videoDTO.video_name)) {
+ const video_names = selection.filter(isVideoName);
+ const image_names = selection.filter((n) => !isVideoName(n));
+ return multipleVideoDndSource.getData({
+ video_names,
+ image_names,
+ board_id: boardId,
+ });
+ }
+ return singleVideoDndSource.getData({ videoDTO }, videoDTO.video_name);
+ },
+ })
+ );
+ }, [videoDTO, store]);
+
+ return (
+
+
+
+ {(isHovered || alwaysShowSizeBadge) && (
+ [0]['imageDTO']}
+ />
+ )}
+ {(isHovered || videoDTO.starred) && }
+
+ );
+});
+
+GalleryVideoItem.displayName = 'GalleryVideoItem';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoPreview.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoPreview.tsx
new file mode 100644
index 00000000000..195e8901f60
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoPreview.tsx
@@ -0,0 +1,402 @@
+import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
+import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
+import { Box, Button, Flex, IconButton } from '@invoke-ai/ui-library';
+import { useStore } from '@nanostores/react';
+import { useAppSelector, useAppStore } from 'app/store/storeHooks';
+import { useClipboard } from 'common/hooks/useClipboard';
+import { useDownloadItem } from 'common/hooks/useDownloadImage';
+import { useDeleteVideoModalApi } from 'features/deleteVideoModal/store/state';
+import { multipleVideoDndSource, singleVideoDndSource } from 'features/dnd/dnd';
+import { firefoxDndFix } from 'features/dnd/util';
+import NextPrevItemButtons from 'features/gallery/components/NextPrevItemButtons';
+import { useNextPrevItemNavigation } from 'features/gallery/components/useNextPrevItemNavigation';
+import { selectSelectedBoardId, selectSelection } from 'features/gallery/store/gallerySelectors';
+import { isVideoName } from 'features/gallery/store/types';
+import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
+import { toast } from 'features/toast/toast';
+import { navigationApi } from 'features/ui/layouts/navigation-api';
+import { selectActiveTab, selectShouldShowProgressInViewer } from 'features/ui/store/uiSelectors';
+import type { AnimationProps } from 'framer-motion';
+import { AnimatePresence, motion } from 'framer-motion';
+import { memo, useCallback, useEffect, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiArrowSquareOutBold, PiCopyBold, PiDownloadSimpleBold, PiTrashSimpleBold, PiXBold } from 'react-icons/pi';
+import type { VideoDTO } from 'services/api/types';
+
+import { useImageViewerContext } from './context';
+import { NoContentForViewer } from './NoContentForViewer';
+import { ProgressImage } from './ProgressImage2';
+import { ProgressIndicator } from './ProgressIndicator2';
+import { VideoPlayButtonOverlay } from './VideoPlayButtonOverlay';
+
+type Props = {
+ videoDTO: VideoDTO | null;
+};
+
+/**
+ * Counterpart to CurrentImagePreview for videos. A single element spans both states:
+ *
+ * - **idle**: muted, no controls. Without a `poster` attribute the browser decodes and
+ * displays the video's actual first frame at full resolution (much sharper than the
+ * small WebP gallery thumbnail upscaled to fit the viewer). A centered play button
+ * overlay sits on top.
+ * - **playing**: native HTML5 controls + audio. The element is the same DOM node, so the
+ * decoded buffer carries over — no reload when the user hits play.
+ *
+ * Changing the selected video swaps the element via `key={videoName}`, which discards the
+ * old playback state cleanly.
+ *
+ * Mirrors CurrentImagePreview's progress overlay so denoise previews from a new render
+ * appear on top of the previously-loaded video. Without this, a freshly generated render's
+ * progress images had nowhere to display whenever a video was the last-selected gallery
+ * item (and the user only saw the static first-frame still until the new video finished).
+ */
+export const CurrentVideoPreview = memo(({ videoDTO }: Props) => {
+ const { t } = useTranslation();
+ const store = useAppStore();
+ const videoName = videoDTO?.video_name ?? null;
+ const videoRef = useRef(null);
+ const [isPlaying, setIsPlaying] = useState(false);
+ const shouldShowProgressInViewer = useAppSelector(selectShouldShowProgressInViewer);
+ const activeTab = useAppSelector(selectActiveTab);
+ const deleteVideoModal = useDeleteVideoModalApi();
+ const { downloadItem } = useDownloadItem();
+ const clipboard = useClipboard();
+ const { $progressEvent, $progressImage, onLoadImage } = useImageViewerContext();
+ const progressEvent = useStore($progressEvent);
+ const progressImage = useStore($progressImage);
+ const withProgress = shouldShowProgressInViewer && progressImage !== null;
+ const { goToPreviousImage, goToNextImage, isFetching } = useNextPrevItemNavigation();
+
+ // Whenever the selected video changes, drop back to the idle still + play overlay.
+ useEffect(() => {
+ setIsPlaying(false);
+ }, [videoName]);
+
+ // Register the viewer's as a drag source so users can drag the currently-displayed
+ // video onto node fields (e.g. a Video Primitive's "Starting Video" input) directly from
+ // the viewer, just like they can from the gallery thumbnail. Mirrors GalleryVideoItem's
+ // setup. Without this, the bare element has no drag handler and the drop target
+ // sees nothing it can accept.
+ useEffect(() => {
+ const element = videoRef.current;
+ if (!element || !videoDTO) {
+ return;
+ }
+ return combine(
+ firefoxDndFix(element),
+ draggable({
+ element,
+ getInitialData: () => {
+ // Honor any active gallery multi-selection so dropping onto a board moves the whole
+ // batch, matching the gallery thumbnail's behavior.
+ const state = store.getState();
+ const selection = selectSelection(state);
+ const boardId = selectSelectedBoardId(state);
+ if (selection.length > 1 && selection.includes(videoDTO.video_name)) {
+ const video_names = selection.filter(isVideoName);
+ const image_names = selection.filter((n) => !isVideoName(n));
+ return multipleVideoDndSource.getData({
+ video_names,
+ image_names,
+ board_id: boardId,
+ });
+ }
+ return singleVideoDndSource.getData({ videoDTO }, videoDTO.video_name);
+ },
+ })
+ );
+ }, [videoDTO, store]);
+
+ const handlePlay = useCallback(() => {
+ setIsPlaying(true);
+ // The ref points at the same element we'll re-render with controls/audio; calling
+ // play() here keeps the user gesture wired to playback without waiting for React.
+ void videoRef.current?.play();
+ }, []);
+
+ // Close: stop playback and drop back to the first-frame preview + play overlay. We
+ // explicitly pause() because toggling React's `controls` prop hides the chrome but does
+ // not stop playback. Seeking back to ~0 nudges the decoder to re-paint the first frame
+ // (mirroring handleLoadedMetadata's near-zero seek trick).
+ const handleClose = useCallback(() => {
+ const el = videoRef.current;
+ if (el) {
+ el.pause();
+ try {
+ el.currentTime = 0.0001;
+ } catch {
+ // Some browsers throw if metadata isn't fully ready yet; harmless.
+ }
+ }
+ setIsPlaying(false);
+ }, []);
+
+ const handleDelete = useCallback(async () => {
+ if (!videoDTO) {
+ return;
+ }
+ try {
+ await deleteVideoModal.delete([videoDTO.video_name]);
+ } catch {
+ // user canceled the confirmation dialog
+ }
+ }, [deleteVideoModal, videoDTO]);
+
+ const handleDownload = useCallback(() => {
+ if (!videoDTO) {
+ return;
+ }
+ void downloadItem(videoDTO.video_url, videoDTO.video_name);
+ }, [downloadItem, videoDTO]);
+
+ const handleOpenInNewTab = useCallback(() => {
+ if (!videoDTO) {
+ return;
+ }
+ window.open(videoDTO.video_url, '_blank', 'noopener,noreferrer');
+ }, [videoDTO]);
+
+ // Cross-browser clipboard support for raw `video/*` MIME types doesn't really exist — Chrome
+ // and Firefox both reject anything outside a small allow-list (image/png, image/jpeg, text).
+ // So instead we grab the currently-displayed frame off the element via a canvas and
+ // hand the resulting PNG to the standard image-clipboard path. The video is same-origin so
+ // the canvas doesn't taint.
+ const handleCopyFrame = useCallback(async () => {
+ const el = videoRef.current;
+ if (!el || !el.videoWidth || !el.videoHeight) {
+ return;
+ }
+ try {
+ const canvas = document.createElement('canvas');
+ canvas.width = el.videoWidth;
+ canvas.height = el.videoHeight;
+ const ctx = canvas.getContext('2d');
+ if (!ctx) {
+ throw new Error('Unable to acquire 2D canvas context');
+ }
+ ctx.drawImage(el, 0, 0);
+ const blob = await new Promise((resolve) => {
+ canvas.toBlob((b) => resolve(b), 'image/png');
+ });
+ if (!blob) {
+ throw new Error('Unable to encode frame as PNG');
+ }
+ clipboard.writeImage(blob, () => {
+ toast({
+ id: 'IMAGE_COPIED',
+ title: t('toast.imageCopied'),
+ status: 'success',
+ });
+ });
+ } catch (err) {
+ toast({
+ id: 'PROBLEM_COPYING_IMAGE',
+ title: t('toast.problemCopyingImage'),
+ description: String(err),
+ status: 'error',
+ });
+ }
+ }, [clipboard, t]);
+
+ // Mirror CurrentImagePreview's hover-driven next/prev gating so the arrows only intrude
+ // while the user is interacting with the viewer.
+ const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] = useState(false);
+ const timeoutId = useRef(0);
+ const onMouseOver = useCallback(() => {
+ setShouldShowNextPrevButtons(true);
+ window.clearTimeout(timeoutId.current);
+ }, []);
+ const onMouseOut = useCallback(() => {
+ timeoutId.current = window.setTimeout(() => {
+ setShouldShowNextPrevButtons(false);
+ }, 500);
+ }, []);
+
+ const handleViewerArrowNavigation = useCallback(
+ (event: KeyboardEvent, navigate: () => void) => {
+ if (!navigationApi.isViewerArrowNavigationMode(activeTab) || !videoDTO || isFetching) {
+ return;
+ }
+ if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {
+ return;
+ }
+ event.preventDefault();
+ navigate();
+ },
+ [activeTab, videoDTO, isFetching]
+ );
+
+ const onHotkeyPrevImage = useCallback(
+ (event: KeyboardEvent) => {
+ handleViewerArrowNavigation(event, goToPreviousImage);
+ },
+ [goToPreviousImage, handleViewerArrowNavigation]
+ );
+
+ const onHotkeyNextImage = useCallback(
+ (event: KeyboardEvent) => {
+ handleViewerArrowNavigation(event, goToNextImage);
+ },
+ [goToNextImage, handleViewerArrowNavigation]
+ );
+
+ useRegisteredHotkeys({
+ id: 'galleryNavLeft',
+ category: 'gallery',
+ callback: onHotkeyPrevImage,
+ options: { preventDefault: true },
+ dependencies: [onHotkeyPrevImage],
+ });
+
+ useRegisteredHotkeys({
+ id: 'galleryNavRight',
+ category: 'gallery',
+ callback: onHotkeyNextImage,
+ options: { preventDefault: true },
+ dependencies: [onHotkeyNextImage],
+ });
+
+ // Analogous to in the image viewer: clear any stale
+ // denoise progress overlay once the new video's metadata is in. Without this, the
+ // ImageViewerContext atom stays set after a video render (there's no image load to
+ // trigger its clear), so the overlay sticks over the freshly-selected video forever.
+ //
+ // Also force a first-frame paint via a near-zero seek. With preload="metadata" some
+ // browsers populate dimensions/duration but don't actually decode and display the first
+ // video frame until playback or a seek — the element just shows its black background.
+ // Setting currentTime to 0.0001 nudges the decoder to paint without measurably advancing.
+ const handleLoadedMetadata = useCallback(() => {
+ onLoadImage();
+ const el = videoRef.current;
+ if (el && !isPlaying && el.currentTime === 0) {
+ try {
+ el.currentTime = 0.0001;
+ } catch {
+ // Some browsers throw if metadata isn't fully ready yet; harmless.
+ }
+ }
+ }, [isPlaying, onLoadImage]);
+
+ if (!videoDTO) {
+ return ;
+ }
+
+ return (
+
+
+ {!isPlaying && !withProgress && }
+ {withProgress && (
+
+
+ {progressEvent && (
+
+ )}
+
+ )}
+ {/* Top action bar, right-aligned. Auto-sized Flex anchored to insetInlineEnd leaves the
+ rest of the viewer click-through so clicking the video still pauses native playback.
+ Order: open in new tab, copy frame, download, delete, then the labelled close button
+ farthest right (only while the player is active). */}
+
+ }
+ onClick={handleOpenInNewTab}
+ variant="solid"
+ size="sm"
+ />
+ }
+ onClick={handleCopyFrame}
+ variant="solid"
+ size="sm"
+ />
+ }
+ onClick={handleDownload}
+ variant="solid"
+ size="sm"
+ />
+ }
+ onClick={handleDelete}
+ colorScheme="error"
+ variant="solid"
+ size="sm"
+ />
+ {isPlaying && (
+ } onClick={handleClose} variant="solid" size="sm">
+ {t('gallery.closeVideoPlayer')}
+
+ )}
+
+
+ {shouldShowNextPrevButtons && (
+
+
+
+ )}
+
+
+ );
+});
+
+const initial: AnimationProps['initial'] = {
+ opacity: 0,
+};
+const animateArrows: AnimationProps['animate'] = {
+ opacity: 1,
+ transition: { duration: 0.07 },
+};
+const exit: AnimationProps['exit'] = {
+ opacity: 0,
+ transition: { duration: 0.07 },
+};
+
+CurrentVideoPreview.displayName = 'CurrentVideoPreview';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
index ce9795ee8b0..35f5e40eaca 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx
@@ -1,12 +1,13 @@
import { Divider, Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
+import { useGalleryItemDTO } from 'common/hooks/useGalleryItemDTO';
import { setComparisonImageDndTarget } from 'features/dnd/dnd';
import { DndDropTarget } from 'features/dnd/DndDropTarget';
import { CurrentImagePreview } from 'features/gallery/components/ImageViewer/CurrentImagePreview';
+import { CurrentVideoPreview } from 'features/gallery/components/ImageViewer/CurrentVideoPreview';
import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
-import { useImageDTO } from 'services/api/endpoints/images';
import { ImageViewerToolbar } from './ImageViewerToolbar';
@@ -16,18 +17,30 @@ export const ImageViewer = memo(() => {
const { t } = useTranslation();
const lastSelectedItem = useAppSelector(selectLastSelectedItem);
- const lastSelectedImageDTO = useImageDTO(lastSelectedItem ?? null);
+ const galleryItem = useGalleryItemDTO(lastSelectedItem);
+
+ // Polymorphic preview: videos render the play-overlay/HTML5 video; images render the existing
+ // DndImage-based preview with progress / metadata / next-prev affordances.
+ let preview;
+ if (galleryItem?.kind === 'video') {
+ preview = ;
+ } else {
+ preview = ;
+ }
+
return (
-
-
+ {preview}
+ {galleryItem?.kind !== 'video' && (
+
+ )}
);
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx
index b963f5a80d6..dac87094e4b 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx
@@ -1,24 +1,28 @@
import { Flex, Spacer } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
+import { useGalleryItemDTO } from 'common/hooks/useGalleryItemDTO';
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
import { memo } from 'react';
-import { useImageDTO } from 'services/api/endpoints/images';
import { CurrentImageButtons } from './CurrentImageButtons';
import { ToggleProgressButton } from './ToggleProgressButton';
export const ImageViewerToolbar = memo(() => {
const lastSelectedItem = useAppSelector(selectLastSelectedItem);
- const imageDTO = useImageDTO(lastSelectedItem);
+ const galleryItem = useGalleryItemDTO(lastSelectedItem);
+
+ // Videos don't carry workflows or recallable metadata yet — the action row + metadata viewer
+ // toggle are image-specific. We still show the progress button (it's media-agnostic).
+ const showImageActions = galleryItem?.kind === 'image';
return (
- {imageDTO && }
+ {showImageActions && }
- {imageDTO && }
+ {showImageActions && }
);
});
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/VideoPlayButtonOverlay.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/VideoPlayButtonOverlay.tsx
new file mode 100644
index 00000000000..39777bdd90a
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/VideoPlayButtonOverlay.tsx
@@ -0,0 +1,40 @@
+import { Flex, Icon } from '@invoke-ai/ui-library';
+import { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiPlayFill } from 'react-icons/pi';
+
+type Props = {
+ onClick: () => void;
+};
+
+/**
+ * Large centered play button shown over the still thumbnail in the video viewer. Clicking it
+ * swaps the preview into HTML5 video playback (see CurrentVideoPreview).
+ */
+export const VideoPlayButtonOverlay = memo(({ onClick }: Props) => {
+ const { t } = useTranslation();
+ return (
+
+
+
+ );
+});
+
+VideoPlayButtonOverlay.displayName = 'VideoPlayButtonOverlay';
diff --git a/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.test.ts b/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.test.ts
new file mode 100644
index 00000000000..eff9123cc61
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.test.ts
@@ -0,0 +1,34 @@
+/**
+ * Pins the polymorphic name-flattening used by `useGalleryImageNames` for both regular boards
+ * and date-based virtual boards.
+ *
+ * The bug (PR #9163 review): virtual boards were image-only — selecting a virtual date fetched
+ * from the legacy image_names endpoint, so videos created on that date never appeared. The hook
+ * now consumes the by-date item_names endpoint, which returns the same (kind, name) refs as the
+ * regular gallery names endpoint, and this shared mapper must keep video refs in the flat list.
+ * (The server-side guarantee that a date query returns video refs is pinned by
+ * tests/app/routers/test_virtual_boards.py.)
+ */
+import type { GalleryItemRef } from 'services/api/types';
+import { describe, expect, it } from 'vitest';
+
+import { itemRefsToNames } from './use-gallery-image-names';
+
+describe('itemRefsToNames', () => {
+ it('keeps video refs interleaved with images, preserving order', () => {
+ const items: GalleryItemRef[] = [
+ { kind: 'image', name: 'newest.png' },
+ { kind: 'video', name: 'middle.mp4' },
+ { kind: 'image', name: 'oldest.png' },
+ ];
+ expect(itemRefsToNames(items)).toEqual(['newest.png', 'middle.mp4', 'oldest.png']);
+ });
+
+ it('handles a video-only list (video-only virtual date)', () => {
+ const items: GalleryItemRef[] = [
+ { kind: 'video', name: 'a.mp4' },
+ { kind: 'video', name: 'b.mp4' },
+ ];
+ expect(itemRefsToNames(items)).toEqual(['a.mp4', 'b.mp4']);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.ts b/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.ts
index 487c5609062..4749dc9de8b 100644
--- a/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.ts
+++ b/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.ts
@@ -3,59 +3,81 @@ import { EMPTY_ARRAY } from 'app/store/constants';
import { useAppSelector } from 'app/store/storeHooks';
import { selectGetImageNamesQueryArgs, selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import { getDateFromVirtualBoardId, isVirtualBoardId } from 'features/gallery/store/types';
-import { useGetImageNamesQuery } from 'services/api/endpoints/images';
-import { useGetVirtualBoardImageNamesByDateQuery } from 'services/api/endpoints/virtual_boards';
+import { useMemo } from 'react';
+import { useGetGalleryItemNamesQuery } from 'services/api/endpoints/gallery';
+import { useGetVirtualBoardItemNamesByDateQuery } from 'services/api/endpoints/virtual_boards';
+import type { GalleryItemRef } from 'services/api/types';
import { useDebounce } from 'use-debounce';
-const selectFromResult = ({
+const selectFromGalleryItemNamesResult = ({
currentData,
isLoading,
isFetching,
}: {
- currentData?: { image_names: string[] };
+ currentData?: { items: GalleryItemRef[] };
isLoading: boolean;
isFetching: boolean;
}) => ({
- imageNames: currentData?.image_names ?? EMPTY_ARRAY,
+ items: currentData?.items ?? (EMPTY_ARRAY as GalleryItemRef[]),
isLoading,
isFetching,
});
-const queryOptions = {
+const galleryQueryOptions = {
refetchOnReconnect: true,
- selectFromResult,
+ selectFromResult: selectFromGalleryItemNamesResult,
};
+/**
+ * Flattens polymorphic (kind, name) refs into the ordered name list consumed by the gallery
+ * grid and navigation hotkeys. Video refs must pass through untouched — regular boards and
+ * date-based virtual boards both contain them. Exported for tests.
+ */
+export const itemRefsToNames = (items: GalleryItemRef[]): string[] => items.map((ref) => ref.name);
+
+/**
+ * Returns the ordered flat list of gallery item names. Names are polymorphic — both image and
+ * video names appear in the same list, interleaved by created_at. Callers that need to know the
+ * kind of a particular name use `isVideoName` from `features/gallery/store/types`.
+ *
+ * Virtual boards (date-based) go through their own by-date endpoint, which returns the same
+ * polymorphic (kind, name) refs as the regular gallery names endpoint.
+ */
export const useGalleryImageNames = () => {
const selectedBoardId = useAppSelector(selectSelectedBoardId);
- const _queryArgs = useAppSelector(selectGetImageNamesQueryArgs);
- const [queryArgs] = useDebounce(_queryArgs, 300);
+ const _imageQueryArgs = useAppSelector(selectGetImageNamesQueryArgs);
+ const [imageQueryArgs] = useDebounce(_imageQueryArgs, 300);
const isVirtual = isVirtualBoardId(selectedBoardId);
- // Regular board query
- const regularResult = useGetImageNamesQuery(isVirtual ? skipToken : queryArgs, queryOptions);
+ // The polymorphic gallery names endpoint shares the same filter args as the image names
+ // endpoint (board_id, categories, search_term, order_dir, starred_first, is_intermediate).
+ const galleryResult = useGetGalleryItemNamesQuery(isVirtual ? skipToken : imageQueryArgs, galleryQueryOptions);
- // Virtual board query
const date = isVirtual ? getDateFromVirtualBoardId(selectedBoardId) : '';
- const virtualResult = useGetVirtualBoardImageNamesByDateQuery(
+ const virtualResult = useGetVirtualBoardItemNamesByDateQuery(
isVirtual
? {
date,
- categories: queryArgs.categories ?? undefined,
- search_term: queryArgs.search_term || undefined,
- order_dir: queryArgs.order_dir,
- starred_first: queryArgs.starred_first,
+ categories: imageQueryArgs.categories ?? undefined,
+ search_term: imageQueryArgs.search_term || undefined,
+ order_dir: imageQueryArgs.order_dir,
+ starred_first: imageQueryArgs.starred_first,
}
: skipToken,
- queryOptions
+ galleryQueryOptions
);
- const result = isVirtual ? virtualResult : regularResult;
+ // Flat names + isLoading exposed for backward compatibility with the existing callers (paged
+ // grid, search, navigation hotkeys). The kind is recoverable from the filename extension.
+ const imageNames = useMemo(() => {
+ const items = isVirtual ? virtualResult.items : galleryResult.items;
+ return itemRefsToNames(items);
+ }, [isVirtual, virtualResult.items, galleryResult.items]);
return {
- imageNames: result.imageNames,
- isLoading: result.isLoading,
- isFetching: result.isFetching,
- queryArgs,
+ imageNames,
+ isLoading: isVirtual ? virtualResult.isLoading : galleryResult.isLoading,
+ isFetching: isVirtual ? virtualResult.isFetching : galleryResult.isFetching,
+ queryArgs: imageQueryArgs,
};
};
diff --git a/invokeai/frontend/web/src/features/gallery/contexts/VideoDTOContext.ts b/invokeai/frontend/web/src/features/gallery/contexts/VideoDTOContext.ts
new file mode 100644
index 00000000000..69b822fb1eb
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/contexts/VideoDTOContext.ts
@@ -0,0 +1,13 @@
+import { createContext, useContext } from 'react';
+import type { VideoDTO } from 'services/api/types';
+import { assert } from 'tsafe';
+
+const VideoDTOContext = createContext(null);
+
+export const VideoDTOContextProvider = VideoDTOContext.Provider;
+
+export const useVideoDTOContext = () => {
+ const dto = useContext(VideoDTOContext);
+ assert(dto !== null, 'useVideoDTOContext must be used within VideoDTOContextProvider');
+ return dto;
+};
diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.test.ts b/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.test.ts
new file mode 100644
index 00000000000..8553399abf2
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.test.ts
@@ -0,0 +1,33 @@
+/**
+ * Regression test for the gallery-has-content decision (PR #9163 review).
+ *
+ * The bug: `useHasImages` decided emptiness from boards + the uncategorized *image* total
+ * only. A gallery whose sole content was one uncategorized video was treated as empty, so
+ * `NoContentForViewer` showed the new-user/get-started view instead of the normal
+ * no-selection state.
+ */
+import { describe, expect, it } from 'vitest';
+
+import { getHasGalleryContent } from './useHasImages';
+
+describe('getHasGalleryContent', () => {
+ it('counts a single uncategorized video as content (zero boards, zero uncategorized images)', () => {
+ expect(getHasGalleryContent([], 0, 1)).toBe(true);
+ });
+
+ it('is empty with zero boards, zero uncategorized images and zero uncategorized videos', () => {
+ expect(getHasGalleryContent([], 0, 0)).toBe(false);
+ });
+
+ it('counts a video-only board as content', () => {
+ expect(getHasGalleryContent([{ image_count: 0, video_count: 2 }], 0, 0)).toBe(true);
+ });
+
+ it('counts uncategorized images as content', () => {
+ expect(getHasGalleryContent([], 3, 0)).toBe(true);
+ });
+
+ it('defaults to content when no totals have arrived yet', () => {
+ expect(getHasGalleryContent(undefined, undefined, undefined)).toBe(true);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.ts b/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.ts
index bf94c465b95..8df053d7846 100644
--- a/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.ts
+++ b/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.ts
@@ -1,9 +1,30 @@
import { useMemo } from 'react';
-import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
+import { useGetBoardVideosTotalQuery, useListAllBoardsQuery } from 'services/api/endpoints/boards';
import { useListImagesQuery } from 'services/api/endpoints/images';
export const LOADING_SYMBOL = Symbol('LOADING');
+/**
+ * Decides whether the gallery has any content. A gallery containing only uncategorized
+ * videos must count as non-empty — the video totals are not derivable from the boards
+ * list (which only covers real boards) or the uncategorized *image* total. Exported for
+ * tests.
+ */
+export const getHasGalleryContent = (
+ boardList: { image_count: number; video_count?: number }[] | undefined,
+ uncategorizedImagesTotal: number | undefined,
+ uncategorizedVideosTotal: number | undefined
+): boolean => {
+ if (boardList && boardList.some((board) => board.image_count + (board.video_count ?? 0) > 0)) {
+ return true;
+ }
+ if (uncategorizedImagesTotal === undefined && uncategorizedVideosTotal === undefined) {
+ // No data at all — default to "has content" so we don't flash the new-user view.
+ return true;
+ }
+ return (uncategorizedImagesTotal ?? 0) > 0 || (uncategorizedVideosTotal ?? 0) > 0;
+};
+
export const useHasImages = () => {
const { data: boardList, isLoading: loadingBoards } = useListAllBoardsQuery({ include_archived: true });
const { data: uncategorizedImages, isLoading: loadingImages } = useListImagesQuery({
@@ -12,22 +33,16 @@ export const useHasImages = () => {
limit: 0,
is_intermediate: false,
});
+ const { data: uncategorizedVideos, isLoading: loadingVideos } = useGetBoardVideosTotalQuery('none');
const hasImages = useMemo(() => {
// default to true
- if (loadingBoards || loadingImages) {
+ if (loadingBoards || loadingImages || loadingVideos) {
return LOADING_SYMBOL;
}
- const hasBoards = boardList && boardList.length > 0;
-
- if (hasBoards) {
- if (boardList.filter((board) => board.image_count > 0).length > 0) {
- return true;
- }
- }
- return uncategorizedImages ? uncategorizedImages.total > 0 : true;
- }, [boardList, uncategorizedImages, loadingBoards, loadingImages]);
+ return getHasGalleryContent(boardList, uncategorizedImages?.total, uncategorizedVideos?.total);
+ }, [boardList, uncategorizedImages, uncategorizedVideos, loadingBoards, loadingImages, loadingVideos]);
return hasImages;
};
diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.test.ts b/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.test.ts
new file mode 100644
index 00000000000..6cec16aa043
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.test.ts
@@ -0,0 +1,14 @@
+import { describe, expect, it } from 'vitest';
+
+import { getVideoPrefetchOptions, hasCachedVideoDTO } from './useRangeBasedImageFetching';
+
+describe('video range prefetch', () => {
+ it('does not retain an RTK Query subscription', () => {
+ expect(getVideoPrefetchOptions()).toEqual({ subscribe: false, forceRefetch: true });
+ });
+
+ it('only treats fulfilled DTO queries as cached', () => {
+ expect(hasCachedVideoDTO({ data: { video_name: 'video.mp4' } })).toBe(true);
+ expect(hasCachedVideoDTO({ isError: true })).toBe(false);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.ts b/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.ts
index 8ea8e9023e3..eab38776e5b 100644
--- a/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.ts
+++ b/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.ts
@@ -1,7 +1,9 @@
import { useAppStore } from 'app/store/storeHooks';
+import { isVideoName } from 'features/gallery/store/types';
import { useCallback, useEffect, useState } from 'react';
import type { ListRange } from 'react-virtuoso';
import { imagesApi, useGetImageDTOsByNamesMutation } from 'services/api/endpoints/images';
+import { videosApi } from 'services/api/endpoints/videos';
import { useThrottledCallback } from 'use-debounce';
interface UseRangeBasedImageFetchingArgs {
@@ -13,6 +15,11 @@ interface UseRangeBasedImageFetchingReturn {
onRangeChanged: (range: ListRange) => void;
}
+export const getVideoPrefetchOptions = () => ({ subscribe: false, forceRefetch: true }) as const;
+
+export const hasCachedVideoDTO = (queryState: { data?: unknown; isError?: boolean }): boolean =>
+ queryState.data !== undefined;
+
const getUncachedNames = (imageNames: string[], cachedImageNames: string[], ranges: ListRange[]): string[] => {
const uncachedNamesSet = new Set();
const cachedImageNamesSet = new Set(cachedImageNames);
@@ -30,9 +37,12 @@ const getUncachedNames = (imageNames: string[], cachedImageNames: string[], rang
};
/**
- * Hook for bulk fetching image DTOs based on the visible range from virtuoso.
- * Individual image components should use `useGetImageDTOQuery(imageName)` to get their specific DTO.
- * This hook ensures DTOs are bulk fetched and cached efficiently.
+ * Hook for bulk fetching gallery item DTOs based on the visible range from virtuoso.
+ *
+ * Names are polymorphic — image names go through the bulk `getImageDTOsByNames` mutation while
+ * video names dispatch individual `getVideoDTO` queries (the videos API doesn't have a batch
+ * endpoint yet; per-item is fine while video counts are low). Individual components still call
+ * `useGetImageDTOQuery` / `useGetVideoDTOQuery` to subscribe — this hook only triggers fetches.
*/
export const useRangeBasedImageFetching = ({
imageNames,
@@ -43,23 +53,36 @@ export const useRangeBasedImageFetching = ({
const [lastRange, setLastRange] = useState(null);
const [pendingRanges, setPendingRanges] = useState([]);
- const fetchImages = useCallback(
- (ranges: ListRange[], imageNames: string[]) => {
+ const fetchItems = useCallback(
+ (ranges: ListRange[], allNames: string[]) => {
if (!enabled) {
return;
}
- const cachedImageNames = imagesApi.util.selectCachedArgsForQuery(store.getState(), 'getImageDTO');
- const uncachedNames = getUncachedNames(imageNames, cachedImageNames, ranges);
- if (uncachedNames.length === 0) {
- return;
+ const state = store.getState();
+
+ // Images — bulk fetch via the existing batch endpoint.
+ const cachedImageNames = imagesApi.util.selectCachedArgsForQuery(state, 'getImageDTO');
+ const uncachedImageNames = getUncachedNames(allNames, cachedImageNames, ranges).filter((n) => !isVideoName(n));
+ if (uncachedImageNames.length > 0) {
+ getImageDTOsByNames({ image_names: uncachedImageNames });
}
- getImageDTOsByNames({ image_names: uncachedNames });
+
+ // Videos — fetch one at a time (no batch endpoint yet). Each `initiate()` is a no-op for
+ // already-cached entries, so this is safe to call repeatedly while scrolling.
+ const cachedVideoNames = videosApi.util
+ .selectCachedArgsForQuery(state, 'getVideoDTO')
+ .filter((videoName) => hasCachedVideoDTO(videosApi.endpoints.getVideoDTO.select(videoName)(state)));
+ const uncachedVideoNames = getUncachedNames(allNames, cachedVideoNames, ranges).filter((n) => isVideoName(n));
+ for (const videoName of uncachedVideoNames) {
+ store.dispatch(videosApi.endpoints.getVideoDTO.initiate(videoName, getVideoPrefetchOptions()));
+ }
+
setPendingRanges([]);
},
[enabled, getImageDTOsByNames, store]
);
- const throttledFetchImages = useThrottledCallback(fetchImages, 500);
+ const throttledFetchItems = useThrottledCallback(fetchItems, 500);
const onRangeChanged = useCallback((range: ListRange) => {
setLastRange(range);
@@ -68,8 +91,8 @@ export const useRangeBasedImageFetching = ({
useEffect(() => {
const combinedRanges = lastRange ? [...pendingRanges, lastRange] : pendingRanges;
- throttledFetchImages(combinedRanges, imageNames);
- }, [imageNames, lastRange, pendingRanges, throttledFetchImages]);
+ throttledFetchItems(combinedRanges, imageNames);
+ }, [imageNames, lastRange, pendingRanges, throttledFetchItems]);
return {
onRangeChanged,
diff --git a/invokeai/frontend/web/src/features/gallery/store/selectCachedGalleryItemNames.ts b/invokeai/frontend/web/src/features/gallery/store/selectCachedGalleryItemNames.ts
new file mode 100644
index 00000000000..a4cdbb0557f
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/store/selectCachedGalleryItemNames.ts
@@ -0,0 +1,81 @@
+import type { AppGetState } from 'app/store/store';
+import { galleryApi } from 'services/api/endpoints/gallery';
+import type { GetGalleryItemNamesArgs } from 'services/api/types';
+
+import { selectGetImageNamesQueryArgs } from './gallerySelectors';
+
+/**
+ * Returns the names (in display order) of the currently-cached gallery item list.
+ *
+ * The grid renders via the polymorphic ``getGalleryItemNames`` endpoint, which returns a
+ * mixed image+video list. Range-selection click handlers (shift-click for ranges, ctrl-click
+ * for discontiguous selection) need that ordered list to compute the items between two
+ * clicks.
+ *
+ * We look up the cache entry whose args match the gallery's current query args. RTK Query
+ * keeps recently-used entries warm (60s default ``keepUnusedDataFor``), so after a board
+ * switch the cache contains entries for every board the user has visited this session.
+ * Falling back to "the first invalidated entry" — the previous behaviour — silently picked
+ * a stale board's list, which manifested as shift-click range selection failing at random
+ * until the user did anything that invalidated ``GalleryItemNameList`` (move, delete) and
+ * forced a refetch.
+ */
+export const selectCachedGalleryItemNames = (state: ReturnType): string[] => {
+ const args = selectGetImageNamesQueryArgs(state);
+ // Exact match: the entry the grid is actively subscribed to. This is the common case.
+ const exact = galleryApi.endpoints.getGalleryItemNames.select(args)(state).data;
+ if (exact) {
+ return exact.items.map((ref) => ref.name);
+ }
+ // Debounce window: the grid hook debounces its args by ~300ms, so for a moment after the
+ // user changes a filter the cache key may not match Redux yet. Best-effort fallback to any
+ // cached entry on the same board so range selection still feels responsive — but do not
+ // silently fall back to an unrelated board's entry, which was the bug.
+ const entries = galleryApi.util.selectInvalidatedBy(state, ['GalleryItemNameList']);
+ for (const entry of entries) {
+ if (entry.endpointName !== 'getGalleryItemNames') {
+ continue;
+ }
+ const entryArgs = entry.originalArgs as GetGalleryItemNamesArgs | undefined;
+ if (!entryArgs || entryArgs.board_id !== args.board_id) {
+ continue;
+ }
+ const data = galleryApi.endpoints.getGalleryItemNames.select(entryArgs)(state).data;
+ if (data) {
+ return data.items.map((ref) => ref.name);
+ }
+ }
+ return [];
+};
+
+/**
+ * Given the ordered gallery list, the index of the currently-displayed item in that list,
+ * and the set of names being deleted, return the name that should be selected after the
+ * deletion completes — preferring the immediate predecessor, falling back to the immediate
+ * successor, and returning null if every remaining item was also deleted.
+ *
+ * Used by the image and video delete flows so that clearing the displayed item from the
+ * Viewer lands on an adjacent gallery item instead of the empty-state placeholder.
+ */
+export const pickSelectionAfterDelete = (
+ galleryItemNames: string[],
+ deletedIndex: number,
+ deletedNames: Set
+): string | null => {
+ if (deletedIndex < 0) {
+ return null;
+ }
+ for (let i = deletedIndex - 1; i >= 0; i--) {
+ const name = galleryItemNames[i];
+ if (name && !deletedNames.has(name)) {
+ return name;
+ }
+ }
+ for (let i = deletedIndex + 1; i < galleryItemNames.length; i++) {
+ const name = galleryItemNames[i];
+ if (name && !deletedNames.has(name)) {
+ return name;
+ }
+ }
+ return null;
+};
diff --git a/invokeai/frontend/web/src/features/gallery/store/types.ts b/invokeai/frontend/web/src/features/gallery/store/types.ts
index c040e5834d7..c844aead1c7 100644
--- a/invokeai/frontend/web/src/features/gallery/store/types.ts
+++ b/invokeai/frontend/web/src/features/gallery/store/types.ts
@@ -48,3 +48,11 @@ const VIRTUAL_BOARD_ID_PREFIX = 'by_date:';
export const isVirtualBoardId = (id: string): boolean => id.startsWith(VIRTUAL_BOARD_ID_PREFIX);
export const getDateFromVirtualBoardId = (id: string): string => id.replace(VIRTUAL_BOARD_ID_PREFIX, '');
+
+/**
+ * The polymorphic gallery treats selection as `string[]` of names. The kind is recoverable from
+ * the filename extension since the backend names images with `.png` and videos with `.mp4` (see
+ * SimpleNameService). Centralizing the discriminator here so callers don't have to know about
+ * the extension contract.
+ */
+export const isVideoName = (name: string): boolean => name.toLowerCase().endsWith('.mp4');
diff --git a/invokeai/frontend/web/src/features/imageActions/actions.ts b/invokeai/frontend/web/src/features/imageActions/actions.ts
index 2c9293127b4..8b454e4af01 100644
--- a/invokeai/frontend/web/src/features/imageActions/actions.ts
+++ b/invokeai/frontend/web/src/features/imageActions/actions.ts
@@ -35,14 +35,15 @@ import {
import { calculateNewSize } from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
import { imageToCompareChanged, selectionChanged } from 'features/gallery/store/gallerySlice';
import type { BoardId } from 'features/gallery/store/types';
-import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
+import { fieldImageValueChanged, fieldVideoValueChanged } from 'features/nodes/store/nodesSlice';
import type { FieldIdentifier } from 'features/nodes/types/field';
import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { WORKSPACE_PANEL_ID } from 'features/ui/layouts/shared';
import { imageDTOToFile, imagesApi, uploadImage } from 'services/api/endpoints/images';
-import type { ImageDTO } from 'services/api/types';
+import { videosApi } from 'services/api/endpoints/videos';
+import type { ImageDTO, VideoDTO } from 'services/api/types';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
@@ -75,6 +76,15 @@ export const setNodeImageFieldImage = (arg: {
dispatch(fieldImageValueChanged({ ...fieldIdentifier, value: imageDTO }));
};
+export const setNodeVideoFieldVideo = (arg: {
+ videoDTO: VideoDTO;
+ fieldIdentifier: FieldIdentifier;
+ dispatch: AppDispatch;
+}) => {
+ const { videoDTO, fieldIdentifier, dispatch } = arg;
+ dispatch(fieldVideoValueChanged({ ...fieldIdentifier, value: videoDTO }));
+};
+
export const setComparisonImage = (arg: { image_name: string; dispatch: AppDispatch }) => {
const { image_name, dispatch } = arg;
dispatch(imageToCompareChanged(image_name));
@@ -323,3 +333,40 @@ export const removeImagesFromBoard = (arg: { image_names: string[]; dispatch: Ap
dispatch(imagesApi.endpoints.removeImagesFromBoard.initiate({ image_names }, { track: false }));
dispatch(selectionChanged([]));
};
+
+// Single-video counterparts to addImagesToBoard / removeImagesFromBoard. The video router
+// only exposes single-video endpoints today (POST/DELETE /api/v1/videos/board), so the
+// callers loop per video. Backend permissions: add requires _assert_board_write_access
+// (admin/owner/public dest) AND _assert_video_direct_owner; remove requires
+// _assert_video_direct_owner plus write access on the *source* board.
+export const addVideoToBoard = (arg: { video_name: string; boardId: BoardId; dispatch: AppDispatch }) => {
+ const { video_name, boardId, dispatch } = arg;
+ dispatch(videosApi.endpoints.addVideoToBoard.initiate({ video_name, board_id: boardId }, { track: false }));
+ dispatch(selectionChanged([]));
+};
+
+export const removeVideoFromBoard = (arg: { video_name: string; dispatch: AppDispatch }) => {
+ const { video_name, dispatch } = arg;
+ dispatch(videosApi.endpoints.removeVideoFromBoard.initiate({ video_name }, { track: false }));
+ dispatch(selectionChanged([]));
+};
+
+// Bulk helpers. No batch endpoint exists for the video router yet, so we fan out per-video over
+// the existing singular mutation — same pattern the change-board modal already uses. Callers
+// (drag-and-drop bulk move-to-board, future bulk actions) get to share the selection-clear and
+// keep their wiring symmetrical with the image-side helpers.
+export const addVideosToBoard = (arg: { video_names: string[]; boardId: BoardId; dispatch: AppDispatch }) => {
+ const { video_names, boardId, dispatch } = arg;
+ for (const video_name of video_names) {
+ dispatch(videosApi.endpoints.addVideoToBoard.initiate({ video_name, board_id: boardId }, { track: false }));
+ }
+ dispatch(selectionChanged([]));
+};
+
+export const removeVideosFromBoard = (arg: { video_names: string[]; dispatch: AppDispatch }) => {
+ const { video_names, dispatch } = arg;
+ for (const video_name of video_names) {
+ dispatch(videosApi.endpoints.removeVideoFromBoard.initiate({ video_name }, { track: false }));
+ }
+ dispatch(selectionChanged([]));
+};
diff --git a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx
index 2d043c9c816..5394bb67a65 100644
--- a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx
+++ b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx
@@ -44,6 +44,25 @@ const LoRASelect = () => {
) {
return model.variant === currentMainModelConfig.variant;
}
+ // For Wan: A14B (t2v_a14b/i2v_a14b) and TI2V-5B have different inner
+ // dims (5120 vs 3072) — applying the wrong variant crashes the layer
+ // patcher. LoRAs whose variant couldn't be detected (null) are kept
+ // so we don't silently hide ambiguous ones.
+ if (
+ currentMainModelConfig?.base === 'wan' &&
+ 'variant' in currentMainModelConfig &&
+ currentMainModelConfig.variant &&
+ 'variant' in model &&
+ model.variant
+ ) {
+ const expected =
+ currentMainModelConfig.variant === 't2v_a14b' || currentMainModelConfig.variant === 'i2v_a14b'
+ ? 'a14b'
+ : currentMainModelConfig.variant === 'ti2v_5b'
+ ? '5b'
+ : null;
+ return expected === null || model.variant === expected;
+ }
return true;
});
}, [modelConfigs, currentBaseModel, currentMainModelConfig]);
diff --git a/invokeai/frontend/web/src/features/metadata/parsing.tsx b/invokeai/frontend/web/src/features/metadata/parsing.tsx
index bc2623045e1..793e595303b 100644
--- a/invokeai/frontend/web/src/features/metadata/parsing.tsx
+++ b/invokeai/frontend/web/src/features/metadata/parsing.tsx
@@ -57,6 +57,11 @@ import {
setZImageSeedVarianceStrength,
setZImageShift,
vaeSelected,
+ wanComponentSourceSelected,
+ wanGuidanceScaleLowNoiseChanged,
+ wanT5EncoderModelSelected,
+ wanTransformerLowNoiseSelected,
+ wanVaeModelSelected,
widthChanged,
zImageQwen3EncoderModelSelected,
zImageQwen3SourceModelSelected,
@@ -858,6 +863,133 @@ const QwenImageShift: SingleMetadataHandler = {
};
//#endregion QwenImageShift
+//#region WanTransformerLowNoise
+const WanTransformerLowNoise: SingleMetadataHandler = {
+ [SingleMetadataKey]: true,
+ type: 'WanTransformerLowNoise',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'wan_transformer_low_noise');
+ // Reject when the key is absent so the handler is not rendered for non-Wan images
+ if (raw === undefined) {
+ return Promise.reject();
+ }
+ if (raw === null) {
+ return Promise.resolve(null);
+ }
+ return Promise.resolve(zModelIdentifierField.parse(raw));
+ },
+ recall: (value, store) => {
+ store.dispatch(wanTransformerLowNoiseSelected(value));
+ },
+ i18nKey: 'modelManager.wanTransformerLowNoise',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps) => (
+
+ ),
+};
+//#endregion WanTransformerLowNoise
+
+//#region WanComponentSource
+const WanComponentSource: SingleMetadataHandler = {
+ [SingleMetadataKey]: true,
+ type: 'WanComponentSource',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'wan_component_source');
+ if (raw === undefined) {
+ return Promise.reject();
+ }
+ if (raw === null) {
+ return Promise.resolve(null);
+ }
+ return Promise.resolve(zModelIdentifierField.parse(raw));
+ },
+ recall: (value, store) => {
+ store.dispatch(wanComponentSourceSelected(value));
+ },
+ i18nKey: 'modelManager.wanComponentSource',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps) => (
+
+ ),
+};
+//#endregion WanComponentSource
+
+//#region WanVaeModel
+const WanVaeModel: SingleMetadataHandler = {
+ [SingleMetadataKey]: true,
+ type: 'WanVaeModel',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'wan_vae_model');
+ if (raw === undefined) {
+ return Promise.reject();
+ }
+ if (raw === null) {
+ return Promise.resolve(null);
+ }
+ return Promise.resolve(zModelIdentifierField.parse(raw));
+ },
+ recall: (value, store) => {
+ store.dispatch(wanVaeModelSelected(value));
+ },
+ i18nKey: 'modelManager.wanVae',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps) => (
+
+ ),
+};
+//#endregion WanVaeModel
+
+//#region WanT5EncoderModel
+const WanT5EncoderModel: SingleMetadataHandler = {
+ [SingleMetadataKey]: true,
+ type: 'WanT5EncoderModel',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'wan_t5_encoder_model');
+ if (raw === undefined) {
+ return Promise.reject();
+ }
+ if (raw === null) {
+ return Promise.resolve(null);
+ }
+ return Promise.resolve(zModelIdentifierField.parse(raw));
+ },
+ recall: (value, store) => {
+ store.dispatch(wanT5EncoderModelSelected(value));
+ },
+ i18nKey: 'modelManager.wanT5Encoder',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps) => (
+
+ ),
+};
+//#endregion WanT5EncoderModel
+
+//#region WanGuidanceScaleLowNoise
+const WanGuidanceScaleLowNoise: SingleMetadataHandler = {
+ [SingleMetadataKey]: true,
+ type: 'WanGuidanceScaleLowNoise',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'wan_guidance_scale_low_noise');
+ if (raw === undefined) {
+ return Promise.reject();
+ }
+ if (raw === null) {
+ return Promise.resolve(null);
+ }
+ const parsed = z.number().parse(raw);
+ return Promise.resolve(parsed);
+ },
+ recall: (value, store) => {
+ store.dispatch(wanGuidanceScaleLowNoiseChanged(value));
+ },
+ i18nKey: 'parameters.wanGuidanceScaleLowNoise',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps) => (
+
+ ),
+};
+//#endregion WanGuidanceScaleLowNoise
+
//#region ZImageShift
const ZImageShift: SingleMetadataHandler = {
[SingleMetadataKey]: true,
@@ -1654,6 +1786,11 @@ export const ImageMetadataHandlers = {
QwenImageQwenVLEncoderModel,
QwenImageQuantization,
QwenImageShift,
+ WanTransformerLowNoise,
+ WanComponentSource,
+ WanVaeModel,
+ WanT5EncoderModel,
+ WanGuidanceScaleLowNoise,
ZImageShift,
LoRAs,
CanvasLayers,
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/models.ts b/invokeai/frontend/web/src/features/modelManagerV2/models.ts
index cf295c9af6a..48db55da321 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/models.ts
+++ b/invokeai/frontend/web/src/features/modelManagerV2/models.ts
@@ -23,6 +23,7 @@ import {
isTIModelConfig,
isUnknownModelConfig,
isVAEModelConfig,
+ isWanT5EncoderModelConfig,
} from 'services/api/types';
import { objectEntries } from 'tsafe';
@@ -85,6 +86,11 @@ const MODEL_CATEGORIES: Record = {
i18nKey: 'modelManager.qwenVLEncoder',
filter: isQwenVLEncoderModelConfig,
},
+ wan_t5_encoder: {
+ category: 'wan_t5_encoder',
+ i18nKey: 'modelManager.wanT5Encoder',
+ filter: isWanT5EncoderModelConfig,
+ },
control_lora: {
category: 'control_lora',
i18nKey: 'modelManager.controlLora',
@@ -165,6 +171,7 @@ export const MODEL_BASE_TO_COLOR: Record = {
'z-image': 'cyan',
external: 'orange',
anima: 'invokePurple',
+ wan: 'cyan',
unknown: 'red',
};
@@ -187,6 +194,7 @@ export const MODEL_TYPE_TO_LONG_NAME: Record = {
t5_encoder: 'T5 Encoder',
qwen3_encoder: 'Qwen3 Encoder',
qwen_vl_encoder: 'Qwen2.5-VL Encoder',
+ wan_t5_encoder: 'Wan T5 Encoder',
clip_embed: 'CLIP Embed',
siglip: 'SigLIP',
flux_redux: 'FLUX Redux',
@@ -212,6 +220,7 @@ export const MODEL_BASE_TO_LONG_NAME: Record = {
'z-image': 'Z-Image',
external: 'External',
anima: 'Anima',
+ wan: 'Wan 2.2',
unknown: 'Unknown',
};
@@ -232,6 +241,7 @@ export const MODEL_BASE_TO_SHORT_NAME: Record = {
'z-image': 'Z-Image',
external: 'External',
anima: 'Anima',
+ wan: 'Wan',
unknown: 'Unknown',
};
@@ -252,6 +262,11 @@ export const MODEL_VARIANT_TO_LONG_NAME: Record = {
gigantic: 'CLIP G',
generate: 'Qwen Image',
edit: 'Qwen Image Edit',
+ t2v_a14b: 'Wan 2.2 T2V A14B',
+ i2v_a14b: 'Wan 2.2 I2V A14B',
+ ti2v_5b: 'Wan 2.2 TI2V 5B',
+ a14b: 'Wan 2.2 A14B LoRA',
+ '5b': 'Wan 2.2 5B LoRA',
qwen3_4b: 'Qwen3 4B',
qwen3_8b: 'Qwen3 8B',
qwen3_06b: 'Qwen3 0.6B',
@@ -271,6 +286,7 @@ export const MODEL_FORMAT_TO_LONG_NAME: Record = {
t5_encoder: 'T5 Encoder',
qwen3_encoder: 'Qwen3 Encoder',
qwen_vl_encoder: 'Qwen2.5-VL Encoder',
+ wan_t5_encoder: 'Wan T5 Encoder (UMT5-XXL)',
bnb_quantized_int8b: 'BNB Quantized (int8b)',
bnb_quantized_nf4b: 'BNB Quantized (nf4b)',
gguf_quantized: 'GGUF Quantized',
@@ -279,7 +295,7 @@ export const MODEL_FORMAT_TO_LONG_NAME: Record = {
export const SUPPORTS_OPTIMIZED_DENOISING_BASE_MODELS: BaseModelType[] = ['flux', 'sd-3'];
-export const SUPPORTS_REF_IMAGES_BASE_MODELS: BaseModelType[] = ['sd-1', 'sdxl', 'flux', 'flux2', 'qwen-image'];
+export const SUPPORTS_REF_IMAGES_BASE_MODELS: BaseModelType[] = ['sd-1', 'sdxl', 'flux', 'flux2', 'qwen-image', 'wan'];
export const SUPPORTS_NEGATIVE_PROMPT_BASE_MODELS: BaseModelType[] = [
'sd-1',
@@ -290,4 +306,5 @@ export const SUPPORTS_NEGATIVE_PROMPT_BASE_MODELS: BaseModelType[] = [
'sd-3',
'z-image',
'anima',
+ 'wan',
];
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx
index 71d2efe0e45..1473e6dd076 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx
@@ -16,6 +16,7 @@ const FORMAT_NAME_MAP: Record = {
t5_encoder: 't5_encoder',
qwen3_encoder: 'qwen3_encoder',
qwen_vl_encoder: 'qwen_vl_encoder',
+ wan_t5_encoder: 'wan_t5_encoder',
bnb_quantized_int8b: 'bnb_quantized_int8b',
bnb_quantized_nf4b: 'quantized',
gguf_quantized: 'gguf',
@@ -37,6 +38,7 @@ const FORMAT_COLOR_MAP: Record = {
t5_encoder: 'base',
qwen3_encoder: 'base',
qwen_vl_encoder: 'base',
+ wan_t5_encoder: 'base',
bnb_quantized_int8b: 'base',
bnb_quantized_nf4b: 'base',
gguf_quantized: 'base',
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx
index 929d16285df..ffd7388070f 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx
@@ -1,7 +1,7 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { Box, IconButton, Image } from '@invoke-ai/ui-library';
-import { dropzoneAccept } from 'common/hooks/useImageUploadButton';
import { typedMemo } from 'common/util/typedMemo';
+import { imageDropzoneAccept } from 'common/util/uploadMediaAccept';
import { toast } from 'features/toast/toast';
import { useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';
@@ -96,7 +96,7 @@ const ModelImageUpload = ({ model_key, model_image }: Props) => {
}, [model_key, t, deleteModelImage]);
const { getInputProps, getRootProps } = useDropzone({
- accept: dropzoneAccept,
+ accept: imageDropzoneAccept,
onDropAccepted,
noDrag: true,
multiple: false,
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx
index c8423a8fe4e..20ee4b21c04 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx
@@ -6,6 +6,7 @@ import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { DndImage } from 'features/dnd/DndImage';
import NextPrevItemButtons from 'features/gallery/components/NextPrevItemButtons';
import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
+import { isVideoName } from 'features/gallery/store/types';
import NonInvocationNodeWrapper from 'features/nodes/components/flow/nodes/common/NonInvocationNodeWrapper';
import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants';
import type { AnimationProps } from 'framer-motion';
@@ -19,7 +20,12 @@ import { $lastProgressEvent } from 'services/events/stores';
const CurrentImageNode = (props: NodeProps) => {
const lastSelectedItem = useAppSelector(selectLastSelectedItem);
const lastProgressEvent = useStore($lastProgressEvent);
- const imageDTO = useImageDTO(lastSelectedItem);
+ // Pass a real name only when the selection is an image. Videos use the polymorphic
+ // gallery and would otherwise trigger GET /api/v1/images/i/.mp4 — which 404s
+ // and emits a noisy "Image record not found" backend log every time a video is
+ // clicked in the gallery while a Current Image node is in the workflow.
+ const imageName = lastSelectedItem && !isVideoName(lastSelectedItem) ? lastSelectedItem : null;
+ const imageDTO = useImageDTO(imageName);
if (lastProgressEvent?.image) {
return (
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx
index 890666b0c4e..e025fe39fe2 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx
@@ -1,7 +1,7 @@
import type { ChakraProps } from '@invoke-ai/ui-library';
import { Flex, FormControlGroup } from '@invoke-ai/ui-library';
import { useIsExecutableNode } from 'features/nodes/hooks/useIsBatchNode';
-import { useNodeHasImageOutput } from 'features/nodes/hooks/useNodeHasImageOutput';
+import { useNodeHasGalleryOutput } from 'features/nodes/hooks/useNodeHasGalleryOutput';
import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants';
import { memo } from 'react';
@@ -15,7 +15,7 @@ type Props = {
const props: ChakraProps = { w: 'unset' };
const InvocationNodeFooter = ({ nodeId }: Props) => {
- const hasImageOutput = useNodeHasImageOutput();
+ const hasGalleryOutput = useNodeHasGalleryOutput();
const isExecutableNode = useIsExecutableNode();
return (
{
>
{isExecutableNode && }
- {isExecutableNode && hasImageOutput && }
+ {isExecutableNode && hasGalleryOutput && }
);
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx
index 7d05ba63da6..4e9912a87be 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx
@@ -1,6 +1,6 @@
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
-import { useNodeHasImageOutput } from 'features/nodes/hooks/useNodeHasImageOutput';
+import { useNodeHasGalleryOutput } from 'features/nodes/hooks/useNodeHasGalleryOutput';
import { useNodeIsIntermediate } from 'features/nodes/hooks/useNodeIsIntermediate';
import { nodeIsIntermediateChanged } from 'features/nodes/store/nodesSlice';
import { NO_PAN_CLASS } from 'features/nodes/types/constants';
@@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
const SaveToGalleryCheckbox = ({ nodeId }: { nodeId: string }) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
- const hasImageOutput = useNodeHasImageOutput();
+ const hasGalleryOutput = useNodeHasGalleryOutput();
const isIntermediate = useNodeIsIntermediate();
const handleChange = useCallback(
(e: ChangeEvent) => {
@@ -25,7 +25,7 @@ const SaveToGalleryCheckbox = ({ nodeId }: { nodeId: string }) => {
[dispatch, nodeId]
);
- if (!hasImageOutput) {
+ if (!hasGalleryOutput) {
return null;
}
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx
index 053f8a09985..d73a840f6bc 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx
@@ -15,6 +15,7 @@ import { StringGeneratorFieldInputComponent } from 'features/nodes/components/fl
import { IntegerFieldInput } from 'features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInput';
import { IntegerFieldInputAndSlider } from 'features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInputAndSlider';
import { IntegerFieldSlider } from 'features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldSlider';
+import { VideoFrameIndexFieldInput } from 'features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput';
import { StringFieldDropdown } from 'features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldDropdown';
import { StringFieldInput } from 'features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldInput';
import { StringFieldTextarea } from 'features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldTextarea';
@@ -63,6 +64,8 @@ import {
isStringGeneratorFieldInputTemplate,
isStylePresetFieldInputInstance,
isStylePresetFieldInputTemplate,
+ isVideoFieldInputInstance,
+ isVideoFieldInputTemplate,
} from 'features/nodes/types/field';
import type { NodeFieldElement } from 'features/nodes/types/workflow';
import { memo } from 'react';
@@ -76,6 +79,7 @@ import EnumFieldInputComponent from './inputs/EnumFieldInputComponent';
import ImageFieldInputComponent from './inputs/ImageFieldInputComponent';
import SchedulerFieldInputComponent from './inputs/SchedulerFieldInputComponent';
import StylePresetFieldInputComponent from './inputs/StylePresetFieldInputComponent';
+import VideoFieldInputComponent from './inputs/VideoFieldInputComponent';
type Props = {
nodeId: string;
@@ -137,6 +141,13 @@ export const InputFieldRenderer = memo(({ nodeId, fieldName, settings }: Props)
if (!isIntegerFieldInputInstance(field)) {
return null;
}
+ // The ``video-frame-index`` ui_component bolts a frame thumbnail + scrubber slider
+ // onto the standard integer input. It's a per-field widget rather than a node-body
+ // widget so it works in both the workflow editor and the Form Builder (both of
+ // which dispatch through this same renderer).
+ if (template.ui_component === 'video-frame-index') {
+ return ;
+ }
if (!settings || settings.type !== 'integer-field-config') {
return ;
}
@@ -215,6 +226,13 @@ export const InputFieldRenderer = memo(({ nodeId, fieldName, settings }: Props)
return ;
}
+ if (isVideoFieldInputTemplate(template)) {
+ if (!isVideoFieldInputInstance(field)) {
+ return null;
+ }
+ return ;
+ }
+
if (isBoardFieldInputTemplate(template)) {
if (!isBoardFieldInputInstance(field)) {
return null;
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput.tsx
new file mode 100644
index 00000000000..4b9e7796b6f
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput.tsx
@@ -0,0 +1,199 @@
+import { CompositeNumberInput, CompositeSlider, Flex, Text } from '@invoke-ai/ui-library';
+import { createSelector } from '@reduxjs/toolkit';
+import { skipToken } from '@reduxjs/toolkit/query';
+import { useAppSelector } from 'app/store/storeHooks';
+import type { FieldComponentProps } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/types';
+import { useIntegerField } from 'features/nodes/components/flow/nodes/Invocation/fields/IntegerField/useIntegerField';
+import { selectFieldInputInstanceSafe, selectNodesSlice } from 'features/nodes/store/selectors';
+import { NO_DRAG_CLASS } from 'features/nodes/types/constants';
+import type { IntegerFieldInputInstance, IntegerFieldInputTemplate } from 'features/nodes/types/field';
+import { isVideoFieldInputInstance } from 'features/nodes/types/field';
+import { memo, useEffect, useMemo, useRef } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useGetVideoDTOQuery } from 'services/api/endpoints/videos';
+
+/**
+ * Integer-field renderer used by ``start_frame`` and ``end_frame`` on the
+ * ``extract_video_range`` invocation. Renders the standard number input plus a
+ * live frame thumbnail and a scrubber slider, all writing to the same Redux
+ * integer field. The thumbnail is a ```` element seeked to
+ * ``frame / fps``; browsers display the frame natively without a canvas
+ * roundtrip.
+ *
+ * The widget looks up its companion ``VideoField`` directly via Redux on the
+ * same node, so it works wherever ``InputFieldRenderer`` is used — the
+ * workflow editor's node body AND the Form Builder's view/edit modes.
+ *
+ * Convention: the source video is expected to live on a sibling field named
+ * ``video`` on the same node. If that field is missing or unset, the widget
+ * gracefully falls back to a plain number input.
+ */
+const COMPANION_VIDEO_FIELD_NAME = 'video';
+
+export const VideoFrameIndexFieldInput = memo(
+ (props: FieldComponentProps) => {
+ const { t } = useTranslation();
+ const { nodeId, field, fieldTemplate } = props;
+ const { defaultValue, onChange, min, max, step, fineStep, constrainValue } = useIntegerField(
+ nodeId,
+ field.name,
+ fieldTemplate
+ );
+
+ const selectVideoName = useMemo(
+ () =>
+ createSelector(selectNodesSlice, (nodes) => {
+ const sibling = selectFieldInputInstanceSafe(nodes, nodeId, COMPANION_VIDEO_FIELD_NAME);
+ if (!sibling || !isVideoFieldInputInstance(sibling)) {
+ return undefined;
+ }
+ return sibling.value?.video_name;
+ }),
+ [nodeId]
+ );
+ const videoName = useAppSelector(selectVideoName);
+ const { currentData: videoDTO } = useGetVideoDTOQuery(videoName ?? skipToken);
+
+ // Frame count is the slider's upper bound. duration*fps can be off-by-one for VFR
+ // containers, but that's tolerable here — the slider is for visual scrubbing, not
+ // for the authoritative range check (which the backend re-validates at invoke time).
+ const fps = videoDTO?.fps ?? null;
+ const frameCount =
+ videoDTO && fps && videoDTO.duration > 0 ? Math.max(1, Math.round(videoDTO.duration * fps)) : null;
+
+ // Resolve negative indices (e.g. -1 = last frame) for display only — the underlying
+ // field value is preserved verbatim so users can still type "-1" and have the
+ // backend resolve it at invoke time against the authoritative decoder frame count.
+ const resolvedIndex = useMemo(() => {
+ if (frameCount === null || field.value === undefined) {
+ return 0;
+ }
+ const candidate = field.value < 0 ? frameCount + field.value : field.value;
+ if (Number.isNaN(candidate)) {
+ return 0;
+ }
+ return Math.max(0, Math.min(frameCount - 1, candidate));
+ }, [field.value, frameCount]);
+
+ return (
+
+
+ {videoDTO && frameCount && fps ? (
+
+ ) : (
+
+
+ {videoName ? t('nodes.extractVideoRange.missingFps') : t('nodes.extractVideoRange.dropVideoPrompt')}
+
+
+ )}
+
+ );
+ }
+);
+
+VideoFrameIndexFieldInput.displayName = 'VideoFrameIndexFieldInput';
+
+type FrameScrubberProps = {
+ videoUrl: string;
+ resolvedIndex: number;
+ fps: number;
+ frameCount: number;
+ onChange: (value: number) => void;
+};
+
+const FrameScrubber = memo(({ videoUrl, resolvedIndex, fps, frameCount, onChange }: FrameScrubberProps) => {
+ const videoRef = useRef(null);
+
+ // Seek the video element whenever the resolved index changes. We nudge currentTime
+ // by half a frame so the seek lands inside the frame's display window — some codecs
+ // decode the boundary as black on first paint without this offset.
+ useEffect(() => {
+ const el = videoRef.current;
+ if (!el) {
+ return;
+ }
+ const targetTime = (resolvedIndex + 0.5) / fps;
+ const setTime = () => {
+ try {
+ el.currentTime = targetTime;
+ } catch {
+ // Seeking before metadata is available throws on some browsers — the
+ // loadedmetadata listener below retries when the element is ready.
+ }
+ };
+ if (el.readyState >= 1) {
+ setTime();
+ } else {
+ el.addEventListener('loadedmetadata', setTime, { once: true });
+ return () => el.removeEventListener('loadedmetadata', setTime);
+ }
+ }, [resolvedIndex, fps, videoUrl]);
+
+ return (
+
+
+
+
+ {`${resolvedIndex} / ${frameCount - 1}`}
+
+
+
+
+ );
+});
+
+FrameScrubber.displayName = 'FrameScrubber';
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VideoFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VideoFieldInputComponent.tsx
new file mode 100644
index 00000000000..c3e4d85958b
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VideoFieldInputComponent.tsx
@@ -0,0 +1,101 @@
+import { Flex, Image, Text } from '@invoke-ai/ui-library';
+import { useStore } from '@nanostores/react';
+import { skipToken } from '@reduxjs/toolkit/query';
+import { useAppDispatch } from 'app/store/storeHooks';
+import type { SetNodeVideoFieldVideoDndTargetData } from 'features/dnd/dnd';
+import { setNodeVideoFieldVideoDndTarget } from 'features/dnd/dnd';
+import { DndDropTarget } from 'features/dnd/DndDropTarget';
+import { fieldVideoValueChanged } from 'features/nodes/store/nodesSlice';
+import { NO_DRAG_CLASS } from 'features/nodes/types/constants';
+import type { VideoFieldInputInstance, VideoFieldInputTemplate } from 'features/nodes/types/field';
+import { memo, useCallback, useEffect, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useGetVideoDTOQuery } from 'services/api/endpoints/videos';
+import { $isConnected } from 'services/events/stores';
+
+import type { FieldComponentProps } from './types';
+
+/**
+ * Counterpart to ImageFieldInputComponent for VideoField inputs. Shows the video's WebP
+ * thumbnail (the first decoded frame at the gallery-default size — same image the gallery
+ * grid uses), with a small dimensions badge in the corner. Users drop a video from the
+ * gallery onto the field; the drop is handled by setNodeVideoFieldVideoDndTarget.
+ */
+const VideoFieldInputComponent = (props: FieldComponentProps) => {
+ const { t } = useTranslation();
+ const { nodeId, field } = props;
+ const dispatch = useAppDispatch();
+ const isConnected = useStore($isConnected);
+
+ const { currentData: videoDTO, isError } = useGetVideoDTOQuery(field.value?.video_name ?? skipToken);
+
+ const handleReset = useCallback(() => {
+ dispatch(
+ fieldVideoValueChanged({
+ nodeId,
+ fieldName: field.name,
+ value: undefined,
+ })
+ );
+ }, [dispatch, field.name, nodeId]);
+
+ const dndTargetData = useMemo(
+ () => setNodeVideoFieldVideoDndTarget.getData({ fieldIdentifier: { nodeId, fieldName: field.name } }),
+ [field.name, nodeId]
+ );
+
+ // If the referenced video was deleted while disconnected, drop the stale reference once
+ // we reconnect — mirrors the image-field behavior.
+ useEffect(() => {
+ if (isConnected && isError) {
+ handleReset();
+ }
+ }, [handleReset, isConnected, isError]);
+
+ return (
+
+ {!videoDTO && (
+
+
+ {t('gallery.drop')}
+
+
+ )}
+ {videoDTO && (
+ <>
+
+
+
+ {`${videoDTO.width}x${videoDTO.height}`}
+ >
+ )}
+
+
+ );
+};
+
+export default memo(VideoFieldInputComponent);
diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx
index b902c75c726..17e78bc019d 100644
--- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx
@@ -1,6 +1,6 @@
import { Box, Button, Flex, Icon, IconButton, Image, Tooltip } from '@invoke-ai/ui-library';
-import { dropzoneAccept } from 'common/hooks/useImageUploadButton';
import { convertImageUrlToBlob } from 'common/util/convertImageUrlToBlob';
+import { imageDropzoneAccept } from 'common/util/uploadMediaAccept';
import { useCallback, useEffect, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { useTranslation } from 'react-i18next';
@@ -67,7 +67,7 @@ export const WorkflowThumbnailField = ({
}, [onChange]);
const { getInputProps, getRootProps } = useDropzone({
- accept: dropzoneAccept,
+ accept: imageDropzoneAccept,
onDropAccepted,
noDrag: true,
multiple: false,
diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeHasGalleryOutput.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeHasGalleryOutput.ts
new file mode 100644
index 00000000000..d1e028d2b5f
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeHasGalleryOutput.ts
@@ -0,0 +1,27 @@
+import { some } from 'es-toolkit/compat';
+import { useMemo } from 'react';
+
+import { useNodeTemplateSafe } from './useNodeTemplateSafe';
+
+/**
+ * True when the node produces an output that lands in the gallery — currently ImageField or
+ * VideoField. Used to gate the "Save in gallery" checkbox and the footer that contains it.
+ *
+ * The `image` and `video` primitive nodes are excluded because they pass through an existing
+ * asset without saving a new copy.
+ */
+export const useNodeHasGalleryOutput = (): boolean => {
+ const template = useNodeTemplateSafe();
+ const hasGalleryOutput = useMemo(
+ () =>
+ some(
+ template?.outputs,
+ (output) =>
+ (output.type.name === 'ImageField' && template?.type !== 'image') ||
+ (output.type.name === 'VideoField' && template?.type !== 'video')
+ ),
+ [template]
+ );
+
+ return hasGalleryOutput;
+};
diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeHasImageOutput.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeHasImageOutput.ts
deleted file mode 100644
index 523f48919fb..00000000000
--- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeHasImageOutput.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { some } from 'es-toolkit/compat';
-import { useMemo } from 'react';
-
-import { useNodeTemplateSafe } from './useNodeTemplateSafe';
-
-export const useNodeHasImageOutput = (): boolean => {
- const template = useNodeTemplateSafe();
- const hasImageOutput = useMemo(
- () =>
- some(
- template?.outputs,
- (output) =>
- output.type.name === 'ImageField' &&
- // the image primitive node (node type "image") does not actually save the image, do not show the image-saving checkboxes
- template?.type !== 'image'
- ),
- [template]
- );
-
- return hasImageOutput;
-};
diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useWithFooter.ts b/invokeai/frontend/web/src/features/nodes/hooks/useWithFooter.ts
index b140295801b..14383affd82 100644
--- a/invokeai/frontend/web/src/features/nodes/hooks/useWithFooter.ts
+++ b/invokeai/frontend/web/src/features/nodes/hooks/useWithFooter.ts
@@ -1,9 +1,9 @@
import { useIsExecutableNode } from 'features/nodes/hooks/useIsBatchNode';
-import { useNodeHasImageOutput } from './useNodeHasImageOutput';
+import { useNodeHasGalleryOutput } from './useNodeHasGalleryOutput';
export const useWithFooter = () => {
- const hasImageOutput = useNodeHasImageOutput();
+ const hasGalleryOutput = useNodeHasGalleryOutput();
const isExecutableNode = useIsExecutableNode();
- return isExecutableNode && hasImageOutput;
+ return isExecutableNode && hasGalleryOutput;
};
diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts
index 61511b46627..85c378d5f80 100644
--- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts
+++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts
@@ -50,6 +50,7 @@ import type {
StringFieldValue,
StringGeneratorFieldValue,
StylePresetFieldValue,
+ VideoFieldValue,
} from 'features/nodes/types/field';
import {
zBoardFieldValue,
@@ -73,6 +74,7 @@ import {
zStringFieldValue,
zStringGeneratorFieldValue,
zStylePresetFieldValue,
+ zVideoFieldValue,
} from 'features/nodes/types/field';
import type { AnyEdge, AnyNode, ConnectorNode } from 'features/nodes/types/invocation';
import { isConnectorNode, isInvocationNode, isNotesNode } from 'features/nodes/types/invocation';
@@ -586,6 +588,9 @@ const slice = createSlice({
fieldImageCollectionValueChanged: (state, action: FieldValueAction) => {
fieldValueReducer(state, action, zImageFieldCollectionValue);
},
+ fieldVideoValueChanged: (state, action: FieldValueAction) => {
+ fieldValueReducer(state, action, zVideoFieldValue);
+ },
fieldLoRACollectionValueChanged: (state, action: FieldValueAction) => {
fieldValueReducer(state, action, zLoRAFieldCollectionValue);
},
@@ -803,6 +808,7 @@ export const {
fieldEnumModelValueChanged,
fieldImageValueChanged,
fieldImageCollectionValueChanged,
+ fieldVideoValueChanged,
fieldLabelChanged,
fieldLoRACollectionValueChanged,
fieldModelIdentifierValueChanged,
diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowLibrarySlice.ts b/invokeai/frontend/web/src/features/nodes/store/workflowLibrarySlice.ts
index 1d5d8554aeb..3c53bb611b0 100644
--- a/invokeai/frontend/web/src/features/nodes/store/workflowLibrarySlice.ts
+++ b/invokeai/frontend/web/src/features/nodes/store/workflowLibrarySlice.ts
@@ -114,7 +114,14 @@ export const WORKFLOW_LIBRARY_TAG_CATEGORIES: WorkflowTagCategory[] = [
},
{
categoryTKey: 'Common Tasks',
- tags: [{ label: 'Upscaling' }, { label: 'Text to Image' }, { label: 'Image to Image' }],
+ tags: [
+ { label: 'Upscaling' },
+ { label: 'Text to Image' },
+ { label: 'Image to Image' },
+ { label: 'Text to Video' },
+ { label: 'Image to Video' },
+ { label: 'Video to Video' },
+ ],
},
{
categoryTKey: 'Model Architecture',
diff --git a/invokeai/frontend/web/src/features/nodes/types/common.ts b/invokeai/frontend/web/src/features/nodes/types/common.ts
index fb2a1ce946a..f23c34ab170 100644
--- a/invokeai/frontend/web/src/features/nodes/types/common.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/common.ts
@@ -11,6 +11,12 @@ type ImageFieldCollection = z.infer;
export const isImageFieldCollection = (field: unknown): field is ImageFieldCollection =>
zImageFieldCollection.safeParse(field).success;
+export const zVideoField = z.object({
+ video_name: z.string().trim().min(1),
+});
+type VideoField = z.infer;
+export const isVideoField = (field: unknown): field is VideoField => zVideoField.safeParse(field).success;
+
export const zBoardField = z.object({
board_id: z.string().trim().min(1),
});
@@ -100,6 +106,7 @@ export const zBaseModelType = z.enum([
'z-image',
'external',
'anima',
+ 'wan',
'unknown',
]);
export type BaseModelType = z.infer;
@@ -114,6 +121,7 @@ export const zMainModelBase = z.enum([
'qwen-image',
'z-image',
'anima',
+ 'wan',
]);
type MainModelBase = z.infer;
export const isMainModelBase = (base: unknown): base is MainModelBase => zMainModelBase.safeParse(base).success;
@@ -134,6 +142,7 @@ export const zModelType = z.enum([
't5_encoder',
'qwen3_encoder',
'qwen_vl_encoder',
+ 'wan_t5_encoder',
'clip_embed',
'siglip',
'flux_redux',
@@ -144,6 +153,7 @@ export type ModelType = z.infer;
export const zSubModelType = z.enum([
'unet',
'transformer',
+ 'transformer_2',
'text_encoder',
'text_encoder_2',
'text_encoder_3',
@@ -163,6 +173,10 @@ export const zFluxVariantType = z.enum(['dev', 'dev_fill', 'schnell']);
export const zFlux2VariantType = z.enum(['klein_4b', 'klein_4b_base', 'klein_9b', 'klein_9b_base']);
export const zZImageVariantType = z.enum(['turbo', 'zbase']);
const zQwenImageVariantType = z.enum(['generate', 'edit']);
+const zWanVariantType = z.enum(['t2v_a14b', 'i2v_a14b', 'ti2v_5b']);
+/** Wan LoRA variant — identifies which model FAMILY (inner_dim) a LoRA
+ * targets. A14B = inner_dim 5120 (both T2V and I2V), 5B = inner_dim 3072. */
+const zWanLoRAVariantType = z.enum(['a14b', '5b']);
export const zQwen3VariantType = z.enum(['qwen3_4b', 'qwen3_8b', 'qwen3_06b']);
export const zAnyModelVariant = z.union([
zModelVariantType,
@@ -171,6 +185,8 @@ export const zAnyModelVariant = z.union([
zFlux2VariantType,
zZImageVariantType,
zQwenImageVariantType,
+ zWanVariantType,
+ zWanLoRAVariantType,
zQwen3VariantType,
]);
export type AnyModelVariant = z.infer;
@@ -187,6 +203,7 @@ export const zModelFormat = z.enum([
't5_encoder',
'qwen3_encoder',
'qwen_vl_encoder',
+ 'wan_t5_encoder',
'bnb_quantized_int8b',
'bnb_quantized_nf4b',
'gguf_quantized',
diff --git a/invokeai/frontend/web/src/features/nodes/types/constants.ts b/invokeai/frontend/web/src/features/nodes/types/constants.ts
index 9da499ab91c..7383629eb84 100644
--- a/invokeai/frontend/web/src/features/nodes/types/constants.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/constants.ts
@@ -57,6 +57,7 @@ export const FIELD_COLORS: { [key: string]: string } = {
CogView4MainModelField: 'teal.500',
ZImageMainModelField: 'teal.500',
AnimaMainModelField: 'teal.500',
+ WanMainModelField: 'teal.500',
SDXLMainModelField: 'teal.500',
SDXLRefinerModelField: 'teal.500',
SpandrelImageToImageModelField: 'teal.500',
diff --git a/invokeai/frontend/web/src/features/nodes/types/field.ts b/invokeai/frontend/web/src/features/nodes/types/field.ts
index e3829b0cc8d..8fabf408976 100644
--- a/invokeai/frontend/web/src/features/nodes/types/field.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/field.ts
@@ -20,6 +20,7 @@ import {
zModelType,
zSchedulerField,
zStylePresetField,
+ zVideoField,
} from './common';
/**
@@ -50,7 +51,7 @@ import {
// #region Base schemas & misc
const zFieldInput = z.enum(['connection', 'direct', 'any']);
-const zFieldUIComponent = z.enum(['none', 'textarea', 'slider']);
+const zFieldUIComponent = z.enum(['none', 'textarea', 'slider', 'video-frame-index']);
const zFieldInputInstanceBase = z.object({
name: z.string().trim().min(1),
label: z.string().catch(''),
@@ -156,6 +157,10 @@ const zImageFieldType = zFieldTypeBase.extend({
name: z.literal('ImageField'),
originalType: zStatelessFieldType.optional(),
});
+const zVideoFieldType = zFieldTypeBase.extend({
+ name: z.literal('VideoField'),
+ originalType: zStatelessFieldType.optional(),
+});
const zImageCollectionFieldType = zFieldTypeBase.extend({
name: z.literal('ImageField'),
cardinality: z.literal(COLLECTION),
@@ -219,6 +224,7 @@ const zStatefulFieldType = z.union([
zBooleanFieldType,
zEnumFieldType,
zImageFieldType,
+ zVideoFieldType,
zBoardFieldType,
zStylePresetFieldType,
zModelIdentifierFieldType,
@@ -546,6 +552,26 @@ export const isEnumFieldInputInstance = buildInstanceTypeGuard(zEnumFieldInputIn
export const isEnumFieldInputTemplate = buildTemplateTypeGuard('EnumField');
// #endregion
+// #region VideoField
+export const zVideoFieldValue = zVideoField.optional();
+const zVideoFieldInputInstance = zFieldInputInstanceBase.extend({
+ value: zVideoFieldValue,
+});
+const zVideoFieldInputTemplate = zFieldInputTemplateBase.extend({
+ type: zVideoFieldType,
+ originalType: zFieldType.optional(),
+ default: zVideoFieldValue,
+});
+const zVideoFieldOutputTemplate = zFieldOutputTemplateBase.extend({
+ type: zVideoFieldType,
+});
+export type VideoFieldValue = z.infer;
+export type VideoFieldInputInstance = z.infer;
+export type VideoFieldInputTemplate = z.infer;
+export const isVideoFieldInputInstance = buildInstanceTypeGuard(zVideoFieldInputInstance);
+export const isVideoFieldInputTemplate = buildTemplateTypeGuard('VideoField', ['SINGLE']);
+// #endregion
+
// #region ImageField
export const zImageFieldValue = zImageField.optional();
const zImageFieldInputInstance = zFieldInputInstanceBase.extend({
@@ -1367,6 +1393,7 @@ export const zStatefulFieldValue = z.union([
zEnumFieldValue,
zImageFieldValue,
zImageFieldCollectionValue,
+ zVideoFieldValue,
zBoardFieldValue,
zStylePresetFieldValue,
zModelIdentifierFieldValue,
@@ -1397,6 +1424,7 @@ const zStatefulFieldInputInstance = z.union([
zEnumFieldInputInstance,
zImageFieldInputInstance,
zImageFieldCollectionInputInstance,
+ zVideoFieldInputInstance,
zBoardFieldInputInstance,
zStylePresetFieldInputInstance,
zModelIdentifierFieldInputInstance,
@@ -1426,6 +1454,7 @@ const zStatefulFieldInputTemplate = z.union([
zEnumFieldInputTemplate,
zImageFieldInputTemplate,
zImageFieldCollectionInputTemplate,
+ zVideoFieldInputTemplate,
zBoardFieldInputTemplate,
zStylePresetFieldInputTemplate,
zModelIdentifierFieldInputTemplate,
@@ -1455,6 +1484,7 @@ const zStatefulFieldOutputTemplate = z.union([
zEnumFieldOutputTemplate,
zImageFieldOutputTemplate,
zImageFieldCollectionOutputTemplate,
+ zVideoFieldOutputTemplate,
zBoardFieldOutputTemplate,
zStylePresetFieldOutputTemplate,
zModelIdentifierFieldOutputTemplate,
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts
index f17ff970f27..103c139b723 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts
@@ -30,6 +30,7 @@ type AddImageToImageArg = {
| 'qwen_image_i2l'
| 'z_image_i2l'
| 'anima_i2l'
+ | 'wan_i2l'
>;
noise?: Invocation<'noise'>;
denoise: Invocation;
@@ -56,6 +57,7 @@ export const addImageToImage = async ({
| 'qwen_image_l2i'
| 'z_image_l2i'
| 'anima_l2i'
+ | 'wan_l2i'
>
> => {
const { denoising_start, denoising_end } = getDenoisingStartAndEnd(state);
@@ -71,7 +73,8 @@ export const addImageToImage = async ({
denoise.type === 'flux2_denoise' ||
denoise.type === 'sd3_denoise' ||
denoise.type === 'z_image_denoise' ||
- denoise.type === 'anima_denoise'
+ denoise.type === 'anima_denoise' ||
+ denoise.type === 'wan_denoise'
) {
denoise.width = scaledSize.width;
denoise.height = scaledSize.height;
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts
index 51ac7c8d6b3..2f47577366b 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts
@@ -33,6 +33,7 @@ type AddInpaintArg = {
| 'qwen_image_i2l'
| 'z_image_i2l'
| 'anima_i2l'
+ | 'wan_i2l'
>;
noise?: Invocation<'noise'>;
denoise: Invocation;
@@ -72,7 +73,8 @@ export const addInpaint = async ({
denoise.type === 'flux2_denoise' ||
denoise.type === 'sd3_denoise' ||
denoise.type === 'z_image_denoise' ||
- denoise.type === 'anima_denoise'
+ denoise.type === 'anima_denoise' ||
+ denoise.type === 'wan_denoise'
) {
denoise.width = scaledSize.width;
denoise.height = scaledSize.height;
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts
index fa362fb095e..4ced0744afd 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts
@@ -65,7 +65,8 @@ export const addOutpaint = async ({
denoise.type === 'flux2_denoise' ||
denoise.type === 'sd3_denoise' ||
denoise.type === 'z_image_denoise' ||
- denoise.type === 'anima_denoise'
+ denoise.type === 'anima_denoise' ||
+ denoise.type === 'wan_denoise'
) {
denoise.width = scaledSize.width;
denoise.height = scaledSize.height;
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts
index 06ece522da5..9e5d8aec82e 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts
@@ -31,6 +31,7 @@ export const addTextToImage = ({
| 'qwen_image_l2i'
| 'z_image_l2i'
| 'anima_l2i'
+ | 'wan_l2i'
> => {
denoise.denoising_start = 0;
denoise.denoising_end = 1;
@@ -44,7 +45,8 @@ export const addTextToImage = ({
denoise.type === 'flux2_denoise' ||
denoise.type === 'sd3_denoise' ||
denoise.type === 'z_image_denoise' ||
- denoise.type === 'anima_denoise'
+ denoise.type === 'anima_denoise' ||
+ denoise.type === 'wan_denoise'
) {
denoise.width = scaledSize.width;
denoise.height = scaledSize.height;
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWanLoRAs.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWanLoRAs.ts
new file mode 100644
index 00000000000..9b7bacccff5
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWanLoRAs.ts
@@ -0,0 +1,132 @@
+import { logger } from 'app/logging/logger';
+import type { RootState } from 'app/store/store';
+import { getPrefixedId } from 'features/controlLayers/konva/util';
+import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
+import { zModelIdentifierField } from 'features/nodes/types/common';
+import type { Graph } from 'features/nodes/util/graph/generation/Graph';
+import type { Invocation, MainModelConfig, S } from 'services/api/types';
+import { isWanLoRAModelConfig } from 'services/api/types';
+
+const log = logger('system');
+
+/** Map a Wan main-model variant onto the LoRA-variant string used by the
+ * probe. A14B (both T2V and I2V) uses inner_dim=5120 → "a14b". TI2V-5B
+ * uses inner_dim=3072 → "5b". */
+const mainVariantToLoRAVariant = (mainVariant: string | null | undefined): 'a14b' | '5b' | null => {
+ if (mainVariant === 't2v_a14b' || mainVariant === 'i2v_a14b') {
+ return 'a14b';
+ }
+ if (mainVariant === 'ti2v_5b') {
+ return '5b';
+ }
+ return null;
+};
+
+/**
+ * Add Wan 2.2 LoRA wiring to the graph between the model loader and the
+ * denoise node.
+ *
+ * Each enabled Wan LoRA becomes a ``lora_selector`` feeding a ``collect``
+ * node, which fans into a ``wan_lora_collection_loader``. The collection
+ * loader rewrites the model loader's transformer output into a
+ * ``WanTransformerField`` with the appropriate ``loras`` /
+ * ``loras_low_noise`` lists populated based on each LoRA's recorded
+ * ``expert`` tag — high-noise LoRAs land on the primary list, low-noise
+ * LoRAs on ``loras_low_noise``, and untagged LoRAs are applied to both
+ * experts. The dual-expert routing happens entirely on the backend; the
+ * FE just hands the loader the bag of LoRAs.
+ *
+ * Variant filter: each LoRA's full config carries a ``variant`` field
+ * (``"a14b"`` / ``"5b"`` / null) set by the backend probe from the LoRA's
+ * inner-dim. A14B LoRAs have 5120-dim weights and can't be reshaped to
+ * fit a TI2V-5B main (3072-dim) — the layer patcher would crash with a
+ * tensor-size error. We fetch each LoRA's config and skip mismatches,
+ * logging a warning so the user can tell why a LoRA they enabled didn't
+ * take effect.
+ */
+export const addWanLoRAs = async (
+ state: RootState,
+ g: Graph,
+ denoise: Invocation<'wan_denoise'>,
+ modelLoader: Invocation<'wan_model_loader'>,
+ mainConfig: MainModelConfig
+): Promise => {
+ // MainModelConfig is the union of all main-config schemas; ``variant`` is
+ // only present on the discriminated members (Wan, FLUX, ZImage, etc.).
+ // Read it defensively rather than relying on TypeScript narrowing through
+ // a typed parameter.
+ const mainVariant = 'variant' in mainConfig ? ((mainConfig as { variant?: string | null }).variant ?? null) : null;
+ const expectedLoRAVariant = mainVariantToLoRAVariant(mainVariant);
+ const candidateLoRAs = state.loras.loras.filter((l) => l.isEnabled && l.model.base === 'wan');
+
+ if (candidateLoRAs.length === 0) {
+ return;
+ }
+
+ // Fetch each LoRA's config and filter by variant compatibility. LoRAs
+ // with ``variant === null`` are kept (the probe couldn't identify them;
+ // best to try rather than silently drop).
+ const compatibleLoRAs: typeof candidateLoRAs = [];
+ for (const lora of candidateLoRAs) {
+ try {
+ const cfg = await fetchModelConfigWithTypeGuard(lora.model.key, isWanLoRAModelConfig);
+ const loraVariant = cfg.variant ?? null;
+ if (loraVariant !== null && expectedLoRAVariant !== null && loraVariant !== expectedLoRAVariant) {
+ log.warn(
+ { lora: lora.model.name, loraVariant, mainVariant },
+ `Skipping Wan LoRA "${lora.model.name}" — its variant (${loraVariant}) is incompatible with ` +
+ `the selected main model variant (${mainVariant}). ` +
+ `A14B and TI2V-5B have different inner dims and LoRA weights aren't interchangeable.`
+ );
+ continue;
+ }
+ compatibleLoRAs.push(lora);
+ } catch (e) {
+ // If the config can't be fetched, fall back to including the LoRA —
+ // the backend will produce a clearer error if it really doesn't fit.
+ log.warn({ lora: lora.model.name, error: String(e) }, `Failed to read variant for Wan LoRA "${lora.model.name}"`);
+ compatibleLoRAs.push(lora);
+ }
+ }
+
+ if (compatibleLoRAs.length === 0) {
+ return;
+ }
+
+ const loraMetadata: S['LoRAMetadataField'][] = [];
+
+ const loraCollector = g.addNode({
+ id: getPrefixedId('lora_collector'),
+ type: 'collect',
+ });
+ const loraCollectionLoader = g.addNode({
+ type: 'wan_lora_collection_loader',
+ id: getPrefixedId('wan_lora_collection_loader'),
+ });
+
+ g.addEdge(loraCollector, 'collection', loraCollectionLoader, 'loras');
+ g.addEdge(modelLoader, 'transformer', loraCollectionLoader, 'transformer');
+ g.deleteEdgesTo(denoise, ['transformer']);
+ g.addEdge(loraCollectionLoader, 'transformer', denoise, 'transformer');
+
+ for (const lora of compatibleLoRAs) {
+ const { weight } = lora;
+ const parsedModel = zModelIdentifierField.parse(lora.model);
+
+ const loraSelector = g.addNode({
+ type: 'lora_selector',
+ id: getPrefixedId('lora_selector'),
+ lora: parsedModel,
+ weight,
+ });
+
+ loraMetadata.push({
+ model: parsedModel,
+ weight,
+ });
+
+ g.addEdge(loraSelector, 'lora', loraCollector, 'item');
+ }
+
+ g.upsertMetadata({ loras: loraMetadata });
+};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildWanGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildWanGraph.ts
new file mode 100644
index 00000000000..74b1da57571
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildWanGraph.ts
@@ -0,0 +1,277 @@
+import { logger } from 'app/logging/logger';
+import { getPrefixedId } from 'features/controlLayers/konva/util';
+import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
+import { selectRefImagesSlice } from 'features/controlLayers/store/refImagesSlice';
+import { selectCanvasMetadata } from 'features/controlLayers/store/selectors';
+import { isWanReferenceImageConfig } from 'features/controlLayers/store/types';
+import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/validators';
+import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
+import { zImageField } from 'features/nodes/types/common';
+import { addImageToImage } from 'features/nodes/util/graph/generation/addImageToImage';
+import { addInpaint } from 'features/nodes/util/graph/generation/addInpaint';
+import { addNSFWChecker } from 'features/nodes/util/graph/generation/addNSFWChecker';
+import { addOutpaint } from 'features/nodes/util/graph/generation/addOutpaint';
+import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage';
+import { addWanLoRAs } from 'features/nodes/util/graph/generation/addWanLoRAs';
+import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker';
+import { Graph } from 'features/nodes/util/graph/generation/Graph';
+import { selectCanvasOutputFields, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils';
+import type { GraphBuilderArg, GraphBuilderReturn, ImageOutputNodes } from 'features/nodes/util/graph/types';
+import { selectActiveTab } from 'features/ui/store/uiSelectors';
+import type { Invocation } from 'services/api/types';
+import { isNonRefinerMainModelConfig } from 'services/api/types';
+import type { Equals } from 'tsafe';
+import { assert } from 'tsafe';
+
+const log = logger('system');
+
+/**
+ * Build a graph for Wan 2.2 image generation.
+ *
+ * Phase 9 piece #1: text-to-image only, Diffusers main model with all
+ * components (transformer, VAE, UMT5-XXL encoder) resolved from the main
+ * model itself. Subsequent pieces will add:
+ * - img2img (Latents input + Image-to-Latents wiring + denoising_start)
+ * - I2V (ref-image encoder, A14B I2V variant gate)
+ * - LoRAs (single + collection)
+ * - Inpaint (mask handling)
+ * - Standalone VAE / T5 / GGUF low-noise-expert wiring via params slice
+ */
+export const buildWanGraph = async (arg: GraphBuilderArg): Promise => {
+ const { generationMode, state, manager } = arg;
+
+ log.debug({ generationMode, manager: manager?.id }, 'Building Wan 2.2 graph');
+
+ const model = selectMainModelConfig(state);
+ assert(model, 'No model selected');
+ assert(model.base === 'wan', 'Selected model is not a Wan model');
+
+ // Fetch the full config early so we can branch on variant. I2V flows
+ // route the raster image through wan_ref_image_encoder instead of
+ // wan_i2l, so the variant has to be known before we choose a graph
+ // shape — not after.
+ const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
+ assert(modelConfig.base === 'wan');
+ const isI2V = modelConfig.variant === 'i2v_a14b';
+
+ const params = selectParamsSlice(state);
+ const { cfgScale: cfg_scale, steps } = params;
+ const prompts = selectPresetModifiedPrompts(state);
+
+ const g = new Graph(getPrefixedId('wan_graph'));
+
+ const modelLoader = g.addNode({
+ type: 'wan_model_loader',
+ id: getPrefixedId('wan_model_loader'),
+ model,
+ transformer_low_noise_model: params.wanTransformerLowNoise ?? undefined,
+ component_source: params.wanComponentSource ?? undefined,
+ vae_model: params.wanVaeModel ?? undefined,
+ wan_t5_encoder_model: params.wanT5EncoderModel ?? undefined,
+ });
+
+ const positivePrompt = g.addNode({
+ id: getPrefixedId('positive_prompt'),
+ type: 'string',
+ });
+ const posCond = g.addNode({
+ type: 'wan_text_encoder',
+ id: getPrefixedId('pos_prompt'),
+ });
+
+ // CFG is mathematically inactive at scale 1.0 — skip the negative branch
+ // entirely so each step runs only one forward pass.
+ const useCfg = cfg_scale > 1;
+ const negCond = useCfg
+ ? g.addNode({
+ type: 'wan_text_encoder',
+ id: getPrefixedId('neg_prompt'),
+ prompt: prompts.negative || ' ',
+ })
+ : null;
+
+ const seed = g.addNode({
+ id: getPrefixedId('seed'),
+ type: 'integer',
+ });
+
+ const denoise = g.addNode({
+ type: 'wan_denoise',
+ id: getPrefixedId('denoise_latents'),
+ guidance_scale: cfg_scale,
+ // The denoise node treats values < 1.0 (including the FE's default 0) as
+ // "fall back to the primary guidance_scale". Forward null/undefined when
+ // the user hasn't set an explicit low-noise CFG so the backend handles it.
+ guidance_scale_low_noise: params.wanGuidanceScaleLowNoise ?? undefined,
+ steps,
+ });
+
+ const l2i = g.addNode({
+ type: 'wan_l2i',
+ id: getPrefixedId('l2i'),
+ });
+
+ g.addEdge(modelLoader, 'transformer', denoise, 'transformer');
+ g.addEdge(modelLoader, 'wan_t5_encoder', posCond, 'wan_t5_encoder');
+ g.addEdge(modelLoader, 'vae', l2i, 'vae');
+
+ g.addEdge(positivePrompt, 'value', posCond, 'prompt');
+ g.addEdge(posCond, 'conditioning', denoise, 'positive_conditioning');
+
+ if (negCond) {
+ g.addEdge(modelLoader, 'wan_t5_encoder', negCond, 'wan_t5_encoder');
+ g.addEdge(negCond, 'conditioning', denoise, 'negative_conditioning');
+ }
+
+ g.addEdge(seed, 'value', denoise, 'seed');
+ g.addEdge(denoise, 'latents', l2i, 'latents');
+
+ // Wan LoRAs (high-noise, low-noise, and untagged). The collection loader
+ // is inserted between modelLoader and denoise; both expert routing and
+ // dual-list population happen on the backend based on each LoRA's
+ // recorded ``expert`` tag. The helper also filters out variant-incompatible
+ // LoRAs (e.g. A14B Lightning on a TI2V-5B main) so the layer patcher
+ // doesn't crash on a shape mismatch.
+ await addWanLoRAs(state, g, denoise, modelLoader, modelConfig);
+
+ g.upsertMetadata({
+ cfg_scale,
+ negative_prompt: prompts.negative,
+ model: Graph.getModelMetadataField(modelConfig),
+ steps,
+ wan_transformer_low_noise: params.wanTransformerLowNoise,
+ wan_component_source: params.wanComponentSource,
+ wan_vae_model: params.wanVaeModel,
+ wan_t5_encoder_model: params.wanT5EncoderModel,
+ wan_guidance_scale_low_noise: params.wanGuidanceScaleLowNoise,
+ });
+ g.addEdgeToMetadata(seed, 'value', 'seed');
+ g.addEdgeToMetadata(positivePrompt, 'value', 'positive_prompt');
+
+ let canvasOutput: Invocation = l2i;
+
+ // I2V variants take a reference image from the global Reference Images
+ // panel (same UX as Qwen Image Edit / FLUX.2 Klein). The image is encoded
+ // by the model's own VAE and concatenated to the noise latents along the
+ // channel dim each step (transformer in_channels=36 on I2V). Canvas modes
+ // (img2img/inpaint/outpaint) don't apply to I2V — the ref image fully
+ // replaces what a raster layer used to provide.
+ if (isI2V) {
+ assert(
+ generationMode === 'txt2img',
+ 'Wan 2.2 I2V only supports text-to-image with a reference image. ' +
+ 'Use a T2V or TI2V model for canvas img2img / inpaint / outpaint.'
+ );
+
+ const wanRefEntity = selectRefImagesSlice(state).entities.find(
+ (entity) =>
+ entity.isEnabled &&
+ isWanReferenceImageConfig(entity.config) &&
+ entity.config.image !== null &&
+ getGlobalReferenceImageWarnings(entity, modelConfig).length === 0
+ );
+ assert(
+ wanRefEntity && isWanReferenceImageConfig(wanRefEntity.config) && wanRefEntity.config.image,
+ 'Wan 2.2 I2V requires a reference image. Add one in the Reference Images panel.'
+ );
+
+ canvasOutput = addTextToImage({ g, state, denoise, l2i });
+ const refImageField = zImageField.parse(
+ wanRefEntity.config.image.crop?.image ?? wanRefEntity.config.image.original.image
+ );
+ const refEncoder = g.addNode({
+ type: 'wan_ref_image_encoder',
+ id: getPrefixedId('wan_ref_encoder'),
+ image: refImageField,
+ width: denoise.width,
+ height: denoise.height,
+ });
+ g.addEdge(modelLoader, 'vae', refEncoder, 'vae');
+ g.addEdge(refEncoder, 'ref_image', denoise, 'ref_image');
+
+ g.upsertMetadata({ generation_mode: 'wan_i2v' });
+ } else if (generationMode === 'txt2img') {
+ canvasOutput = addTextToImage({
+ g,
+ state,
+ denoise,
+ l2i,
+ });
+ g.upsertMetadata({ generation_mode: 'wan_txt2img' });
+ } else if (generationMode === 'img2img') {
+ assert(manager !== null);
+ const i2l = g.addNode({
+ type: 'wan_i2l',
+ id: getPrefixedId('wan_i2l'),
+ });
+ canvasOutput = await addImageToImage({
+ g,
+ state,
+ manager,
+ denoise,
+ l2i,
+ i2l,
+ vaeSource: modelLoader,
+ });
+ g.upsertMetadata({ generation_mode: 'wan_img2img' });
+ } else if (generationMode === 'inpaint') {
+ assert(manager !== null);
+ const i2l = g.addNode({
+ type: 'wan_i2l',
+ id: getPrefixedId('wan_i2l'),
+ });
+ canvasOutput = await addInpaint({
+ g,
+ state,
+ manager,
+ l2i,
+ i2l,
+ denoise,
+ vaeSource: modelLoader,
+ modelLoader,
+ seed,
+ });
+ g.upsertMetadata({ generation_mode: 'wan_inpaint' });
+ } else if (generationMode === 'outpaint') {
+ assert(manager !== null);
+ const i2l = g.addNode({
+ type: 'wan_i2l',
+ id: getPrefixedId('wan_i2l'),
+ });
+ canvasOutput = await addOutpaint({
+ g,
+ state,
+ manager,
+ l2i,
+ i2l,
+ denoise,
+ vaeSource: modelLoader,
+ modelLoader,
+ seed,
+ });
+ g.upsertMetadata({ generation_mode: 'wan_outpaint' });
+ } else {
+ assert>(false);
+ }
+
+ if (state.system.shouldUseNSFWChecker) {
+ canvasOutput = addNSFWChecker(g, canvasOutput);
+ }
+ if (state.system.shouldUseWatermarker) {
+ canvasOutput = addWatermarker(g, canvasOutput);
+ }
+
+ g.updateNode(canvasOutput, selectCanvasOutputFields(state));
+
+ if (selectActiveTab(state) === 'canvas') {
+ g.upsertMetadata(selectCanvasMetadata(state));
+ }
+
+ g.setMetadataReceivingNode(canvasOutput);
+
+ return {
+ g,
+ seed,
+ positivePrompt,
+ };
+};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts
index 28aa74db5ec..9d5f165ef78 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts
@@ -217,7 +217,8 @@ export const isMainModelWithoutUnet = (modelLoader: Invocation =
ColorField: { r: 0, g: 0, b: 0, a: 1 },
FloatField: 0,
ImageField: undefined,
+ VideoField: undefined,
IntegerField: 0,
ModelIdentifierField: undefined,
LoRAField: [],
diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts b/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts
index 7751390e9da..e4b527178cb 100644
--- a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts
@@ -26,6 +26,7 @@ import type {
StringFieldInputTemplate,
StringGeneratorFieldInputTemplate,
StylePresetFieldInputTemplate,
+ VideoFieldInputTemplate,
} from 'features/nodes/types/field';
import {
getFloatGeneratorArithmeticSequenceDefaults,
@@ -321,6 +322,20 @@ const buildImageFieldInputTemplate: FieldInputTemplateBuilder = ({
+ schemaObject,
+ baseField,
+ fieldType,
+}) => {
+ const template: VideoFieldInputTemplate = {
+ ...baseField,
+ type: fieldType,
+ default: schemaObject.default ?? undefined,
+ };
+
+ return template;
+};
+
const buildImageFieldCollectionInputTemplate: FieldInputTemplateBuilder = ({
schemaObject,
baseField,
@@ -520,6 +535,7 @@ const TEMPLATE_BUILDER_MAP: Record {
+ const dispatch = useAppDispatch();
+ const { t } = useTranslation();
+ const value = useAppSelector(selectWanTransformerLowNoise);
+ const [modelConfigs, { isLoading }] = useWanGGUFLowNoiseModels();
+
+ const _onChange = useCallback(
+ (model: MainModelConfig | null) => {
+ if (model) {
+ dispatch(wanTransformerLowNoiseSelected(zModelIdentifierField.parse(model)));
+ } else {
+ dispatch(wanTransformerLowNoiseSelected(null));
+ }
+ },
+ [dispatch]
+ );
+
+ const {
+ options,
+ value: comboValue,
+ onChange,
+ noOptionsMessage,
+ } = useModelCombobox({
+ modelConfigs,
+ onChange: _onChange,
+ selectedModel: value,
+ isLoading,
+ });
+
+ return (
+
+ {t('modelManager.wanTransformerLowNoise')}
+
+
+ );
+});
+
+ParamWanTransformerLowNoiseSelect.displayName = 'ParamWanTransformerLowNoiseSelect';
+
+/**
+ * Wan 2.2 Component Source Select
+ *
+ * Picks a Diffusers Wan model whose VAE and UMT5-XXL encoder will be extracted
+ * for the workflow. Required when the main Wan model is a GGUF (since GGUF
+ * mains are transformer-only). Ignored for Diffusers mains, which carry their
+ * own VAE and encoder.
+ */
+const ParamWanComponentSourceSelect = memo(() => {
+ const dispatch = useAppDispatch();
+ const { t } = useTranslation();
+ const value = useAppSelector(selectWanComponentSource);
+ const [modelConfigs, { isLoading }] = useWanDiffusersModels();
+
+ const _onChange = useCallback(
+ (model: MainModelConfig | null) => {
+ if (model) {
+ dispatch(wanComponentSourceSelected(zModelIdentifierField.parse(model)));
+ } else {
+ dispatch(wanComponentSourceSelected(null));
+ }
+ },
+ [dispatch]
+ );
+
+ const {
+ options,
+ value: comboValue,
+ onChange,
+ noOptionsMessage,
+ } = useModelCombobox({
+ modelConfigs,
+ onChange: _onChange,
+ selectedModel: value,
+ isLoading,
+ });
+
+ return (
+
+ {t('modelManager.wanComponentSource')}
+
+
+ );
+});
+
+ParamWanComponentSourceSelect.displayName = 'ParamWanComponentSourceSelect';
+
+/**
+ * Wan 2.2 Standalone VAE Select
+ *
+ * Selects a standalone Wan VAE checkpoint. When set, this overrides the VAE
+ * provided by the Component Source (or the main Diffusers model).
+ */
+const ParamWanVaeModelSelect = memo(() => {
+ const dispatch = useAppDispatch();
+ const { t } = useTranslation();
+ const vaeModel = useAppSelector(selectWanVaeModel);
+ const [modelConfigs, { isLoading }] = useWanVAEModels();
+
+ const _onChange = useCallback(
+ (model: VAEModelConfig | null) => {
+ if (model) {
+ dispatch(wanVaeModelSelected(zModelIdentifierField.parse(model)));
+ } else {
+ dispatch(wanVaeModelSelected(null));
+ }
+ },
+ [dispatch]
+ );
+
+ const { options, value, onChange, noOptionsMessage } = useModelCombobox({
+ modelConfigs,
+ onChange: _onChange,
+ selectedModel: vaeModel,
+ isLoading,
+ });
+
+ return (
+
+ {t('modelManager.wanVae')}
+
+
+ );
+});
+
+ParamWanVaeModelSelect.displayName = 'ParamWanVaeModelSelect';
+
+/**
+ * Wan 2.2 Standalone UMT5-XXL Encoder Select
+ *
+ * Selects a standalone UMT5-XXL encoder. When set, this overrides the encoder
+ * provided by the Component Source (or the main Diffusers model).
+ */
+const ParamWanT5EncoderModelSelect = memo(() => {
+ const dispatch = useAppDispatch();
+ const { t } = useTranslation();
+ const encoderModel = useAppSelector(selectWanT5EncoderModel);
+ const [modelConfigs, { isLoading }] = useWanT5EncoderModels();
+
+ const _onChange = useCallback(
+ (model: WanT5EncoderModelConfig | null) => {
+ if (model) {
+ dispatch(wanT5EncoderModelSelected(zModelIdentifierField.parse(model)));
+ } else {
+ dispatch(wanT5EncoderModelSelected(null));
+ }
+ },
+ [dispatch]
+ );
+
+ const { options, value, onChange, noOptionsMessage } = useModelCombobox({
+ modelConfigs,
+ onChange: _onChange,
+ selectedModel: encoderModel,
+ isLoading,
+ });
+
+ return (
+
+ {t('modelManager.wanT5Encoder')}
+
+
+ );
+});
+
+ParamWanT5EncoderModelSelect.displayName = 'ParamWanT5EncoderModelSelect';
+
+/**
+ * Combined Wan 2.2 component selectors (low-noise transformer + standalone
+ * VAE + standalone T5 encoder + Component Source).
+ *
+ * Only relevant for GGUF workflows. Diffusers Wan mains have everything
+ * built in; TI2V-5B is a single-expert model with no low-noise pair. Showing
+ * these always is fine since they're optional — but the AdvancedSettingsAccordion
+ * still gates the render on `isWan` so they don't pollute other tabs.
+ */
+const ParamWanModelSelects = () => {
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default memo(ParamWanModelSelects);
diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamWanGuidanceScaleLowNoise.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamWanGuidanceScaleLowNoise.tsx
new file mode 100644
index 00000000000..5d3f7b3e34a
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamWanGuidanceScaleLowNoise.tsx
@@ -0,0 +1,94 @@
+import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel, IconButton } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import {
+ selectWanGuidanceScaleLowNoise,
+ wanGuidanceScaleLowNoiseChanged,
+} from 'features/controlLayers/store/paramsSlice';
+import type React from 'react';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiXBold } from 'react-icons/pi';
+
+// Match the primary ParamCFGScale's range so the slider thumb position is
+// visually comparable between the two CFG sliders at the same numeric value
+// (e.g. CFG=5 and CFG-Low=3 should look correct relative to each other).
+const CONSTRAINTS = {
+ initial: 3.5,
+ sliderMin: 1,
+ sliderMax: 20,
+ numberInputMin: 1,
+ numberInputMax: 200,
+ fineStep: 0.1,
+ coarseStep: 0.5,
+};
+
+const MARKS = [CONSTRAINTS.sliderMin, Math.floor(CONSTRAINTS.sliderMax / 2), CONSTRAINTS.sliderMax];
+
+/**
+ * Wan 2.2 Guidance Scale (Low Noise)
+ *
+ * Optional separate CFG for the A14B low-noise expert. When null (cleared),
+ * the denoise node falls back to the primary guidance_scale. Ignored for
+ * TI2V-5B (single-expert).
+ *
+ * Diffusers reference defaults for A14B: primary 4.0 / low-noise 3.0 — i.e.
+ * a slightly lower CFG on the detail-pass expert produces less over-sharpened
+ * output.
+ */
+const ParamWanGuidanceScaleLowNoise = () => {
+ const { t } = useTranslation();
+ const value = useAppSelector(selectWanGuidanceScaleLowNoise);
+ const dispatch = useAppDispatch();
+
+ const onChange = useCallback((v: number) => dispatch(wanGuidanceScaleLowNoiseChanged(v)), [dispatch]);
+ const onReset = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ dispatch(wanGuidanceScaleLowNoiseChanged(null));
+ },
+ [dispatch]
+ );
+
+ const displayValue = value ?? CONSTRAINTS.initial;
+
+ return (
+
+
+ {t('parameters.wanGuidanceScaleLowNoise')}{' '}
+ {value !== null && (
+ }
+ onClick={onReset}
+ minW={4}
+ h={4}
+ />
+ )}
+
+
+
+
+ );
+};
+
+export default memo(ParamWanGuidanceScaleLowNoise);
diff --git a/invokeai/frontend/web/src/features/parameters/types/constants.ts b/invokeai/frontend/web/src/features/parameters/types/constants.ts
index a3ffa24cc64..1674c16009e 100644
--- a/invokeai/frontend/web/src/features/parameters/types/constants.ts
+++ b/invokeai/frontend/web/src/features/parameters/types/constants.ts
@@ -49,6 +49,10 @@ export const CLIP_SKIP_MAP: { [key in BaseModelType]?: { maxClip: number; marker
maxClip: 0,
markers: [],
},
+ wan: {
+ maxClip: 0,
+ markers: [],
+ },
};
/**
diff --git a/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts b/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts
index 2ac59a32e2b..4b2263db2f4 100644
--- a/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts
+++ b/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts
@@ -63,9 +63,16 @@ export const isInSDXLTrainingDimensions = (width: number, height: number): boole
/**
* Gets the grid size for a given base model. For Flux, the grid size is 16, otherwise it is 8.
* - sd-1, sd-2, sdxl, anima: 8
- * - flux, sd-3, qwen-image, z-image: 16
+ * - flux, sd-3, qwen-image, z-image, wan: 16
* - cogview4: 32
* - default: 8
+ *
+ * Wan 2.2's transformer has ``patch_size=(1, 2, 2)``: it patch-embeds with
+ * stride 2 then un-patches by 2. Combined with the VAE's 8x spatial scale,
+ * canvas H/W must be a multiple of ``8 * 2 = 16``; otherwise the patch
+ * round-trip produces an off-by-one and the scheduler step fails with a
+ * spatial-dim mismatch between latents and noise prediction.
+ *
* @param base The base model
* @returns The grid size for the model, defaulting to 8
*/
@@ -77,6 +84,7 @@ export const getGridSize = (base?: BaseModelType | null): number => {
case 'flux2':
case 'sd-3':
case 'qwen-image':
+ case 'wan':
case 'z-image':
return 16;
case 'sd-1':
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts
index 1229371b6e8..7eef621819b 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts
@@ -21,6 +21,7 @@ import { buildQwenImageGraph } from 'features/nodes/util/graph/generation/buildQ
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph';
import { buildSDXLGraph } from 'features/nodes/util/graph/generation/buildSDXLGraph';
+import { buildWanGraph } from 'features/nodes/util/graph/generation/buildWanGraph';
import { buildZImageGraph } from 'features/nodes/util/graph/generation/buildZImageGraph';
import { selectCanvasDestination } from 'features/nodes/util/graph/graphBuilderUtils';
import type { GraphBuilderArg } from 'features/nodes/util/graph/types';
@@ -73,6 +74,8 @@ const enqueueCanvas = async (store: AppStore, canvasManager: CanvasManager, prep
return await buildExternalGraph(graphBuilderArg);
case 'anima':
return await buildAnimaGraph(graphBuilderArg);
+ case 'wan':
+ return await buildWanGraph(graphBuilderArg);
default:
assert(false, `No graph builders for base ${base}`);
}
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts
index 8b0c30d924f..5650905576a 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts
@@ -19,6 +19,7 @@ import { buildQwenImageGraph } from 'features/nodes/util/graph/generation/buildQ
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph';
import { buildSDXLGraph } from 'features/nodes/util/graph/generation/buildSDXLGraph';
+import { buildWanGraph } from 'features/nodes/util/graph/generation/buildWanGraph';
import { buildZImageGraph } from 'features/nodes/util/graph/generation/buildZImageGraph';
import type { GraphBuilderArg } from 'features/nodes/util/graph/types';
import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
@@ -66,6 +67,8 @@ const enqueueGenerate = async (store: AppStore, prepend: boolean) => {
return await buildExternalGraph(graphBuilderArg);
case 'anima':
return await buildAnimaGraph(graphBuilderArg);
+ case 'wan':
+ return await buildWanGraph(graphBuilderArg);
default:
assert(false, `No graph builders for base ${base}`);
}
diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.ts b/invokeai/frontend/web/src/features/queue/store/readiness.ts
index 1e40cc6ce18..d6958ff750b 100644
--- a/invokeai/frontend/web/src/features/queue/store/readiness.ts
+++ b/invokeai/frontend/web/src/features/queue/store/readiness.ts
@@ -311,6 +311,19 @@ export const getReasonsWhyCannotEnqueueGenerateTab = (arg: {
}
}
+ if (model?.base === 'wan' && model.format === 'gguf_quantized') {
+ // GGUF Wan mains carry only the transformer; VAE + UMT5-XXL encoder must
+ // come from either standalone models or the Component Source (Diffusers).
+ // The low-noise A14B partner expert is optional — if omitted, the loader
+ // will use the high-noise expert for the whole schedule (lower quality
+ // but still produces an image).
+ const hasVaeSource = params.wanVaeModel !== null || params.wanComponentSource !== null;
+ const hasEncoderSource = params.wanT5EncoderModel !== null || params.wanComponentSource !== null;
+ if (!hasVaeSource || !hasEncoderSource) {
+ reasons.push({ content: i18n.t('parameters.invoke.noWanComponentSourceSelected') });
+ }
+ }
+
if (model?.base === 'z-image') {
// Check if VAE source is available (either separate VAE or Qwen3 Source)
const hasVaeSource = params.zImageVaeModel !== null || params.zImageQwen3SourceModel !== null;
@@ -771,6 +784,19 @@ export const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
}
}
+ if (model?.base === 'wan' && model.format === 'gguf_quantized') {
+ // GGUF Wan mains carry only the transformer; VAE + UMT5-XXL encoder must
+ // come from either standalone models or the Component Source (Diffusers).
+ // The low-noise A14B partner expert is optional — if omitted, the loader
+ // will use the high-noise expert for the whole schedule (lower quality
+ // but still produces an image).
+ const hasVaeSource = params.wanVaeModel !== null || params.wanComponentSource !== null;
+ const hasEncoderSource = params.wanT5EncoderModel !== null || params.wanComponentSource !== null;
+ if (!hasVaeSource || !hasEncoderSource) {
+ reasons.push({ content: i18n.t('parameters.invoke.noWanComponentSourceSelected') });
+ }
+ }
+
if (model?.base === 'z-image') {
// Check if VAE source is available (either separate VAE or Qwen3 Source)
const hasVaeSource = params.zImageVaeModel !== null || params.zImageQwen3SourceModel !== null;
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
index bfb69b945c8..312c9b71df9 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
@@ -10,6 +10,7 @@ import {
selectIsFlux2,
selectIsQwenImage,
selectIsSD3,
+ selectIsWan,
selectIsZImage,
selectParamsSlice,
selectVAEKey,
@@ -24,6 +25,7 @@ import ParamFlux2KleinModelSelect from 'features/parameters/components/Advanced/
import ParamQwenImageComponentSourceSelect from 'features/parameters/components/Advanced/ParamQwenImageComponentSourceSelect';
import ParamQwenImageQuantization from 'features/parameters/components/Advanced/ParamQwenImageQuantization';
import ParamT5EncoderModelSelect from 'features/parameters/components/Advanced/ParamT5EncoderModelSelect';
+import ParamWanModelSelects from 'features/parameters/components/Advanced/ParamWanModelSelects';
import ParamZImageQwen3VaeModelSelect from 'features/parameters/components/Advanced/ParamZImageQwen3VaeModelSelect';
import ParamSeamlessXAxis from 'features/parameters/components/Seamless/ParamSeamlessXAxis';
import ParamSeamlessYAxis from 'features/parameters/components/Seamless/ParamSeamlessYAxis';
@@ -54,6 +56,7 @@ export const AdvancedSettingsAccordion = memo(() => {
const isExternal = useAppSelector(selectIsExternal);
const isQwenImage = useAppSelector(selectIsQwenImage);
const isAnima = useAppSelector(selectIsAnima);
+ const isWan = useAppSelector(selectIsWan);
const selectBadges = useMemo(
() =>
@@ -107,13 +110,13 @@ export const AdvancedSettingsAccordion = memo(() => {
return (
- {!isZImage && !isAnima && !isFlux2 && !isQwenImage && (
+ {!isZImage && !isAnima && !isFlux2 && !isQwenImage && !isWan && (
{isFLUX ? : }
{!isFLUX && !isSD3 && }
)}
- {!isFLUX && !isFlux2 && !isSD3 && !isZImage && !isQwenImage && !isAnima && (
+ {!isFLUX && !isFlux2 && !isSD3 && !isZImage && !isQwenImage && !isAnima && !isWan && (
<>
@@ -166,6 +169,11 @@ export const AdvancedSettingsAccordion = memo(() => {
)}
+ {isWan && (
+
+
+
+ )}
);
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
index 220008a38b0..2ec05cd46d8 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
@@ -13,6 +13,7 @@ import {
selectIsFlux2,
selectIsQwenImage,
selectIsSD3,
+ selectIsWan,
selectIsZImage,
selectModelSupportsGuidance,
selectModelSupportsSteps,
@@ -29,6 +30,7 @@ import ParamGuidance from 'features/parameters/components/Core/ParamGuidance';
import ParamQwenImageShift from 'features/parameters/components/Core/ParamQwenImageShift';
import ParamScheduler from 'features/parameters/components/Core/ParamScheduler';
import ParamSteps from 'features/parameters/components/Core/ParamSteps';
+import ParamWanGuidanceScaleLowNoise from 'features/parameters/components/Core/ParamWanGuidanceScaleLowNoise';
import ParamZImageScheduler from 'features/parameters/components/Core/ParamZImageScheduler';
import ParamZImageShift from 'features/parameters/components/Core/ParamZImageShift';
import ParamZImageSeedVarianceSettings from 'features/parameters/components/SeedVariance/ParamZImageSeedVarianceSettings';
@@ -55,6 +57,7 @@ export const GenerationSettingsAccordion = memo(() => {
const isExternal = useAppSelector(selectIsExternal);
const isQwenImage = useAppSelector(selectIsQwenImage);
const isAnima = useAppSelector(selectIsAnima);
+ const isWan = useAppSelector(selectIsWan);
const fluxDypePreset = useAppSelector(selectFluxDypePreset);
const modelSupportsGuidance = useAppSelector(selectModelSupportsGuidance);
const modelSupportsSteps = useAppSelector(selectModelSupportsSteps);
@@ -104,7 +107,8 @@ export const GenerationSettingsAccordion = memo(() => {
!isCogView4 &&
!isZImage &&
!isQwenImage &&
- !isAnima && }
+ !isAnima &&
+ !isWan && }
{!isExternal && (isFLUX || isFlux2) && }
{!isExternal && isZImage && }
{!isExternal && isAnima && }
@@ -114,6 +118,7 @@ export const GenerationSettingsAccordion = memo(() => {
)}
{!isExternal && !isFLUX && !isFlux2 && }
+ {!isExternal && isWan && }
{!isExternal && isZImage && }
{!isExternal && isQwenImage && }
{!isExternal && isFLUX && }
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
index 66f76dcd153..134ab5f1e62 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
@@ -17,7 +17,26 @@ export const MainModelPicker = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const activeTab = useAppSelector(selectActiveTab);
- const [modelConfigs] = useMainModels();
+ const [allModelConfigs] = useMainModels();
+ // Low-noise Wan GGUFs belong in the Transformer (Low Noise) slot of the
+ // Wan advanced section, not as a primary main. Filter them out of the main
+ // model dropdown so users can't accidentally wire them backwards.
+ const modelConfigs = useMemo(
+ () =>
+ allModelConfigs.filter((c) => {
+ if (
+ c.type === 'main' &&
+ c.base === 'wan' &&
+ c.format === 'gguf_quantized' &&
+ 'expert' in c &&
+ c.expert === 'low'
+ ) {
+ return false;
+ }
+ return true;
+ }),
+ [allModelConfigs]
+ );
const selectedModelConfig = useSelectedModelConfig();
const onChange = useCallback(
(modelConfig: AnyModelConfigWithExternal) => {
diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx
index 089293d4e7a..e1b642b9ff0 100644
--- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx
+++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx
@@ -1,5 +1,5 @@
import { Box, Button, Flex, Icon, IconButton, Image, Tooltip } from '@invoke-ai/ui-library';
-import { dropzoneAccept } from 'common/hooks/useImageUploadButton';
+import { imageDropzoneAccept } from 'common/util/uploadMediaAccept';
import { useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import type { UseControllerProps } from 'react-hook-form';
@@ -27,7 +27,7 @@ export const StylePresetImageField = (props: UseControllerProps({
+ query: (board_id) => ({
+ url: getListVideosUrl({
+ board_id: board_id ?? 'none',
+ is_intermediate: false,
+ limit: 0,
+ offset: 0,
+ }),
+ method: 'GET',
+ }),
+ providesTags: (result, error, arg) => [{ type: 'BoardVideosTotal', id: arg ?? 'none' }, 'FetchOnReconnect'],
+ transformResponse: (response: OffsetPaginatedResults_VideoDTO_) => {
+ return { total: response.total };
+ },
+ }),
+
/**
* Boards Mutations
*/
@@ -132,6 +149,7 @@ export const {
useListAllBoardsQuery,
useGetBoardImagesTotalQuery,
useGetBoardAssetsTotalQuery,
+ useGetBoardVideosTotalQuery,
useCreateBoardMutation,
useUpdateBoardMutation,
useListAllImageNamesForBoardQuery,
diff --git a/invokeai/frontend/web/src/services/api/endpoints/gallery.ts b/invokeai/frontend/web/src/services/api/endpoints/gallery.ts
new file mode 100644
index 00000000000..5b3dd85493e
--- /dev/null
+++ b/invokeai/frontend/web/src/services/api/endpoints/gallery.ts
@@ -0,0 +1,58 @@
+import type {
+ GetGalleryItemNamesArgs,
+ GetGalleryItemNamesResult,
+ ListGalleryItemsArgs,
+ ListGalleryItemsResponse,
+} from 'services/api/types';
+import { getListGalleryItemsUrl } from 'services/api/util';
+import stableHash from 'stable-hash';
+
+import { api, buildV1Url } from '..';
+
+/**
+ * Builds an endpoint URL for the gallery router.
+ * @example
+ * buildGalleryUrl('items/') // 'api/v1/gallery/items/'
+ */
+const buildGalleryUrl = (path: string = '', query?: Parameters[1]) =>
+ buildV1Url(`gallery/${path}`, query);
+
+export const galleryApi = api.injectEndpoints({
+ endpoints: (build) => ({
+ /** Paginated polymorphic stream of images + videos, sorted by created_at. */
+ listGalleryItems: build.query({
+ query: (queryArgs) => ({
+ url: getListGalleryItemsUrl(queryArgs),
+ method: 'GET',
+ }),
+ providesTags: (result, error, queryArgs) => [
+ 'GalleryItemList',
+ 'FetchOnReconnect',
+ { type: 'GalleryItemList', id: stableHash(queryArgs) },
+ { type: 'Board', id: queryArgs.board_id ?? 'none' },
+ ],
+ }),
+
+ /**
+ * Ordered (kind, name) refs for virtualized selection. The gallery grid's name list and
+ * keyboard navigation use this — the flat string list is derived by mapping items to `name`.
+ */
+ getGalleryItemNames: build.query({
+ query: (queryArgs) => ({
+ url: buildGalleryUrl('items/names', queryArgs),
+ method: 'GET',
+ }),
+ providesTags: (result, error, queryArgs) => [
+ 'GalleryItemNameList',
+ 'FetchOnReconnect',
+ { type: 'GalleryItemNameList', id: stableHash(queryArgs) },
+ ],
+ }),
+ }),
+});
+
+// useGetGalleryItemNamesQuery is consumed by use-gallery-image-names.ts.
+export const { useGetGalleryItemNamesQuery } = galleryApi;
+
+/** @knipignore Lands with the paged gallery view / future bulk-DTO consumers; not used today. */
+export const { useListGalleryItemsQuery } = galleryApi;
diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts
index 7b150ac3572..a47035e2075 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/images.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts
@@ -296,7 +296,9 @@ export const imagesApi = api.injectEndpoints({
query: ({ board_id }) => ({ url: buildBoardsUrl(board_id), method: 'DELETE' }),
invalidatesTags: () => [
{ type: 'Board', id: LIST_TAG },
- // invalidate the 'No Board' cache
+ // Both images and videos on the board cascade to the 'No Board' bucket on the
+ // backend side; invalidate the 'none' caches for both kinds so the polymorphic
+ // gallery surfaces them. The Gallery* tags refresh the unified gallery list view.
{
type: 'ImageList',
id: getListImagesUrl({
@@ -311,6 +313,10 @@ export const imagesApi = api.injectEndpoints({
categories: ASSETS_CATEGORIES,
}),
},
+ { type: 'VideoList', id: LIST_TAG },
+ 'VideoNameList',
+ 'GalleryItemList',
+ 'GalleryItemNameList',
],
}),
@@ -323,7 +329,15 @@ export const imagesApi = api.injectEndpoints({
method: 'DELETE',
params: { include_images: true },
}),
- invalidatesTags: () => [{ type: 'Board', id: LIST_TAG }],
+ // The backend now also cascade-deletes videos on the board, so the unified gallery
+ // and the video list both need invalidation in addition to the board tag.
+ invalidatesTags: () => [
+ { type: 'Board', id: LIST_TAG },
+ { type: 'VideoList', id: LIST_TAG },
+ 'VideoNameList',
+ 'GalleryItemList',
+ 'GalleryItemNameList',
+ ],
}),
addImageToBoard: build.mutation<
paths['/api/v1/board_images/']['post']['responses']['201']['content']['application/json'],
@@ -484,7 +498,6 @@ export const {
useStarImagesMutation,
useUnstarImagesMutation,
useBulkDownloadImagesMutation,
- useGetImageNamesQuery,
useGetImageDTOsByNamesMutation,
} = imagesApi;
diff --git a/invokeai/frontend/web/src/services/api/endpoints/videos.ts b/invokeai/frontend/web/src/services/api/endpoints/videos.ts
new file mode 100644
index 00000000000..a151b3f5413
--- /dev/null
+++ b/invokeai/frontend/web/src/services/api/endpoints/videos.ts
@@ -0,0 +1,348 @@
+import { skipToken } from '@reduxjs/toolkit/query';
+import { getStore } from 'app/store/nanostores/store';
+import type { paths } from 'services/api/schema';
+import type {
+ GetVideoNamesArgs,
+ GetVideoNamesResult,
+ ListVideosArgs,
+ ListVideosResponse,
+ UploadVideoArg,
+ VideoDTO,
+} from 'services/api/types';
+import { getListVideosUrl } from 'services/api/util';
+import {
+ getTagsToInvalidateForBoardAffectingMutation,
+ getTagsToInvalidateForVideoMutation,
+} from 'services/api/util/tagInvalidation';
+import stableHash from 'stable-hash';
+import type { Param0 } from 'tsafe';
+import type { JsonObject } from 'type-fest';
+
+import { api, buildV1Url, LIST_TAG } from '..';
+
+/**
+ * Builds an endpoint URL for the videos router.
+ * @example
+ * buildVideosUrl('some-path') // 'api/v1/videos/some-path'
+ */
+const buildVideosUrl = (path: string = '', query?: Parameters[1]) =>
+ buildV1Url(`videos/${path}`, query);
+
+/**
+ * Video RTK Query endpoints — parallel to imagesApi. Used by the gallery (Phase 4) and the
+ * viewer / linear flows that land in later phases.
+ */
+export const videosApi = api.injectEndpoints({
+ endpoints: (build) => ({
+ /**
+ * List videos (paginated). Used directly when a video-only view is needed; the gallery
+ * itself uses the polymorphic /gallery/items/ endpoint.
+ */
+ listVideos: build.query({
+ query: (queryArgs) => ({
+ url: getListVideosUrl(queryArgs),
+ method: 'GET',
+ }),
+ providesTags: (result, error, queryArgs) => [
+ { type: 'VideoList', id: stableHash(queryArgs) },
+ { type: 'Board', id: queryArgs.board_id ?? 'none' },
+ 'FetchOnReconnect',
+ ],
+ async onQueryStarted(_, { dispatch, queryFulfilled }) {
+ // Pre-populate the per-video getVideoDTO cache so selection feels snappy.
+ const res = await queryFulfilled;
+ const videoDTOs = res.data.items;
+ const updates: Param0 = [];
+ for (const videoDTO of videoDTOs) {
+ updates.push({
+ endpointName: 'getVideoDTO',
+ arg: videoDTO.video_name,
+ value: videoDTO,
+ });
+ }
+ dispatch(videosApi.util.upsertQueryEntries(updates));
+ },
+ }),
+
+ getVideoDTO: build.query({
+ query: (video_name) => ({ url: buildVideosUrl(`i/${video_name}`) }),
+ providesTags: (result, error, video_name) => [{ type: 'Video', id: video_name }],
+ }),
+
+ getVideoMetadata: build.query({
+ query: (video_name) => ({ url: buildVideosUrl(`i/${video_name}/metadata`) }),
+ providesTags: (result, error, video_name) => [{ type: 'VideoMetadata', id: video_name }],
+ }),
+
+ getVideoNames: build.query({
+ query: (queryArgs) => ({
+ url: buildVideosUrl('names', queryArgs),
+ method: 'GET',
+ }),
+ providesTags: (result, error, queryArgs) => [
+ 'VideoNameList',
+ 'FetchOnReconnect',
+ { type: 'VideoNameList', id: stableHash(queryArgs) },
+ ],
+ }),
+
+ deleteVideo: build.mutation<
+ paths['/api/v1/videos/i/{video_name}']['delete']['responses']['200']['content']['application/json'],
+ paths['/api/v1/videos/i/{video_name}']['delete']['parameters']['path']
+ >({
+ query: ({ video_name }) => ({
+ url: buildVideosUrl(`i/${video_name}`),
+ method: 'DELETE',
+ }),
+ invalidatesTags: (result) => {
+ if (!result) {
+ return [];
+ }
+ return [
+ ...getTagsToInvalidateForBoardAffectingMutation(result.affected_boards),
+ { type: 'VideoList', id: LIST_TAG },
+ ];
+ },
+ }),
+
+ deleteVideos: build.mutation<
+ paths['/api/v1/videos/delete']['post']['responses']['200']['content']['application/json'],
+ paths['/api/v1/videos/delete']['post']['requestBody']['content']['application/json']
+ >({
+ query: (body) => ({
+ url: buildVideosUrl('delete'),
+ method: 'POST',
+ body,
+ }),
+ invalidatesTags: (result) => {
+ if (!result) {
+ return [];
+ }
+ return [
+ ...getTagsToInvalidateForBoardAffectingMutation(result.affected_boards),
+ { type: 'VideoList', id: LIST_TAG },
+ ];
+ },
+ }),
+
+ /** Toggle a video's is_intermediate flag. */
+ changeVideoIsIntermediate: build.mutation<
+ paths['/api/v1/videos/i/{video_name}']['patch']['responses']['200']['content']['application/json'],
+ { video_name: string; is_intermediate: boolean }
+ >({
+ query: ({ video_name, is_intermediate }) => ({
+ url: buildVideosUrl(`i/${video_name}`),
+ method: 'PATCH',
+ body: { is_intermediate },
+ }),
+ invalidatesTags: (result) => {
+ if (!result) {
+ return [];
+ }
+ return [
+ ...getTagsToInvalidateForVideoMutation([result.video_name]),
+ ...getTagsToInvalidateForBoardAffectingMutation([result.board_id ?? 'none']),
+ ];
+ },
+ }),
+
+ starVideos: build.mutation<
+ paths['/api/v1/videos/star']['post']['responses']['200']['content']['application/json'],
+ paths['/api/v1/videos/star']['post']['requestBody']['content']['application/json']
+ >({
+ query: (body) => ({
+ url: buildVideosUrl('star'),
+ method: 'POST',
+ body,
+ }),
+ invalidatesTags: (result) => {
+ if (!result) {
+ return [];
+ }
+ // ``starred_first=true`` gallery queries are cached under the LIST_TAG-scoped
+ // ``VideoList`` tag, so without this invalidation the freshly-starred video
+ // stays in its original position until some other mutation refetches the list.
+ return [
+ ...getTagsToInvalidateForVideoMutation(result.starred_videos),
+ ...getTagsToInvalidateForBoardAffectingMutation(result.affected_boards),
+ { type: 'VideoList', id: LIST_TAG },
+ ];
+ },
+ }),
+
+ unstarVideos: build.mutation<
+ paths['/api/v1/videos/unstar']['post']['responses']['200']['content']['application/json'],
+ paths['/api/v1/videos/unstar']['post']['requestBody']['content']['application/json']
+ >({
+ query: (body) => ({
+ url: buildVideosUrl('unstar'),
+ method: 'POST',
+ body,
+ }),
+ invalidatesTags: (result) => {
+ if (!result) {
+ return [];
+ }
+ return [
+ ...getTagsToInvalidateForVideoMutation(result.unstarred_videos),
+ ...getTagsToInvalidateForBoardAffectingMutation(result.affected_boards),
+ { type: 'VideoList', id: LIST_TAG },
+ ];
+ },
+ }),
+
+ uploadVideo: build.mutation<
+ paths['/api/v1/videos/upload']['post']['responses']['201']['content']['application/json'],
+ UploadVideoArg
+ >({
+ query: ({ file, video_category, is_intermediate, session_id, board_id, metadata }) => {
+ const formData = new FormData();
+ formData.append('file', file);
+ if (metadata) {
+ formData.append('metadata', JSON.stringify(metadata));
+ }
+ return {
+ url: buildVideosUrl('upload'),
+ method: 'POST',
+ body: formData,
+ params: {
+ video_category,
+ is_intermediate,
+ session_id,
+ board_id: board_id === 'none' ? undefined : board_id,
+ },
+ };
+ },
+ invalidatesTags: (result) => {
+ if (!result || result.is_intermediate) {
+ return [];
+ }
+ const boardId = result.board_id ?? 'none';
+ return [
+ ...getTagsToInvalidateForVideoMutation([result.video_name]),
+ ...getTagsToInvalidateForBoardAffectingMutation([boardId]),
+ { type: 'VideoList', id: LIST_TAG },
+ ];
+ },
+ }),
+
+ addVideoToBoard: build.mutation<
+ paths['/api/v1/videos/board']['post']['responses']['200']['content']['application/json'],
+ paths['/api/v1/videos/board']['post']['requestBody']['content']['application/json']
+ >({
+ query: (body) => ({
+ url: buildVideosUrl('board'),
+ method: 'POST',
+ body,
+ }),
+ invalidatesTags: (result) => {
+ if (!result) {
+ return [];
+ }
+ return [
+ ...getTagsToInvalidateForVideoMutation(result.added_videos),
+ ...getTagsToInvalidateForBoardAffectingMutation(result.affected_boards),
+ ];
+ },
+ }),
+
+ removeVideoFromBoard: build.mutation<
+ paths['/api/v1/videos/board']['delete']['responses']['200']['content']['application/json'],
+ paths['/api/v1/videos/board']['delete']['requestBody']['content']['application/json']
+ >({
+ query: (body) => ({
+ url: buildVideosUrl('board'),
+ method: 'DELETE',
+ body,
+ }),
+ invalidatesTags: (result) => {
+ if (!result) {
+ return [];
+ }
+ return [
+ ...getTagsToInvalidateForVideoMutation(result.removed_videos),
+ ...getTagsToInvalidateForBoardAffectingMutation(result.affected_boards),
+ ];
+ },
+ }),
+ }),
+});
+
+export const {
+ useUploadVideoMutation,
+ useGetVideoDTOQuery,
+ useStarVideosMutation,
+ useUnstarVideosMutation,
+ useAddVideoToBoardMutation,
+ useRemoveVideoFromBoardMutation,
+} = videosApi;
+
+/** @knipignore Reserved for follow-up phases (bulk delete / intermediate toggle / video-only views).
+ * useDeleteVideoMutation is here because the only call site uses videosApi.endpoints.deleteVideo.initiate
+ * via the delete-video modal, but a future bulk/multi-select flow may want the React hook form. */
+export const {
+ useListVideosQuery,
+ useGetVideoMetadataQuery,
+ useGetVideoNamesQuery,
+ useDeleteVideoMutation,
+ useDeleteVideosMutation,
+ useChangeVideoIsIntermediateMutation,
+} = videosApi;
+
+/**
+ * Imperative helper to fetch a VideoDTO. Mirrors `getImageDTOSafe`.
+ */
+export const getVideoDTOSafe = async (
+ video_name: string,
+ options?: Parameters[1]
+): Promise => {
+ const _options = { subscribe: false, ...options };
+ const req = getStore().dispatch(videosApi.endpoints.getVideoDTO.initiate(video_name, _options));
+ try {
+ return await req.unwrap();
+ } catch {
+ return null;
+ }
+};
+
+/** @knipignore Multi-phase rollout; consumed by Phase 5 viewer code. */
+export const getVideoDTO = (
+ video_name: string,
+ options?: Parameters[1]
+): Promise => {
+ const _options = { subscribe: false, ...options };
+ const req = getStore().dispatch(videosApi.endpoints.getVideoDTO.initiate(video_name, _options));
+ return req.unwrap();
+};
+
+/** @knipignore Multi-phase rollout; imperative form consumed by Phase 6 invocations. */
+export const uploadVideo = (arg: UploadVideoArg): Promise => {
+ const { dispatch } = getStore();
+ const req = dispatch(videosApi.endpoints.uploadVideo.initiate(arg, { track: false }));
+ return req.unwrap();
+};
+
+/**
+ * Uploads a batch of videos and resolves with the DTOs that succeeded.
+ *
+ * Rejections are NOT re-thrown, mirroring `uploadImages`: per-file failure feedback
+ * (an error toast naming the failed file) is handled by the `uploadVideo.matchRejected`
+ * listener in `videoUploaded.ts`, which fires for every rejected mutation regardless of
+ * how the caller aggregates the promises. Callers should treat the resolved array as
+ * "what actually made it" and must not assume it matches the request 1:1.
+ */
+export const uploadVideos = async (args: UploadVideoArg[]): Promise => {
+ const { dispatch } = getStore();
+ const results = await Promise.allSettled(
+ args.map((arg) => {
+ const req = dispatch(videosApi.endpoints.uploadVideo.initiate(arg, { track: false }));
+ return req.unwrap();
+ })
+ );
+ return results.filter((r): r is PromiseFulfilledResult => r.status === 'fulfilled').map((r) => r.value);
+};
+
+export const useVideoDTO = (videoName: string | null | undefined) => {
+ const { currentData: videoDTO } = useGetVideoDTOQuery(videoName ?? skipToken);
+ return videoDTO ?? null;
+};
diff --git a/invokeai/frontend/web/src/services/api/endpoints/virtual_boards.ts b/invokeai/frontend/web/src/services/api/endpoints/virtual_boards.ts
index b450bf84436..cb0e015547d 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/virtual_boards.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/virtual_boards.ts
@@ -1,5 +1,5 @@
import queryString from 'query-string';
-import type { ImageCategory } from 'services/api/types';
+import type { GetGalleryItemNamesResult, ImageCategory } from 'services/api/types';
import type { ApiTagDescription } from '..';
import { api, buildV1Url } from '..';
@@ -10,13 +10,9 @@ export type VirtualSubBoard = {
date: string;
image_count: number;
asset_count: number;
+ video_count: number;
cover_image_name: string | null;
-};
-
-type ImageNamesResult = {
- image_names: string[];
- starred_count: number;
- total_count: number;
+ cover_video_name: string | null;
};
const buildVirtualBoardsUrl = (path: string = '') => buildV1Url(`virtual_boards/${path}`);
@@ -30,8 +26,12 @@ const virtualBoardsApi = api.injectEndpoints({
providesTags: (): ApiTagDescription[] => ['VirtualBoards', 'FetchOnReconnect'],
}),
- getVirtualBoardImageNamesByDate: build.query<
- ImageNamesResult,
+ /**
+ * Polymorphic (image + video) refs for a virtual date board. Same result shape as the
+ * gallery's `getGalleryItemNames`, so the gallery grid can consume either transparently.
+ */
+ getVirtualBoardItemNamesByDate: build.query<
+ GetGalleryItemNamesResult,
{
date: string;
starred_first?: boolean;
@@ -42,15 +42,19 @@ const virtualBoardsApi = api.injectEndpoints({
>({
query: ({ date, ...params }) => ({
url: buildVirtualBoardsUrl(
- `by_date/${date}/image_names?${queryString.stringify(params, { arrayFormat: 'none', skipNull: true, skipEmptyString: true })}`
+ `by_date/${date}/item_names?${queryString.stringify(params, { arrayFormat: 'none', skipNull: true, skipEmptyString: true })}`
),
}),
+ // Both image and video mutations must refetch a virtual date's contents, so this
+ // provides the name-list tag of each kind plus the polymorphic one.
providesTags: (_result, _error, arg): ApiTagDescription[] => [
{ type: 'ImageNameList', id: `virtual_${arg.date}` },
+ { type: 'VideoNameList', id: `virtual_${arg.date}` },
+ 'GalleryItemNameList',
'FetchOnReconnect',
],
}),
}),
});
-export const { useListVirtualBoardsByDateQuery, useGetVirtualBoardImageNamesByDateQuery } = virtualBoardsApi;
+export const { useListVirtualBoardsByDateQuery, useGetVirtualBoardItemNamesByDateQuery } = virtualBoardsApi;
diff --git a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts b/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts
index 07e89a305d4..8de39061518 100644
--- a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts
+++ b/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts
@@ -38,6 +38,10 @@ import {
isTextLLMModelConfig,
isTIModelConfig,
isVAEModelConfigOrSubmodel,
+ isWanDiffusersMainModelConfig,
+ isWanGGUFLowNoiseMainModelConfig,
+ isWanT5EncoderModelConfig,
+ isWanVAEModelConfig,
isZImageDiffusersMainModelConfig,
} from 'services/api/types';
@@ -113,6 +117,10 @@ export const useQwenImageDiffusersModels = () => buildModelsHook(isQwenImageDiff
export const useQwenImageVAEModels = () => buildModelsHook(isQwenImageVAEModelConfig)();
export const useQwenVLEncoderModels = () => buildModelsHook(isQwenVLEncoderModelConfig)();
export const useQwen3EncoderModels = () => buildModelsHook(isQwen3EncoderModelConfig)();
+export const useWanDiffusersModels = () => buildModelsHook(isWanDiffusersMainModelConfig)();
+export const useWanGGUFLowNoiseModels = () => buildModelsHook(isWanGGUFLowNoiseMainModelConfig)();
+export const useWanVAEModels = () => buildModelsHook(isWanVAEModelConfig)();
+export const useWanT5EncoderModels = () => buildModelsHook(isWanT5EncoderModelConfig)();
export const useGlobalReferenceImageModels = buildModelsHook(
(config) => isIPAdapterModelConfig(config) || isFluxReduxModelConfig(config) || isFluxKontextModelConfig(config)
);
@@ -155,5 +163,8 @@ export const selectZImageDiffusersModels = buildModelsSelector(isZImageDiffusers
export const selectFlux2DiffusersModels = buildModelsSelector(isFlux2DiffusersMainModelConfig);
export const selectFluxVAEModels = buildModelsSelector(isFluxVAEModelConfig);
export const selectAnimaVAEModels = buildModelsSelector(isAnimaVAEModelConfig);
+export const selectWanDiffusersModels = buildModelsSelector(isWanDiffusersMainModelConfig);
+export const selectWanVAEModels = buildModelsSelector(isWanVAEModelConfig);
+export const selectWanT5EncoderModels = buildModelsSelector(isWanT5EncoderModelConfig);
export const useTextLLMModels = () => buildModelsHook(isTextLLMModelConfig)();
export const useLlavaModels = () => buildModelsHook(isLLaVAModelConfig)();
diff --git a/invokeai/frontend/web/src/services/api/index.ts b/invokeai/frontend/web/src/services/api/index.ts
index 8df82602e8c..b493f2dd314 100644
--- a/invokeai/frontend/web/src/services/api/index.ts
+++ b/invokeai/frontend/web/src/services/api/index.ts
@@ -17,6 +17,7 @@ const tagTypes = [
'Board',
'BoardImagesTotal',
'BoardAssetsTotal',
+ 'BoardVideosTotal',
'HFTokenStatus',
'Image',
'ImageNameList',
@@ -63,6 +64,15 @@ const tagTypes = [
'UserList',
'CustomNodePacks',
'VirtualBoards',
+ // Video tags (parallel to Image tags).
+ 'Video',
+ 'VideoList',
+ 'VideoMetadata',
+ 'VideoNameList',
+ 'BoardVideosTotal',
+ // Polymorphic gallery list (images + videos interleaved by created_at).
+ 'GalleryItemList',
+ 'GalleryItemNameList',
] as const;
export type ApiTagDescription = TagDescription<(typeof tagTypes)[number]>;
export const LIST_TAG = 'LIST';
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index f938b7f0f2c..4cf7f1c6a6b 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -1415,6 +1415,276 @@ export type paths = {
patch?: never;
trace?: never;
};
+ "/api/v1/videos/upload": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /**
+ * Upload Video
+ * @description Uploads a video for the current user.
+ */
+ post: operations["upload_video"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/i/{video_name}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Get Video Dto */
+ get: operations["get_video_dto"];
+ put?: never;
+ post?: never;
+ /** Delete Video */
+ delete: operations["delete_video"];
+ options?: never;
+ head?: never;
+ /** Update Video */
+ patch: operations["update_video"];
+ trace?: never;
+ };
+ "/api/v1/videos/delete": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Delete Videos From List */
+ post: operations["delete_videos_from_list"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/i/{video_name}/metadata": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Get Video Metadata */
+ get: operations["get_video_metadata"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/i/{video_name}/full": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Get Video Full
+ * @description Serves the video file with HTTP Range support so HTML5 seek/scrub works.
+ *
+ * Browser media requests authenticate with the path-scoped HttpOnly cookie set at login.
+ */
+ get: operations["get_video_full"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ /**
+ * Get Video Full
+ * @description Serves the video file with HTTP Range support so HTML5 seek/scrub works.
+ *
+ * Browser media requests authenticate with the path-scoped HttpOnly cookie set at login.
+ */
+ head: operations["get_video_full_head"];
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/i/{video_name}/thumbnail": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Get Video Thumbnail
+ * @description Returns the first-frame WebP thumbnail of an authorized video.
+ */
+ get: operations["get_video_thumbnail"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/i/{video_name}/urls": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** Get Video Urls */
+ get: operations["get_video_urls"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * List Video Dtos
+ * @description Gets a list of video DTOs for the current user.
+ */
+ get: operations["list_video_dtos"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/names": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Get Video Names
+ * @description Gets ordered list of video names with metadata for optimistic updates.
+ */
+ get: operations["get_video_names"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/star": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Star Videos In List */
+ post: operations["star_videos_in_list"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/unstar": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Unstar Videos In List */
+ post: operations["unstar_videos_in_list"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/videos/board": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** Add Video To Board */
+ post: operations["add_video_to_board"];
+ /** Remove Video From Board */
+ delete: operations["remove_video_from_board"];
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/gallery/items/": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * List Gallery Items
+ * @description Returns a paginated, time-sorted stream of polymorphic gallery items (images + videos).
+ */
+ get: operations["list_gallery_items"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/gallery/items/names": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Get Gallery Item Names
+ * @description Returns an ordered (kind, name) list — used to drive virtualized gallery selection.
+ */
+ get: operations["get_gallery_item_names"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/api/v1/boards/": {
parameters: {
query?: never;
@@ -1560,7 +1830,7 @@ export type paths = {
};
/**
* List Virtual Boards By Date
- * @description Gets a list of virtual sub-boards grouped by date.
+ * @description Gets a list of virtual sub-boards grouped by date. Covers both images and videos.
*/
get: operations["list_virtual_boards_by_date"];
put?: never;
@@ -1580,7 +1850,8 @@ export type paths = {
};
/**
* List Virtual Board Image Names By Date
- * @description Gets ordered image names for a specific date.
+ * @description Gets ordered image names for a specific date. Image-only; kept for API compatibility —
+ * the UI uses the polymorphic `/by_date/{date}/item_names` endpoint.
*/
get: operations["list_virtual_board_image_names_by_date"];
put?: never;
@@ -1591,6 +1862,26 @@ export type paths = {
patch?: never;
trace?: never;
};
+ "/api/v1/virtual_boards/by_date/{date}/item_names": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * List Virtual Board Item Names By Date
+ * @description Gets ordered polymorphic (image + video) item refs for a specific date.
+ */
+ get: operations["list_virtual_board_item_names_by_date"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/api/v1/model_relationships/i/{model_key}": {
parameters: {
query?: never;
@@ -2912,6 +3203,19 @@ export type components = {
*/
type: "add";
};
+ /** AddVideosToBoardResult */
+ AddVideosToBoardResult: {
+ /**
+ * Affected Boards
+ * @description The ids of boards affected by the operation
+ */
+ affected_boards: string[];
+ /**
+ * Added Videos
+ * @description The video names that were added to the board
+ */
+ added_videos: string[];
+ };
/**
* AdminUserCreateRequest
* @description Request body for admin to create a new user.
@@ -3741,7 +4045,7 @@ export type components = {
*/
type: "anima_text_encoder";
};
- AnyModelConfig: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ AnyModelConfig: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
/**
* AppVersion
* @description App Version Response
@@ -3893,7 +4197,7 @@ export type components = {
* fallback/null value `BaseModelType.Any` for these models, instead of making the model base optional.
* @enum {string}
*/
- BaseModelType: "any" | "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "flux2" | "cogview4" | "z-image" | "external" | "qwen-image" | "anima" | "unknown";
+ BaseModelType: "any" | "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "flux2" | "cogview4" | "z-image" | "external" | "qwen-image" | "anima" | "wan" | "unknown";
/** Batch */
Batch: {
/**
@@ -3943,7 +4247,7 @@ export type components = {
* Items
* @description The list of items to substitute into the node/field.
*/
- items?: (string | number | components["schemas"]["ImageField"] | (string | number | components["schemas"]["ImageField"])[])[];
+ items?: (string | number | components["schemas"]["ImageField"] | components["schemas"]["VideoField"] | (string | number | components["schemas"]["ImageField"] | components["schemas"]["VideoField"])[])[];
};
/**
* BatchEnqueuedEvent
@@ -4240,11 +4544,22 @@ export type components = {
* @default private
*/
board_visibility?: components["schemas"]["BoardVisibility"];
+ /**
+ * Cover Video Name
+ * @description The name of the board's cover video, when the most recent item is a video.
+ */
+ cover_video_name?: string | null;
/**
* Image Count
* @description The number of images in the board.
*/
image_count: number;
+ /**
+ * Video Count
+ * @description The number of videos in the board.
+ * @default 0
+ */
+ video_count?: number;
/**
* Asset Count
* @description The number of assets in the board.
@@ -4357,6 +4672,14 @@ export type components = {
*/
image_names: string[];
};
+ /** Body_delete_videos_from_list */
+ Body_delete_videos_from_list: {
+ /**
+ * Video Names
+ * @description The list of names of videos to delete
+ */
+ video_names: string[];
+ };
/** Body_do_hf_login */
Body_do_hf_login: {
/**
@@ -4480,6 +4803,14 @@ export type components = {
*/
image_names: string[];
};
+ /** Body_remove_video_from_board */
+ Body_remove_video_from_board: {
+ /**
+ * Video Name
+ * @description The name of the video to remove from its board
+ */
+ video_name: string;
+ };
/** Body_set_workflow_thumbnail */
Body_set_workflow_thumbnail: {
/**
@@ -4497,6 +4828,14 @@ export type components = {
*/
image_names: string[];
};
+ /** Body_star_videos_in_list */
+ Body_star_videos_in_list: {
+ /**
+ * Video Names
+ * @description The list of names of videos to star
+ */
+ video_names: string[];
+ };
/** Body_unstar_images_in_list */
Body_unstar_images_in_list: {
/**
@@ -4505,6 +4844,14 @@ export type components = {
*/
image_names: string[];
};
+ /** Body_unstar_videos_in_list */
+ Body_unstar_videos_in_list: {
+ /**
+ * Video Names
+ * @description The list of names of videos to unstar
+ */
+ video_names: string[];
+ };
/** Body_update_model_image */
Body_update_model_image: {
/**
@@ -4558,6 +4905,19 @@ export type components = {
*/
metadata?: string | null;
};
+ /** Body_upload_video */
+ Body_upload_video: {
+ /**
+ * File
+ * Format: binary
+ */
+ file: Blob;
+ /**
+ * Metadata
+ * @description The metadata to associate with the video, must be a stringified JSON dict
+ */
+ metadata?: string | null;
+ };
/**
* Boolean Collection Primitive
* @description A collection of boolean primitive values
@@ -7879,7 +8239,7 @@ export type components = {
* @description The generation mode that output this image
* @default null
*/
- generation_mode?: ("txt2img" | "img2img" | "inpaint" | "outpaint" | "sdxl_txt2img" | "sdxl_img2img" | "sdxl_inpaint" | "sdxl_outpaint" | "flux_txt2img" | "flux_img2img" | "flux_inpaint" | "flux_outpaint" | "flux2_txt2img" | "flux2_img2img" | "flux2_inpaint" | "flux2_outpaint" | "sd3_txt2img" | "sd3_img2img" | "sd3_inpaint" | "sd3_outpaint" | "cogview4_txt2img" | "cogview4_img2img" | "cogview4_inpaint" | "cogview4_outpaint" | "z_image_txt2img" | "z_image_img2img" | "z_image_inpaint" | "z_image_outpaint" | "qwen_image_txt2img" | "qwen_image_img2img" | "qwen_image_inpaint" | "qwen_image_outpaint" | "anima_txt2img" | "anima_img2img" | "anima_inpaint" | "anima_outpaint") | null;
+ generation_mode?: ("txt2img" | "img2img" | "inpaint" | "outpaint" | "sdxl_txt2img" | "sdxl_img2img" | "sdxl_inpaint" | "sdxl_outpaint" | "flux_txt2img" | "flux_img2img" | "flux_inpaint" | "flux_outpaint" | "flux2_txt2img" | "flux2_img2img" | "flux2_inpaint" | "flux2_outpaint" | "sd3_txt2img" | "sd3_img2img" | "sd3_inpaint" | "sd3_outpaint" | "cogview4_txt2img" | "cogview4_img2img" | "cogview4_inpaint" | "cogview4_outpaint" | "z_image_txt2img" | "z_image_img2img" | "z_image_inpaint" | "z_image_outpaint" | "qwen_image_txt2img" | "qwen_image_img2img" | "qwen_image_inpaint" | "qwen_image_outpaint" | "anima_txt2img" | "anima_img2img" | "anima_inpaint" | "anima_outpaint" | "wan_txt2img" | "wan_img2img" | "wan_inpaint" | "wan_outpaint" | "wan_i2v") | null;
/**
* Positive Prompt
* @description The positive prompt parameter
@@ -8511,6 +8871,16 @@ export type components = {
* @description The names of the images that were deleted.
*/
deleted_images: string[];
+ /**
+ * Deleted Board Videos
+ * @description The video names of the board-videos relationships that were deleted.
+ */
+ deleted_board_videos?: string[];
+ /**
+ * Deleted Videos
+ * @description The names of the videos that were deleted.
+ */
+ deleted_videos?: string[];
};
/**
* DeleteByDestinationResult
@@ -8565,6 +8935,19 @@ export type components = {
[key: string]: string;
};
};
+ /** DeleteVideosResult */
+ DeleteVideosResult: {
+ /**
+ * Affected Boards
+ * @description The ids of boards affected by the operation
+ */
+ affected_boards: string[];
+ /**
+ * Deleted Videos
+ * @description The names of the videos that were deleted
+ */
+ deleted_videos: string[];
+ };
/**
* Denoise - SD1.5, SDXL
* @description Denoises noisy latents to decodable images
@@ -9720,6 +10103,132 @@ export type components = {
/** Height */
height: number;
};
+ /**
+ * Frame Range from Video
+ * @description Trim a video to a contiguous frame range and re-encode as MP4.
+ *
+ * Both bounds are inclusive and 0-based — ``start_frame=10, end_frame=50``
+ * emits 41 frames. Negative indices count from the end (``end_frame=-1``
+ * is the final frame), matching ``video_frame_extract``. The output frame
+ * rate defaults to the source video's frame rate; set ``fps=0`` to inherit
+ * it (or 16 fps if the source rate can't be probed).
+ *
+ * The resolved (positive) ``start_frame`` and ``end_frame`` are also emitted as
+ * outputs, so chained workflows can re-use the boundary indices — e.g. feeding
+ * them into a downstream Frame from Video to extract the same boundary frame.
+ */
+ ExtractVideoRangeInvocation: {
+ /**
+ * @description The board to save the image to
+ * @default null
+ */
+ board?: components["schemas"]["BoardField"] | null;
+ /**
+ * @description Optional metadata to be saved with the image
+ * @default null
+ */
+ metadata?: components["schemas"]["MetadataField"] | null;
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description The video to extract a frame range from.
+ * @default null
+ */
+ video?: components["schemas"]["VideoField"] | null;
+ /**
+ * Start Frame
+ * @description First frame to keep, inclusive. 0 = first frame. Negative indices count from the end.
+ * @default 0
+ */
+ start_frame?: number;
+ /**
+ * End Frame
+ * @description Last frame to keep, inclusive. -1 = last frame. Negative indices count from the end.
+ * @default -1
+ */
+ end_frame?: number;
+ /**
+ * Fps
+ * @description Output frame rate. 0 = match the source video's frame rate (falls back to 16 fps if the source rate can't be probed).
+ * @default 0
+ */
+ fps?: number;
+ /**
+ * type
+ * @default extract_video_range
+ * @constant
+ */
+ type: "extract_video_range";
+ };
+ /**
+ * ExtractVideoRangeOutput
+ * @description Output of ``extract_video_range``: a trimmed video plus the resolved frame indices.
+ *
+ * Mirrors ``VideoOutput`` so the video can be piped directly into Concatenate Videos or
+ * any other ``VideoField``-consuming node, and additionally exposes the resolved
+ * (positive, clamped) start and end indices so chained workflows can feed them back in
+ * — e.g. drive a downstream Frame from Video to pull the same boundary frame.
+ */
+ ExtractVideoRangeOutput: {
+ /** @description The trimmed video */
+ video: components["schemas"]["VideoField"];
+ /**
+ * Width
+ * @description The width of the video in pixels
+ */
+ width: number;
+ /**
+ * Height
+ * @description The height of the video in pixels
+ */
+ height: number;
+ /**
+ * Num Frames
+ * @description The number of frames in the trimmed video
+ */
+ num_frames: number;
+ /**
+ * Fps
+ * @description The frames-per-second of the trimmed video
+ */
+ fps: number;
+ /**
+ * Duration
+ * @description The duration of the trimmed video in seconds
+ */
+ duration: number;
+ /**
+ * Start Frame
+ * @description The resolved (positive, 0-based) start frame index in the source video
+ */
+ start_frame: number;
+ /**
+ * End Frame
+ * @description The resolved (positive, 0-based) end frame index in the source video
+ */
+ end_frame: number;
+ /**
+ * type
+ * @default extract_video_range_output
+ * @constant
+ */
+ type: "extract_video_range_output";
+ };
/**
* Apply LoRA Collection - FLUX
* @description Applies a collection of LoRAs to a FLUX transformer.
@@ -12385,6 +12894,113 @@ export type components = {
*/
type: "freeu";
};
+ /**
+ * GalleryItem
+ * @description A gallery item — either an image or a video, with shared fields and a discriminator.
+ *
+ * Frontend code should dispatch on `kind` to render image- vs video-specific UI.
+ */
+ GalleryItem: {
+ /** @description Whether the item is an image or video. */
+ kind: components["schemas"]["GalleryItemKind"];
+ /**
+ * Name
+ * @description The unique name of the image or video.
+ */
+ name: string;
+ /**
+ * Full Url
+ * @description URL to the full-resolution image PNG or the full-quality video MP4.
+ */
+ full_url: string;
+ /**
+ * Thumbnail Url
+ * @description URL to the static (WebP) thumbnail.
+ */
+ thumbnail_url: string;
+ /**
+ * Width
+ * @description The width of the item in pixels.
+ */
+ width: number;
+ /**
+ * Height
+ * @description The height of the item in pixels.
+ */
+ height: number;
+ /** @description The category of the item (images and videos share the same enum). */
+ category: components["schemas"]["ImageCategory"];
+ /**
+ * Starred
+ * @description Whether the item is starred.
+ */
+ starred: boolean;
+ /**
+ * Is Intermediate
+ * @description Whether the item is an intermediate output.
+ */
+ is_intermediate: boolean;
+ /**
+ * Board Id
+ * @description Owning board id, if any.
+ */
+ board_id?: string | null;
+ /**
+ * Created At
+ * @description The created timestamp of the item.
+ */
+ created_at: string;
+ /**
+ * Duration
+ * @description Video duration in seconds. None for images.
+ */
+ duration?: number | null;
+ /**
+ * Fps
+ * @description Video frames per second. None for images.
+ */
+ fps?: number | null;
+ };
+ /**
+ * GalleryItemKind
+ * @description Discriminator for polymorphic gallery items.
+ * @enum {string}
+ */
+ GalleryItemKind: "image" | "video";
+ /**
+ * GalleryItemNamesResult
+ * @description Ordered list of gallery item references plus counts for optimistic UI.
+ */
+ GalleryItemNamesResult: {
+ /**
+ * Items
+ * @description Ordered list of (kind, name) references.
+ */
+ items: components["schemas"]["GalleryItemRef"][];
+ /**
+ * Starred Count
+ * @description Number of starred items (when starred_first=True).
+ */
+ starred_count: number;
+ /**
+ * Total Count
+ * @description Total number of items matching the query.
+ */
+ total_count: number;
+ };
+ /**
+ * GalleryItemRef
+ * @description A thin reference to a gallery item — used for ordered name lists.
+ */
+ GalleryItemRef: {
+ /** @description Whether the item is an image or video. */
+ kind: components["schemas"]["GalleryItemKind"];
+ /**
+ * Name
+ * @description The unique name of the image or video.
+ */
+ name: string;
+ };
/**
* Gemini Image Generation
* @description Generate images using a Gemini-hosted external model.
@@ -12599,7 +13215,7 @@ export type components = {
* @description The nodes in this graph
*/
nodes?: {
- [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
};
/**
* Edges
@@ -12636,7 +13252,7 @@ export type components = {
* @description The results of node executions
*/
results: {
- [key: string]: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"];
+ [key: string]: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["ExtractVideoRangeOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["VideoOutput"] | components["schemas"]["WanConditioningOutput"] | components["schemas"]["WanLoRALoaderOutput"] | components["schemas"]["WanModelLoaderOutput"] | components["schemas"]["WanRefImageOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"];
};
/**
* Errors
@@ -16076,7 +16692,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -16086,7 +16702,7 @@ export type components = {
* Result
* @description The result of the invocation
*/
- result: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"];
+ result: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["ExtractVideoRangeOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["VideoOutput"] | components["schemas"]["WanConditioningOutput"] | components["schemas"]["WanLoRALoaderOutput"] | components["schemas"]["WanModelLoaderOutput"] | components["schemas"]["WanRefImageOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"];
};
/**
* InvocationErrorEvent
@@ -16140,7 +16756,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -16218,6 +16834,7 @@ export type components = {
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
esrgan: components["schemas"]["ImageOutput"];
expand_mask_with_fade: components["schemas"]["ImageOutput"];
+ extract_video_range: components["schemas"]["ExtractVideoRangeOutput"];
face_identifier: components["schemas"]["ImageOutput"];
face_mask_detection: components["schemas"]["FaceMaskOutput"];
face_off: components["schemas"]["FaceOffOutput"];
@@ -16410,6 +17027,21 @@ export type components = {
unsharp_mask: components["schemas"]["ImageOutput"];
unsharp_mask_oklab: components["schemas"]["ImageOutput"];
vae_loader: components["schemas"]["VAEOutput"];
+ video: components["schemas"]["VideoOutput"];
+ video_concat: components["schemas"]["VideoOutput"];
+ video_frame_extract: components["schemas"]["ImageOutput"];
+ wan_denoise: components["schemas"]["LatentsOutput"];
+ wan_i2l: components["schemas"]["LatentsOutput"];
+ wan_i2v_ideal_dimensions: components["schemas"]["IdealSizeOutput"];
+ wan_l2i: components["schemas"]["ImageOutput"];
+ wan_l2v: components["schemas"]["VideoOutput"];
+ wan_lora_collection_loader: components["schemas"]["WanLoRALoaderOutput"];
+ wan_lora_loader: components["schemas"]["WanLoRALoaderOutput"];
+ wan_model_loader: components["schemas"]["WanModelLoaderOutput"];
+ wan_ref_image_encoder: components["schemas"]["WanRefImageOutput"];
+ wan_text_encoder: components["schemas"]["WanConditioningOutput"];
+ wan_ti2v_ideal_dimensions: components["schemas"]["IdealSizeOutput"];
+ wan_video_denoise: components["schemas"]["LatentsOutput"];
workflow_return: components["schemas"]["WorkflowReturnOutput"];
workflow_return_get: components["schemas"]["WorkflowReturnGetOutput"];
workflow_return_value: components["schemas"]["WorkflowReturnValueOutput"];
@@ -16476,7 +17108,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -16551,7 +17183,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -19460,6 +20092,104 @@ export type components = {
*/
base: "sdxl";
};
+ /**
+ * LoRA_LyCORIS_Wan_Config
+ * @description Model config for Wan 2.2 LoRA models in LyCORIS format.
+ *
+ * Wan LoRAs target ``WanTransformer3DModel`` blocks. The Wan 2.2 A14B family
+ * is dual-expert (high-noise + low-noise) — LoRAs are typically trained
+ * against one expert. ``expert`` records which one so the model loader
+ * invocation can wire it to the correct ``loras`` / ``loras_low_noise`` list.
+ * Many LoRAs are expert-agnostic (TI2V-5B family, or community LoRAs that
+ * just don't tag the expert) — these get ``expert=None`` and are applied to
+ * both experts by default.
+ */
+ LoRA_LyCORIS_Wan_Config: {
+ /**
+ * Key
+ * @description A unique key for this model.
+ */
+ key: string;
+ /**
+ * Hash
+ * @description The hash of the model file(s).
+ */
+ hash: string;
+ /**
+ * Path
+ * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.
+ */
+ path: string;
+ /**
+ * File Size
+ * @description The size of the model in bytes.
+ */
+ file_size: number;
+ /**
+ * Name
+ * @description Name of the model.
+ */
+ name: string;
+ /**
+ * Description
+ * @description Model description
+ */
+ description: string | null;
+ /**
+ * Source
+ * @description The original source of the model (path, URL or repo_id).
+ */
+ source: string;
+ /** @description The type of source */
+ source_type: components["schemas"]["ModelSourceType"];
+ /**
+ * Source Api Response
+ * @description The original API response from the source, as stringified JSON.
+ */
+ source_api_response: string | null;
+ /**
+ * Source Url
+ * @description Optional URL for the model (e.g. download page or model page).
+ */
+ source_url: string | null;
+ /**
+ * Cover Image
+ * @description Url for image to preview model
+ */
+ cover_image: string | null;
+ /**
+ * Type
+ * @default lora
+ * @constant
+ */
+ type: "lora";
+ /**
+ * Trigger Phrases
+ * @description Set of trigger phrases for this model
+ */
+ trigger_phrases: string[] | null;
+ /** @description Default settings for this model */
+ default_settings: components["schemas"]["LoraModelDefaultSettings"] | null;
+ /**
+ * Format
+ * @default lycoris
+ * @constant
+ */
+ format: "lycoris";
+ /**
+ * Base
+ * @default wan
+ * @constant
+ */
+ base: "wan";
+ /**
+ * Expert
+ * @description For Wan 2.2 A14B dual-expert LoRAs: 'high' targets the high-noise expert, 'low' targets the low-noise expert. None means the LoRA is expert-agnostic (TI2V-5B, or community LoRAs without explicit tagging) and is applied to both.
+ */
+ expert: ("high" | "low") | null;
+ /** @description The Wan model family this LoRA targets, detected from its inner-dim (5120 -> A14B, 3072 -> TI2V-5B). A14B LoRAs are incompatible with TI2V-5B mains (and vice versa) — they crash with a shape mismatch in the layer patcher. The linear-view graph builder filters LoRAs on variant when building the LoRA collection. None means the LoRA's inner-dim couldn't be identified. */
+ variant: components["schemas"]["WanLoRAVariantType"] | null;
+ };
/**
* LoRA_LyCORIS_ZImage_Config
* @description Model config for Z-Image LoRA models in LyCORIS format.
@@ -21641,10 +22371,14 @@ export type components = {
base: "sdxl";
};
/**
- * Main_Diffusers_ZImage_Config
- * @description Model config for Z-Image diffusers models (Z-Image-Turbo, Z-Image-Base).
+ * Main_Diffusers_Wan_Config
+ * @description Model config for Wan 2.2 diffusers models.
+ *
+ * Covers both the dual-expert T2V-A14B family and the single-transformer TI2V-5B
+ * family. Variant is detected from the on-disk transformer config (latent channel
+ * count) plus the presence of a sibling ``transformer_2/`` directory.
*/
- Main_Diffusers_ZImage_Config: {
+ Main_Diffusers_Wan_Config: {
/**
* Key
* @description A unique key for this model.
@@ -21720,17 +22454,28 @@ export type components = {
repo_variant: components["schemas"]["ModelRepoVariant"];
/**
* Base
- * @default z-image
+ * @default wan
* @constant
*/
- base: "z-image";
- variant: components["schemas"]["ZImageVariantType"];
+ base: "wan";
+ variant: components["schemas"]["WanVariantType"];
+ /**
+ * Has Dual Expert
+ * @description Whether this model ships two transformer experts (Wan 2.2 A14B MoE). False for TI2V-5B.
+ * @default false
+ */
+ has_dual_expert: boolean;
+ /**
+ * Boundary Ratio
+ * @description MoE expert switch point as a fraction of num_train_timesteps (typically 1000). None for single-transformer models. Read from model_index.json by Diffusers' WanPipeline.
+ */
+ boundary_ratio: number | null;
};
/**
- * Main_GGUF_FLUX_Config
- * @description Model config for main checkpoint models.
+ * Main_Diffusers_ZImage_Config
+ * @description Model config for Z-Image diffusers models (Z-Image-Turbo, Z-Image-Base).
*/
- Main_GGUF_FLUX_Config: {
+ Main_Diffusers_ZImage_Config: {
/**
* Key
* @description A unique key for this model.
@@ -21797,29 +22542,115 @@ export type components = {
/** @description Default settings for this model */
default_settings: components["schemas"]["MainModelDefaultSettings"] | null;
/**
- * Config Path
- * @description Path to the config for this model, if any.
- */
- config_path: string | null;
- /**
- * Base
- * @default flux
+ * Format
+ * @default diffusers
* @constant
*/
- base: "flux";
+ format: "diffusers";
+ /** @default */
+ repo_variant: components["schemas"]["ModelRepoVariant"];
/**
- * Format
- * @default gguf_quantized
+ * Base
+ * @default z-image
* @constant
*/
- format: "gguf_quantized";
- variant: components["schemas"]["FluxVariantType"];
+ base: "z-image";
+ variant: components["schemas"]["ZImageVariantType"];
};
/**
- * Main_GGUF_Flux2_Config
- * @description Model config for GGUF-quantized FLUX.2 checkpoint models (e.g. Klein).
+ * Main_GGUF_FLUX_Config
+ * @description Model config for main checkpoint models.
*/
- Main_GGUF_Flux2_Config: {
+ Main_GGUF_FLUX_Config: {
+ /**
+ * Key
+ * @description A unique key for this model.
+ */
+ key: string;
+ /**
+ * Hash
+ * @description The hash of the model file(s).
+ */
+ hash: string;
+ /**
+ * Path
+ * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.
+ */
+ path: string;
+ /**
+ * File Size
+ * @description The size of the model in bytes.
+ */
+ file_size: number;
+ /**
+ * Name
+ * @description Name of the model.
+ */
+ name: string;
+ /**
+ * Description
+ * @description Model description
+ */
+ description: string | null;
+ /**
+ * Source
+ * @description The original source of the model (path, URL or repo_id).
+ */
+ source: string;
+ /** @description The type of source */
+ source_type: components["schemas"]["ModelSourceType"];
+ /**
+ * Source Api Response
+ * @description The original API response from the source, as stringified JSON.
+ */
+ source_api_response: string | null;
+ /**
+ * Source Url
+ * @description Optional URL for the model (e.g. download page or model page).
+ */
+ source_url: string | null;
+ /**
+ * Cover Image
+ * @description Url for image to preview model
+ */
+ cover_image: string | null;
+ /**
+ * Type
+ * @default main
+ * @constant
+ */
+ type: "main";
+ /**
+ * Trigger Phrases
+ * @description Set of trigger phrases for this model
+ */
+ trigger_phrases: string[] | null;
+ /** @description Default settings for this model */
+ default_settings: components["schemas"]["MainModelDefaultSettings"] | null;
+ /**
+ * Config Path
+ * @description Path to the config for this model, if any.
+ */
+ config_path: string | null;
+ /**
+ * Base
+ * @default flux
+ * @constant
+ */
+ base: "flux";
+ /**
+ * Format
+ * @default gguf_quantized
+ * @constant
+ */
+ format: "gguf_quantized";
+ variant: components["schemas"]["FluxVariantType"];
+ };
+ /**
+ * Main_GGUF_Flux2_Config
+ * @description Model config for GGUF-quantized FLUX.2 checkpoint models (e.g. Klein).
+ */
+ Main_GGUF_Flux2_Config: {
/**
* Key
* @description A unique key for this model.
@@ -21993,6 +22824,106 @@ export type components = {
format: "gguf_quantized";
variant: components["schemas"]["QwenImageVariantType"] | null;
};
+ /**
+ * Main_GGUF_Wan_Config
+ * @description Model config for GGUF-quantized Wan 2.2 transformer models.
+ *
+ * A14B's MoE ships as two GGUF files (one per expert); ``expert`` records
+ * which one this is so the model loader invocation can pair them. TI2V-5B
+ * is a single-transformer model and stores ``expert='none'``.
+ */
+ Main_GGUF_Wan_Config: {
+ /**
+ * Key
+ * @description A unique key for this model.
+ */
+ key: string;
+ /**
+ * Hash
+ * @description The hash of the model file(s).
+ */
+ hash: string;
+ /**
+ * Path
+ * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.
+ */
+ path: string;
+ /**
+ * File Size
+ * @description The size of the model in bytes.
+ */
+ file_size: number;
+ /**
+ * Name
+ * @description Name of the model.
+ */
+ name: string;
+ /**
+ * Description
+ * @description Model description
+ */
+ description: string | null;
+ /**
+ * Source
+ * @description The original source of the model (path, URL or repo_id).
+ */
+ source: string;
+ /** @description The type of source */
+ source_type: components["schemas"]["ModelSourceType"];
+ /**
+ * Source Api Response
+ * @description The original API response from the source, as stringified JSON.
+ */
+ source_api_response: string | null;
+ /**
+ * Source Url
+ * @description Optional URL for the model (e.g. download page or model page).
+ */
+ source_url: string | null;
+ /**
+ * Cover Image
+ * @description Url for image to preview model
+ */
+ cover_image: string | null;
+ /**
+ * Type
+ * @default main
+ * @constant
+ */
+ type: "main";
+ /**
+ * Trigger Phrases
+ * @description Set of trigger phrases for this model
+ */
+ trigger_phrases: string[] | null;
+ /** @description Default settings for this model */
+ default_settings: components["schemas"]["MainModelDefaultSettings"] | null;
+ /**
+ * Config Path
+ * @description Path to the config for this model, if any.
+ */
+ config_path: string | null;
+ /**
+ * Base
+ * @default wan
+ * @constant
+ */
+ base: "wan";
+ /**
+ * Format
+ * @default gguf_quantized
+ * @constant
+ */
+ format: "gguf_quantized";
+ variant: components["schemas"]["WanVariantType"];
+ /**
+ * Expert
+ * @description For Wan 2.2 A14B's dual-expert MoE: 'high' for the high-noise expert, 'low' for the low-noise expert. 'none' for single-transformer models (TI2V-5B).
+ * @default none
+ * @enum {string}
+ */
+ expert: "high" | "low" | "none";
+ };
/**
* Main_GGUF_ZImage_Config
* @description Model config for GGUF-quantized Z-Image transformer models.
@@ -23773,7 +24704,7 @@ export type components = {
* @description Storage format of model.
* @enum {string}
*/
- ModelFormat: "omi" | "diffusers" | "checkpoint" | "lycoris" | "onnx" | "olive" | "embedding_file" | "embedding_folder" | "invokeai" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "bnb_quantized_int8b" | "bnb_quantized_nf4b" | "gguf_quantized" | "external_api" | "unknown";
+ ModelFormat: "omi" | "diffusers" | "checkpoint" | "lycoris" | "onnx" | "olive" | "embedding_file" | "embedding_folder" | "invokeai" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "wan_t5_encoder" | "bnb_quantized_int8b" | "bnb_quantized_nf4b" | "gguf_quantized" | "external_api" | "unknown";
/** ModelIdentifierField */
ModelIdentifierField: {
/**
@@ -23910,7 +24841,7 @@ export type components = {
* Config
* @description The installed model's config
*/
- config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
};
/**
* ModelInstallDownloadProgressEvent
@@ -24076,7 +25007,7 @@ export type components = {
* Config Out
* @description After successful installation, this will hold the configuration object.
*/
- config_out?: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]) | null;
+ config_out?: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]) | null;
/**
* Inplace
* @description Leave model in its current location; otherwise install under models directory
@@ -24162,7 +25093,7 @@ export type components = {
* Config
* @description The model's config
*/
- config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
/**
* @description The submodel type, if any
* @default null
@@ -24183,7 +25114,7 @@ export type components = {
* Config
* @description The model's config
*/
- config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
/**
* @description The submodel type, if any
* @default null
@@ -24309,7 +25240,7 @@ export type components = {
* Variant
* @description The variant of the model.
*/
- variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
+ variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["WanVariantType"] | components["schemas"]["WanLoRAVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
/** @description The prediction type of the model. */
prediction_type?: components["schemas"]["SchedulerPredictionType"] | null;
/**
@@ -24391,7 +25322,7 @@ export type components = {
* @description Model type.
* @enum {string}
*/
- ModelType: "onnx" | "main" | "vae" | "lora" | "control_lora" | "controlnet" | "embedding" | "ip_adapter" | "clip_vision" | "clip_embed" | "t2i_adapter" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "spandrel_image_to_image" | "siglip" | "flux_redux" | "llava_onevision" | "text_llm" | "external_image_generator" | "unknown";
+ ModelType: "onnx" | "main" | "vae" | "lora" | "control_lora" | "controlnet" | "embedding" | "ip_adapter" | "clip_vision" | "clip_embed" | "t2i_adapter" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "wan_t5_encoder" | "spandrel_image_to_image" | "siglip" | "flux_redux" | "llava_onevision" | "text_llm" | "external_image_generator" | "unknown";
/**
* ModelVariantType
* @description Variant type.
@@ -24404,7 +25335,7 @@ export type components = {
*/
ModelsList: {
/** Models */
- models: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"])[];
+ models: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"])[];
};
/**
* Multiply Integers
@@ -24463,7 +25394,7 @@ export type components = {
* Value
* @description The value to substitute into the node/field.
*/
- value: string | number | components["schemas"]["ImageField"] | (string | number | components["schemas"]["ImageField"])[];
+ value: string | number | components["schemas"]["ImageField"] | components["schemas"]["VideoField"] | (string | number | components["schemas"]["ImageField"] | components["schemas"]["VideoField"])[];
};
/**
* NodePackInfo
@@ -24658,6 +25589,29 @@ export type components = {
*/
items: components["schemas"]["BoardDTO"][];
};
+ /** OffsetPaginatedResults[GalleryItem] */
+ OffsetPaginatedResults_GalleryItem_: {
+ /**
+ * Limit
+ * @description Limit of items to get
+ */
+ limit: number;
+ /**
+ * Offset
+ * @description Offset from which to retrieve items
+ */
+ offset: number;
+ /**
+ * Total
+ * @description Total number of items in result
+ */
+ total: number;
+ /**
+ * Items
+ * @description Items
+ */
+ items: components["schemas"]["GalleryItem"][];
+ };
/** OffsetPaginatedResults[ImageDTO] */
OffsetPaginatedResults_ImageDTO_: {
/**
@@ -24681,6 +25635,29 @@ export type components = {
*/
items: components["schemas"]["ImageDTO"][];
};
+ /** OffsetPaginatedResults[VideoDTO] */
+ OffsetPaginatedResults_VideoDTO_: {
+ /**
+ * Limit
+ * @description Limit of items to get
+ */
+ limit: number;
+ /**
+ * Offset
+ * @description Offset from which to retrieve items
+ */
+ offset: number;
+ /**
+ * Total
+ * @description Total number of items in result
+ */
+ total: number;
+ /**
+ * Items
+ * @description Items
+ */
+ items: components["schemas"]["VideoDTO"][];
+ };
/**
* Unsharp Mask (Oklab)
* @description Applies an unsharp mask filter to an image in the Oklab color space
@@ -27078,6 +28055,19 @@ export type components = {
*/
removed_images: string[];
};
+ /** RemoveVideosFromBoardResult */
+ RemoveVideosFromBoardResult: {
+ /**
+ * Affected Boards
+ * @description The ids of boards affected by the operation
+ */
+ affected_boards: string[];
+ /**
+ * Removed Videos
+ * @description The video names that were removed from their board
+ */
+ removed_videos: string[];
+ };
/**
* Resize Latents
* @description Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8.
@@ -29203,6 +30193,19 @@ export type components = {
*/
starred_images: string[];
};
+ /** StarredVideosResult */
+ StarredVideosResult: {
+ /**
+ * Affected Boards
+ * @description The ids of boards affected by the operation
+ */
+ affected_boards: string[];
+ /**
+ * Starred Videos
+ * @description The names of the videos that were starred
+ */
+ starred_videos: string[];
+ };
/** StarterModel */
StarterModel: {
/** Description */
@@ -29215,7 +30218,7 @@ export type components = {
type: components["schemas"]["ModelType"];
format?: components["schemas"]["ModelFormat"] | null;
/** Variant */
- variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
+ variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["WanVariantType"] | components["schemas"]["WanLoRAVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
/**
* Is Installed
* @default false
@@ -29260,7 +30263,7 @@ export type components = {
type: components["schemas"]["ModelType"];
format?: components["schemas"]["ModelFormat"] | null;
/** Variant */
- variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
+ variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["WanVariantType"] | components["schemas"]["WanLoRAVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
/**
* Is Installed
* @default false
@@ -29784,14 +30787,14 @@ export type components = {
* @description Submodel type.
* @enum {string}
*/
- SubModelType: "unet" | "transformer" | "text_encoder" | "text_encoder_2" | "text_encoder_3" | "tokenizer" | "tokenizer_2" | "tokenizer_3" | "vae" | "vae_decoder" | "vae_encoder" | "scheduler" | "safety_checker";
+ SubModelType: "unet" | "transformer" | "transformer_2" | "text_encoder" | "text_encoder_2" | "text_encoder_3" | "tokenizer" | "tokenizer_2" | "tokenizer_3" | "vae" | "vae_decoder" | "vae_encoder" | "scheduler" | "safety_checker";
/** SubmodelDefinition */
SubmodelDefinition: {
/** Path Or Prefix */
path_or_prefix: string;
model_type: components["schemas"]["ModelType"];
/** Variant */
- variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
+ variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["WanVariantType"] | components["schemas"]["WanLoRAVariantType"] | components["schemas"]["Qwen3VariantType"] | null;
};
/**
* Subtract Integers
@@ -31148,7 +32151,7 @@ export type components = {
* inferred from the field type.
* @enum {string}
*/
- UIComponent: "none" | "textarea" | "slider";
+ UIComponent: "none" | "textarea" | "slider" | "video-frame-index";
/**
* UIConfigBase
* @description Provides additional node configuration to the UI.
@@ -31453,6 +32456,19 @@ export type components = {
*/
unstarred_images: string[];
};
+ /** UnstarredVideosResult */
+ UnstarredVideosResult: {
+ /**
+ * Affected Boards
+ * @description The ids of boards affected by the operation
+ */
+ affected_boards: string[];
+ /**
+ * Unstarred Videos
+ * @description The names of the videos that were unstarred
+ */
+ unstarred_videos: string[];
+ };
/**
* UpdateAppGenerationSettingsRequest
* @description Writable generation-related app settings.
@@ -32159,10 +33175,13 @@ export type components = {
base: "sdxl";
};
/**
- * VAE_Diffusers_Flux2_Config
- * @description Model config for FLUX.2 VAE models in diffusers format (AutoencoderKLFlux2).
+ * VAE_Checkpoint_Wan_Config
+ * @description Model config for Wan 2.2 VAE checkpoint models (AutoencoderKLWan).
+ *
+ * Distinguishes A14B (z_dim=16, standard Wan VAE) from TI2V-5B (z_dim=48,
+ * Wan2.2-VAE) via the input channel count of ``decoder.conv_in.weight``.
*/
- VAE_Diffusers_Flux2_Config: {
+ VAE_Checkpoint_Wan_Config: {
/**
* Key
* @description A unique key for this model.
@@ -32216,28 +33235,115 @@ export type components = {
*/
cover_image: string | null;
/**
- * Format
- * @default diffusers
- * @constant
+ * Config Path
+ * @description Path to the config for this model, if any.
*/
- format: "diffusers";
- /** @default */
- repo_variant: components["schemas"]["ModelRepoVariant"];
+ config_path: string | null;
/**
* Type
* @default vae
* @constant
*/
type: "vae";
+ /**
+ * Format
+ * @default checkpoint
+ * @constant
+ */
+ format: "checkpoint";
/**
* Base
- * @default flux2
+ * @default wan
* @constant
*/
- base: "flux2";
+ base: "wan";
+ /**
+ * Latent Channels
+ * @description VAE latent channel count: 16 for A14B (standard Wan VAE) or 48 for TI2V-5B (Wan2.2-VAE).
+ * @enum {integer}
+ */
+ latent_channels: 16 | 48;
};
- /** VAE_Diffusers_SD1_Config */
- VAE_Diffusers_SD1_Config: {
+ /**
+ * VAE_Diffusers_Flux2_Config
+ * @description Model config for FLUX.2 VAE models in diffusers format (AutoencoderKLFlux2).
+ */
+ VAE_Diffusers_Flux2_Config: {
+ /**
+ * Key
+ * @description A unique key for this model.
+ */
+ key: string;
+ /**
+ * Hash
+ * @description The hash of the model file(s).
+ */
+ hash: string;
+ /**
+ * Path
+ * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.
+ */
+ path: string;
+ /**
+ * File Size
+ * @description The size of the model in bytes.
+ */
+ file_size: number;
+ /**
+ * Name
+ * @description Name of the model.
+ */
+ name: string;
+ /**
+ * Description
+ * @description Model description
+ */
+ description: string | null;
+ /**
+ * Source
+ * @description The original source of the model (path, URL or repo_id).
+ */
+ source: string;
+ /** @description The type of source */
+ source_type: components["schemas"]["ModelSourceType"];
+ /**
+ * Source Api Response
+ * @description The original API response from the source, as stringified JSON.
+ */
+ source_api_response: string | null;
+ /**
+ * Source Url
+ * @description Optional URL for the model (e.g. download page or model page).
+ */
+ source_url: string | null;
+ /**
+ * Cover Image
+ * @description Url for image to preview model
+ */
+ cover_image: string | null;
+ /**
+ * Format
+ * @default diffusers
+ * @constant
+ */
+ format: "diffusers";
+ /** @default */
+ repo_variant: components["schemas"]["ModelRepoVariant"];
+ /**
+ * Type
+ * @default vae
+ * @constant
+ */
+ type: "vae";
+ /**
+ * Base
+ * @default flux2
+ * @constant
+ */
+ base: "flux2";
+ };
+ /** VAE_Diffusers_SD1_Config */
+ VAE_Diffusers_SD1_Config: {
/**
* Key
* @description A unique key for this model.
@@ -32386,549 +33492,584 @@ export type components = {
*/
base: "sdxl";
};
- /** ValidationError */
- ValidationError: {
- /** Location */
- loc: (string | number)[];
- /** Message */
- msg: string;
- /** Error Type */
- type: string;
- };
/**
- * VirtualSubBoardDTO
- * @description A virtual sub-board computed from image metadata, not stored in the database.
+ * VAE_Diffusers_Wan_Config
+ * @description Model config for Wan 2.2 VAE in diffusers folder layout (AutoencoderKLWan).
*/
- VirtualSubBoardDTO: {
+ VAE_Diffusers_Wan_Config: {
/**
- * Virtual Board Id
- * @description The virtual board ID, e.g. 'by_date:2026-03-18'.
+ * Key
+ * @description A unique key for this model.
*/
- virtual_board_id: string;
+ key: string;
/**
- * Board Name
- * @description The display name of the virtual sub-board, e.g. '2026-03-18'.
+ * Hash
+ * @description The hash of the model file(s).
*/
- board_name: string;
+ hash: string;
/**
- * Date
- * @description The ISO date string, e.g. '2026-03-18'.
+ * Path
+ * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.
*/
- date: string;
+ path: string;
/**
- * Image Count
- * @description The number of general images for this date.
+ * File Size
+ * @description The size of the model in bytes.
*/
- image_count: number;
+ file_size: number;
/**
- * Asset Count
- * @description The number of asset images for this date.
+ * Name
+ * @description Name of the model.
*/
- asset_count: number;
+ name: string;
/**
- * Cover Image Name
- * @description The most recent image name for this date.
+ * Description
+ * @description Model description
*/
- cover_image_name?: string | null;
- };
- /** Workflow */
- Workflow: {
+ description: string | null;
/**
- * Name
- * @description The name of the workflow.
+ * Source
+ * @description The original source of the model (path, URL or repo_id).
*/
- name: string;
+ source: string;
+ /** @description The type of source */
+ source_type: components["schemas"]["ModelSourceType"];
/**
- * Author
- * @description The author of the workflow.
+ * Source Api Response
+ * @description The original API response from the source, as stringified JSON.
*/
- author: string;
+ source_api_response: string | null;
/**
- * Description
- * @description The description of the workflow.
+ * Source Url
+ * @description Optional URL for the model (e.g. download page or model page).
*/
- description: string;
+ source_url: string | null;
/**
- * Version
- * @description The version of the workflow.
+ * Cover Image
+ * @description Url for image to preview model
*/
- version: string;
+ cover_image: string | null;
/**
- * Contact
- * @description The contact of the workflow.
+ * Format
+ * @default diffusers
+ * @constant
*/
- contact: string;
+ format: "diffusers";
+ /** @default */
+ repo_variant: components["schemas"]["ModelRepoVariant"];
/**
- * Tags
- * @description The tags of the workflow.
+ * Type
+ * @default vae
+ * @constant
*/
- tags: string;
+ type: "vae";
/**
- * Notes
- * @description The notes of the workflow.
+ * Base
+ * @default wan
+ * @constant
*/
- notes: string;
+ base: "wan";
/**
- * Exposedfields
- * @description The exposed fields of the workflow.
+ * Latent Channels
+ * @description VAE latent channel count: 16 for A14B or 48 for TI2V-5B's Wan2.2-VAE.
+ * @default 16
+ * @enum {integer}
*/
- exposedFields: components["schemas"]["ExposedField"][];
- /** @description The meta of the workflow. */
- meta: components["schemas"]["WorkflowMeta"];
+ latent_channels: 16 | 48;
+ };
+ /** ValidationError */
+ ValidationError: {
+ /** Location */
+ loc: (string | number)[];
+ /** Message */
+ msg: string;
+ /** Error Type */
+ type: string;
+ };
+ /** VideoBoardArg */
+ VideoBoardArg: {
/**
- * Nodes
- * @description The nodes of the workflow.
+ * Board Id
+ * @description The id of the board to add or remove the video from
*/
- nodes: {
- [key: string]: components["schemas"]["JsonValue"];
- }[];
+ board_id: string;
/**
- * Edges
- * @description The edges of the workflow.
+ * Video Name
+ * @description The name of the video to add to / remove from the board
*/
- edges: {
- [key: string]: components["schemas"]["JsonValue"];
- }[];
+ video_name: string;
+ };
+ /**
+ * Concatenate Videos
+ * @description Join two or more videos into a single MP4.
+ *
+ * Transitions:
+ *
+ * * ``cut`` — hard splice, no blending. Fastest; total length is the sum of inputs.
+ * * ``crossfade`` — linear A→B cross-dissolve over ``transition_frames``. Each boundary
+ * consumes ``transition_frames`` from both adjacent clips, so total length is
+ * ``sum(inputs) - transition_frames * (n - 1)``.
+ * * ``fade_through_black`` — A fades to black, then B fades in from black. Each boundary
+ * consumes ``transition_frames // 2`` frames from the preceding clip's tail and the
+ * remainder (``transition_frames - transition_frames // 2``) from the next clip's head,
+ * so the total emitted is exactly ``transition_frames`` per boundary — even for odd
+ * ``transition_frames`` — and the overall length equals the sum of inputs.
+ *
+ * All inputs must share the same pixel dimensions. Output frame rate defaults to the
+ * first input's fps; override with ``fps`` to force a specific rate (the frames are not
+ * resampled, only the container is encoded at the new rate).
+ */
+ VideoConcatInvocation: {
/**
- * Form
- * @description The form of the workflow.
+ * @description The board to save the image to
+ * @default null
*/
- form?: {
- [key: string]: components["schemas"]["JsonValue"];
- } | null;
+ board?: components["schemas"]["BoardField"] | null;
+ /**
+ * @description Optional metadata to be saved with the image
+ * @default null
+ */
+ metadata?: components["schemas"]["MetadataField"] | null;
/**
* Id
- * @description The id of the workflow.
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
*/
id: string;
- };
- /**
- * WorkflowAccessRevokedEvent
- * @description Event model for workflow_access_revoked.
- */
- WorkflowAccessRevokedEvent: {
/**
- * Timestamp
- * @description The timestamp of the event
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
*/
- timestamp: number;
+ is_intermediate?: boolean;
/**
- * Workflow Id
- * @description The ID of the workflow
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
*/
- workflow_id: string;
+ use_cache?: boolean;
/**
- * User Id
- * @description The owner of the workflow
+ * Videos
+ * @description Videos to concatenate, in order. At least two are required.
+ * @default null
*/
- user_id: string;
- };
- /** WorkflowAndGraphResponse */
- WorkflowAndGraphResponse: {
+ videos?: components["schemas"]["VideoField"][] | null;
/**
- * Workflow
- * @description The workflow used to generate the image, as stringified JSON
+ * Transition
+ * @description Transition between consecutive clips.
+ * @default cut
+ * @enum {string}
*/
- workflow: string | null;
+ transition?: "cut" | "crossfade" | "fade_through_black";
/**
- * Graph
- * @description The graph used to generate the image, as stringified JSON
+ * Transition Frames
+ * @description Length of each transition in frames. Ignored when transition is 'cut'.
+ * @default 8
*/
- graph: string | null;
- };
- /** WorkflowCallCompatibility */
- WorkflowCallCompatibility: {
+ transition_frames?: number;
/**
- * Is Callable
- * @description Whether the workflow can currently be executed by call_saved_workflow.
+ * Fps
+ * @description Output frame rate. Defaults to the first input's fps.
+ * @default null
*/
- is_callable: boolean;
- /** @description Structured compatibility result. */
- reason: components["schemas"]["WorkflowCallCompatibilityReason"];
+ fps?: number | null;
/**
- * Message
- * @description Human-readable compatibility detail when unavailable.
+ * type
+ * @default video_concat
+ * @constant
*/
- message?: string | null;
+ type: "video_concat";
};
/**
- * WorkflowCallCompatibilityReason
- * @enum {string}
- */
- WorkflowCallCompatibilityReason: "ok" | "missing_workflow_return" | "multiple_workflow_return" | "unsupported_node" | "unsupported_batch_input" | "invalid_graph" | "invalid_inputs" | "exceeds_capacity" | "unknown";
- /**
- * WorkflowCallExecution
- * @description Tracks one parent/child workflow-call relationship and its lifecycle.
+ * VideoDTO
+ * @description Deserialized video record, enriched for the frontend.
*/
- WorkflowCallExecution: {
- /**
- * Id
- * @description The workflow-call execution id.
- */
- id?: string;
+ VideoDTO: {
/**
- * Parent Session Id
- * @description The parent graph execution state id.
+ * Video Name
+ * @description The unique name of the video.
*/
- parent_session_id: string;
+ video_name: string;
/**
- * Child Session Id
- * @description The child graph execution state id, if any.
+ * Video Url
+ * @description The URL of the video file (MP4).
*/
- child_session_id?: string | null;
+ video_url: string;
/**
- * Prepared Call Node Id
- * @description The prepared exec node id for the parent call site.
+ * Thumbnail Url
+ * @description The URL of the video's first-frame thumbnail (WebP).
*/
- prepared_call_node_id: string;
+ thumbnail_url: string;
+ /** @description The origin of the video. */
+ video_origin: components["schemas"]["ResourceOrigin"];
+ /** @description The category of the video (reuses ImageCategory). */
+ video_category: components["schemas"]["ImageCategory"];
/**
- * Source Call Node Id
- * @description The source graph node id for the parent call site.
+ * Width
+ * @description The pixel width of the video.
*/
- source_call_node_id: string;
+ width: number;
/**
- * Workflow Id
- * @description The saved workflow being called.
+ * Height
+ * @description The pixel height of the video.
*/
- workflow_id: string;
+ height: number;
/**
- * Depth
- * @description The 1-based depth of this call frame.
+ * Duration
+ * @description The duration of the video in seconds.
*/
- depth: number;
+ duration: number;
/**
- * Status
- * @description The current workflow-call lifecycle state.
- * @enum {string}
+ * Fps
+ * @description The frames-per-second of the video, if known.
*/
- status: "waiting_for_child" | "running_child" | "completed" | "failed";
+ fps?: number | null;
/**
- * Error Message
- * @description Failure reason, if the call failed.
+ * Created At
+ * @description The created timestamp of the video.
*/
- error_message?: string | null;
+ created_at: string;
/**
- * Child Session Ids
- * @description All child graph execution state ids.
+ * Updated At
+ * @description The updated timestamp of the video.
*/
- child_session_ids?: string[];
+ updated_at: string;
/**
- * Child Item Ids
- * @description Child queue item ids in enqueue order.
+ * Deleted At
+ * @description The deleted timestamp of the video.
*/
- child_item_ids?: number[];
+ deleted_at?: string | null;
/**
- * Expected Child Count
- * @description The number of child executions for this call.
- * @default 1
+ * Is Intermediate
+ * @description Whether this is an intermediate video.
*/
- expected_child_count?: number;
+ is_intermediate: boolean;
/**
- * Completed Child Item Ids
- * @description The child queue item ids whose workflow_return outputs have been aggregated.
+ * Session Id
+ * @description The session ID that produced this video, if any.
*/
- completed_child_item_ids?: number[];
+ session_id?: string | null;
/**
- * Aggregated Values
- * @description The aggregated workflow_return values accumulated from child executions.
+ * Node Id
+ * @description The node ID that produced this video, if any.
*/
- aggregated_values?: {
- [key: string]: unknown[];
- };
+ node_id?: string | null;
/**
- * Child Outputs
- * @description Workflow return values keyed by child queue item id.
+ * Starred
+ * @description Whether this video is starred.
*/
- child_outputs?: {
- [key: string]: {
- [key: string]: unknown;
- };
- };
- };
- /**
- * WorkflowCallFrame
- * @description Represents one workflow-call frame in a nested call chain.
- */
- WorkflowCallFrame: {
+ starred: boolean;
/**
- * Prepared Call Node Id
- * @description The prepared exec node id for the call site.
+ * Has Workflow
+ * @description Whether this video has a workflow associated.
*/
- prepared_call_node_id: string;
+ has_workflow: boolean;
/**
- * Source Call Node Id
- * @description The source graph node id for the call site.
+ * Video Subfolder
+ * @description The subfolder where the video is stored on disk.
+ * @default
*/
- source_call_node_id: string;
+ video_subfolder?: string;
/**
- * Workflow Id
- * @description The saved workflow being called.
+ * Board Id
+ * @description The id of the board the video belongs to, if one exists.
*/
- workflow_id: string;
+ board_id?: string | null;
+ };
+ /**
+ * VideoField
+ * @description A video primitive field
+ */
+ VideoField: {
/**
- * Depth
- * @description The 1-based depth of this call frame.
+ * Video Name
+ * @description The name of the video
*/
- depth: number;
+ video_name: string;
};
/**
- * WorkflowCallParentRef
- * @description Reference from a child execution state back to its parent workflow-call relationship.
+ * Frame from Video
+ * @description Extract a single frame from a video and save it as an image.
+ *
+ * ``frame_index`` is 0-based. Negative indices count from the end, so the
+ * default of -1 returns the final frame — the typical setup for chaining
+ * I2V clips into a longer sequence.
*/
- WorkflowCallParentRef: {
+ VideoFrameExtractInvocation: {
/**
- * Workflow Call Id
- * @description The workflow-call execution id.
+ * @description The board to save the image to
+ * @default null
*/
- workflow_call_id: string;
+ board?: components["schemas"]["BoardField"] | null;
/**
- * Parent Session Id
- * @description The parent graph execution state id.
+ * @description Optional metadata to be saved with the image
+ * @default null
*/
- parent_session_id: string;
+ metadata?: components["schemas"]["MetadataField"] | null;
/**
- * Prepared Call Node Id
- * @description The prepared exec node id for the parent call site.
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
*/
- prepared_call_node_id: string;
+ id: string;
/**
- * Source Call Node Id
- * @description The source graph node id for the parent call site.
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
*/
- source_call_node_id: string;
+ is_intermediate?: boolean;
/**
- * Workflow Id
- * @description The saved workflow being called.
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
*/
- workflow_id: string;
+ use_cache?: boolean;
/**
- * Depth
- * @description The 1-based depth of this call frame.
+ * @description The video to extract a frame from.
+ * @default null
*/
- depth: number;
+ video?: components["schemas"]["VideoField"] | null;
+ /**
+ * Frame Index
+ * @description Index of the frame to extract. 0 = first frame, -1 = last frame, -2 = second-to-last, etc.
+ * @default -1
+ */
+ frame_index?: number;
+ /**
+ * type
+ * @default video_frame_extract
+ * @constant
+ */
+ type: "video_frame_extract";
};
/**
- * WorkflowCategory
- * @enum {string}
- */
- WorkflowCategory: "user" | "default";
- /**
- * WorkflowCreatedEvent
- * @description Event model for workflow_created
+ * Video Primitive
+ * @description A video primitive value. Drop a video onto the field to make it available as an input
+ * to downstream nodes (e.g. Frame from Video, Concatenate Videos).
*/
- WorkflowCreatedEvent: {
+ VideoInvocation: {
/**
- * Timestamp
- * @description The timestamp of the event
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
*/
- timestamp: number;
+ id: string;
/**
- * Workflow Id
- * @description The ID of the workflow
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
*/
- workflow_id: string;
+ is_intermediate?: boolean;
/**
- * User Id
- * @description The owner of the workflow
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
*/
- user_id: string;
+ use_cache?: boolean;
/**
- * Is Public
- * @description Whether the workflow is shared with all users
+ * @description The video to load
+ * @default null
*/
- is_public: boolean;
+ video?: components["schemas"]["VideoField"] | null;
+ /**
+ * type
+ * @default video
+ * @constant
+ */
+ type: "video";
};
/**
- * WorkflowDeletedEvent
- * @description Event model for workflow_deleted
+ * VideoNamesResult
+ * @description Response containing ordered video names with metadata for optimistic updates.
*/
- WorkflowDeletedEvent: {
- /**
- * Timestamp
- * @description The timestamp of the event
- */
- timestamp: number;
+ VideoNamesResult: {
/**
- * Workflow Id
- * @description The ID of the workflow
+ * Video Names
+ * @description Ordered list of video names
*/
- workflow_id: string;
+ video_names: string[];
/**
- * User Id
- * @description The owner of the workflow
+ * Starred Count
+ * @description Number of starred videos (when starred_first=True)
*/
- user_id: string;
+ starred_count: number;
/**
- * Is Public
- * @description Whether the workflow was shared when it was deleted
+ * Total Count
+ * @description Total number of videos matching the query
*/
- is_public: boolean;
+ total_count: number;
};
- /** WorkflowMeta */
- WorkflowMeta: {
+ /**
+ * VideoOutput
+ * @description Output of a node that produces a video file (e.g. Wan 2.2 latents-to-video).
+ */
+ VideoOutput: {
+ /** @description The output video */
+ video: components["schemas"]["VideoField"];
/**
- * Version
- * @description The version of the workflow schema.
+ * Width
+ * @description The width of the video in pixels
*/
- version: string;
- /** @description The category of the workflow (user or default). */
- category: components["schemas"]["WorkflowCategory"];
- };
- /** WorkflowRecordDTO */
- WorkflowRecordDTO: {
+ width: number;
/**
- * Workflow Id
- * @description The id of the workflow.
+ * Height
+ * @description The height of the video in pixels
*/
- workflow_id: string;
+ height: number;
/**
- * Name
- * @description The name of the workflow.
+ * Num Frames
+ * @description The number of frames in the video
*/
- name: string;
+ num_frames: number;
/**
- * Created At
- * @description The created timestamp of the workflow.
+ * Fps
+ * @description The frames-per-second of the video
*/
- created_at: string;
+ fps: number;
/**
- * Updated At
- * @description The updated timestamp of the workflow.
+ * Duration
+ * @description The duration of the video in seconds
*/
- updated_at: string;
+ duration: number;
/**
- * Opened At
- * @description The opened timestamp of the workflow.
- */
- opened_at?: string | null;
- /**
- * User Id
- * @description The id of the user who owns this workflow.
- */
- user_id: string;
- /**
- * Is Public
- * @description Whether this workflow is shared with all users.
+ * type
+ * @default video_output
+ * @constant
*/
- is_public: boolean;
- /** @description The workflow. */
- workflow: components["schemas"]["Workflow"];
+ type: "video_output";
};
- /** WorkflowRecordListItemWithThumbnailDTO */
- WorkflowRecordListItemWithThumbnailDTO: {
- /**
- * Workflow Id
- * @description The id of the workflow.
- */
- workflow_id: string;
- /**
- * Name
- * @description The name of the workflow.
- */
- name: string;
- /**
- * Created At
- * @description The created timestamp of the workflow.
- */
- created_at: string;
- /**
- * Updated At
- * @description The updated timestamp of the workflow.
- */
- updated_at: string;
+ /**
+ * VideoRecordChanges
+ * @description Allowed mutations on a video record.
+ */
+ VideoRecordChanges: {
+ /** @description The video's new category. */
+ video_category?: components["schemas"]["ImageCategory"] | null;
/**
- * Opened At
- * @description The opened timestamp of the workflow.
+ * Session Id
+ * @description The video's new session ID.
*/
- opened_at?: string | null;
+ session_id?: string | null;
/**
- * User Id
- * @description The id of the user who owns this workflow.
+ * Is Intermediate
+ * @description The video's new `is_intermediate` flag.
*/
- user_id: string;
+ is_intermediate?: boolean | null;
/**
- * Is Public
- * @description Whether this workflow is shared with all users.
+ * Starred
+ * @description The video's new `starred` state.
*/
- is_public: boolean;
+ starred?: boolean | null;
+ } & {
+ [key: string]: unknown;
+ };
+ /**
+ * VideoUrlsDTO
+ * @description The URLs for a video and its thumbnail.
+ */
+ VideoUrlsDTO: {
/**
- * Description
- * @description The description of the workflow.
+ * Video Name
+ * @description The unique name of the video.
*/
- description: string;
- /** @description The description of the workflow. */
- category: components["schemas"]["WorkflowCategory"];
+ video_name: string;
/**
- * Tags
- * @description The tags of the workflow.
+ * Video Url
+ * @description The URL of the video file (MP4).
*/
- tags: string;
+ video_url: string;
/**
* Thumbnail Url
- * @description The URL of the workflow thumbnail.
+ * @description The URL of the video's first-frame thumbnail (WebP).
*/
- thumbnail_url?: string | null;
- /** @description Whether this workflow is currently callable by call_saved_workflow. */
- call_saved_workflow_compatibility?: components["schemas"]["WorkflowCallCompatibility"] | null;
+ thumbnail_url: string;
};
/**
- * WorkflowRecordOrderBy
- * @description The order by options for workflow records
- * @enum {string}
+ * VirtualSubBoardDTO
+ * @description A virtual sub-board computed from image/video metadata, not stored in the database.
*/
- WorkflowRecordOrderBy: "created_at" | "updated_at" | "opened_at" | "name" | "is_public";
- /** WorkflowRecordWithThumbnailDTO */
- WorkflowRecordWithThumbnailDTO: {
+ VirtualSubBoardDTO: {
/**
- * Workflow Id
- * @description The id of the workflow.
+ * Virtual Board Id
+ * @description The virtual board ID, e.g. 'by_date:2026-03-18'.
*/
- workflow_id: string;
+ virtual_board_id: string;
/**
- * Name
- * @description The name of the workflow.
+ * Board Name
+ * @description The display name of the virtual sub-board, e.g. '2026-03-18'.
*/
- name: string;
+ board_name: string;
/**
- * Created At
- * @description The created timestamp of the workflow.
+ * Date
+ * @description The ISO date string, e.g. '2026-03-18'.
*/
- created_at: string;
+ date: string;
/**
- * Updated At
- * @description The updated timestamp of the workflow.
+ * Image Count
+ * @description The number of general images for this date.
*/
- updated_at: string;
+ image_count: number;
/**
- * Opened At
- * @description The opened timestamp of the workflow.
+ * Asset Count
+ * @description The number of asset images for this date.
*/
- opened_at?: string | null;
+ asset_count: number;
/**
- * User Id
- * @description The id of the user who owns this workflow.
+ * Video Count
+ * @description The number of videos for this date.
+ * @default 0
*/
- user_id: string;
+ video_count?: number;
/**
- * Is Public
- * @description Whether this workflow is shared with all users.
+ * Cover Image Name
+ * @description The most recent image name for this date.
*/
- is_public: boolean;
- /** @description The workflow. */
- workflow: components["schemas"]["Workflow"];
+ cover_image_name?: string | null;
/**
- * Thumbnail Url
- * @description The URL of the workflow thumbnail.
+ * Cover Video Name
+ * @description The most recent video name for this date. Set instead of cover_image_name when the newest item for the date is a video.
*/
- thumbnail_url?: string | null;
- /** @description Whether this workflow is currently callable by call_saved_workflow. */
- call_saved_workflow_compatibility?: components["schemas"]["WorkflowCallCompatibility"] | null;
+ cover_video_name?: string | null;
};
/**
- * Get Workflow Return Value
- * @description Extracts one named value from a callable workflow return.
+ * WanConditioningField
+ * @description A Wan 2.2 conditioning tensor primitive value.
+ *
+ * Wan conditioning is the UMT5-XXL hidden state for the prompt plus an attention
+ * mask marking valid (non-padding) tokens.
*/
- WorkflowReturnGetInvocation: {
+ WanConditioningField: {
+ /**
+ * Conditioning Name
+ * @description The name of conditioning tensor
+ */
+ conditioning_name: string;
+ };
+ /**
+ * WanConditioningOutput
+ * @description Base class for nodes that output a Wan 2.2 text conditioning tensor.
+ */
+ WanConditioningOutput: {
+ /** @description Conditioning tensor */
+ conditioning: components["schemas"]["WanConditioningField"];
+ /**
+ * type
+ * @default wan_conditioning_output
+ * @constant
+ */
+ type: "wan_conditioning_output";
+ };
+ /**
+ * Denoise - Wan 2.2
+ * @description Run the denoising process with a Wan 2.2 model.
+ *
+ * Drives a flow-matching Euler schedule via Diffusers'
+ * ``FlowMatchEulerDiscreteScheduler``. CFG is supported when negative
+ * conditioning is provided and ``guidance_scale != 1.0``.
+ *
+ * For Wan 2.2 A14B the high-noise expert handles timesteps at and above
+ * ``boundary_ratio * num_train_timesteps``; the low-noise expert handles
+ * timesteps below. Both experts share the model cache; only the active one is
+ * GPU-resident at any time.
+ */
+ WanDenoiseInvocation: {
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -32943,52 +34084,116 @@ export type components = {
/**
* Use Cache
* @description Whether or not to use the cache
- * @default false
+ * @default true
*/
use_cache?: boolean;
/**
- * Values
- * @description The named workflow return values.
- * @default {}
+ * Transformer
+ * @description Wan transformer field (transformer + optional dual-expert metadata).
+ * @default null
*/
- values?: {
- [key: string]: unknown;
- };
+ transformer?: components["schemas"]["WanTransformerField"] | null;
/**
- * Key
- * @description The return key to extract.
- * @default
+ * @description Positive conditioning tensor
+ * @default null
*/
- key?: string;
+ positive_conditioning?: components["schemas"]["WanConditioningField"] | null;
/**
- * type
- * @default workflow_return_get
- * @constant
+ * @description Negative conditioning tensor
+ * @default null
*/
- type: "workflow_return_get";
- };
- /**
- * WorkflowReturnGetOutput
- * @description A value extracted from named workflow return values.
- */
- WorkflowReturnGetOutput: {
+ negative_conditioning?: components["schemas"]["WanConditioningField"] | null;
/**
- * Value
- * @description The extracted workflow return value.
+ * Reference Image
+ * @description Reference-image (VAE-latent) conditioning for Wan 2.2 I2V.
+ * @default null
*/
- value: unknown;
+ ref_image?: components["schemas"]["WanRefImageConditioningField"] | null;
+ /**
+ * @description Latents tensor
+ * @default null
+ */
+ latents?: components["schemas"]["LatentsField"] | null;
+ /**
+ * @description A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.
+ * @default null
+ */
+ denoise_mask?: components["schemas"]["DenoiseMaskField"] | null;
+ /**
+ * Denoising Start
+ * @description When to start denoising, expressed a percentage of total steps
+ * @default 0
+ */
+ denoising_start?: number;
+ /**
+ * Denoising End
+ * @description When to stop denoising, expressed a percentage of total steps
+ * @default 1
+ */
+ denoising_end?: number;
+ /**
+ * Add Noise
+ * @description Add noise based on denoising start.
+ * @default true
+ */
+ add_noise?: boolean;
+ /**
+ * Guidance Scale
+ * @description Classifier-free guidance scale. 4.0 is the Wan 2.2 default for A14B; TI2V-5B can tolerate higher values up to ~5.5.
+ * @default 4
+ */
+ guidance_scale?: number;
+ /**
+ * Guidance Scale (Low Noise)
+ * @description Optional separate CFG scale for the low-noise expert (Wan 2.2 A14B only). Values below 1.0 (including 0) fall back to the primary 'Guidance Scale'. Ignored for TI2V-5B.
+ * @default null
+ */
+ guidance_scale_low_noise?: number | null;
+ /**
+ * Width
+ * @description Width of the generated image.
+ * @default 1024
+ */
+ width?: number;
+ /**
+ * Height
+ * @description Height of the generated image.
+ * @default 1024
+ */
+ height?: number;
+ /**
+ * Steps
+ * @description Number of denoising steps.
+ * @default 40
+ */
+ steps?: number;
+ /**
+ * Seed
+ * @description Randomness seed for reproducibility.
+ * @default 0
+ */
+ seed?: number;
/**
* type
- * @default workflow_return_get_output
+ * @default wan_denoise
* @constant
*/
- type: "workflow_return_get_output";
+ type: "wan_denoise";
};
/**
- * Workflow Return
- * @description Defines the explicit named result returned by a callable workflow.
+ * Wan 2.2 I2V Ideal Dimensions (A14B)
+ * @description Ideal dimensions for the Wan 2.2 A14B models (I2V-A14B and T2V-A14B).
+ *
+ * Use this node for the A14B family. For the TI2V-5B model use "Wan 2.2 TI2V
+ * Ideal Dimensions" instead — TI2V-5B requires multiples of 32, and feeding it
+ * these multiples-of-16 dims fails the patchify step.
+ *
+ * Scales the input W×H so the shorter side equals the chosen preset (480 / 720 /
+ * 1080 px), then snaps each dimension to a multiple of 16 (the A14B pixel-grid
+ * constraint). Wire from ``Image Primitive``'s width/height outputs and into
+ * ``wan_ref_image_encoder`` / ``wan_denoise``.
*/
- WorkflowReturnInvocation: {
+ WanI2VIdealDimensionsInvocation: {
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -33003,64 +34208,61 @@ export type components = {
/**
* Use Cache
* @description Whether or not to use the cache
- * @default false
+ * @default true
*/
use_cache?: boolean;
/**
- * Values
- * @description The named values returned to a calling workflow.
- * @default []
+ * Width
+ * @description Source image width in pixels.
+ * @default 1024
*/
- values?: components["schemas"]["WorkflowReturnValueField"] | components["schemas"]["WorkflowReturnValueField"][];
+ width?: number;
/**
- * type
- * @default workflow_return
- * @constant
+ * Height
+ * @description Source image height in pixels.
+ * @default 1024
*/
- type: "workflow_return";
- };
- /**
- * WorkflowReturnOutput
- * @description The explicit named values returned from a callable workflow.
- */
- WorkflowReturnOutput: {
+ height?: number;
/**
- * Values
- * @description The workflow return values, keyed by return name.
- * @default {}
+ * Target Resolution
+ * @description Short-side resolution preset. 480p and 720p are Wan 2.2's native training resolutions; 1080p works but is extrapolation and costs ~2.25x the memory of 720p.
+ * @default 720p
+ * @enum {string}
*/
- values: {
- [key: string]: unknown;
- };
+ target_resolution?: "480p" | "720p" | "1080p";
+ /**
+ * Rounding
+ * @description How to snap each dimension to a multiple of 16. 'floor' rounds down — safest for VRAM, guaranteed not to exceed the unsnapped target. 'ceiling' rounds up. 'nearest' minimizes aspect-ratio drift (default).
+ * @default nearest
+ * @enum {string}
+ */
+ rounding?: "nearest" | "floor" | "ceiling";
/**
* type
- * @default workflow_return_output
+ * @default wan_i2v_ideal_dimensions
* @constant
*/
- type: "workflow_return_output";
+ type: "wan_i2v_ideal_dimensions";
};
/**
- * WorkflowReturnValueField
- * @description One named workflow return value.
+ * Image to Latents - Wan 2.2
+ * @description Encodes an image with the Wan VAE (AutoencoderKLWan).
+ *
+ * The output latents have the temporal dimension squeezed out, so downstream
+ * nodes see 4D ``[B, C, H, W]``. The denoise loop re-adds ``T=1`` before
+ * feeding the transformer.
*/
- WorkflowReturnValueField: {
+ WanImageToLatentsInvocation: {
/**
- * Key
- * @description The workflow return key.
+ * @description The board to save the image to
+ * @default null
*/
- key: string;
+ board?: components["schemas"]["BoardField"] | null;
/**
- * Value
- * @description The workflow return value.
+ * @description Optional metadata to be saved with the image
* @default null
*/
- value?: unknown;
- };
- /**
- * Workflow Return Value
- * @description Creates one named value for a callable workflow return.
- */
- WorkflowReturnValueInvocation: {
+ metadata?: components["schemas"]["MetadataField"] | null;
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -33075,225 +34277,199 @@ export type components = {
/**
* Use Cache
* @description Whether or not to use the cache
- * @default false
+ * @default true
*/
use_cache?: boolean;
/**
- * Key
- * @description The return key.
- * @default
+ * @description The image to encode.
+ * @default null
*/
- key?: string;
+ image?: components["schemas"]["ImageField"] | null;
/**
- * Value
- * @description The value returned under this key.
+ * @description VAE
* @default null
*/
- value?: unknown | null;
+ vae?: components["schemas"]["VAEField"] | null;
/**
* type
- * @default workflow_return_value
+ * @default wan_i2l
* @constant
*/
- type: "workflow_return_value";
+ type: "wan_i2l";
};
/**
- * WorkflowReturnValueOutput
- * @description A named workflow return value.
+ * Latents to Image - Wan 2.2
+ * @description Decodes Wan latents back to RGB.
*/
- WorkflowReturnValueOutput: {
- /**
- * Return Value
- * @description The named workflow return value.
- */
- value: components["schemas"]["WorkflowReturnValueField"];
+ WanLatentsToImageInvocation: {
/**
- * type
- * @default workflow_return_value_output
- * @constant
+ * @description The board to save the image to
+ * @default null
*/
- type: "workflow_return_value_output";
- };
- /**
- * WorkflowUpdatedEvent
- * @description Event model for workflow_updated
- */
- WorkflowUpdatedEvent: {
+ board?: components["schemas"]["BoardField"] | null;
/**
- * Timestamp
- * @description The timestamp of the event
+ * @description Optional metadata to be saved with the image
+ * @default null
*/
- timestamp: number;
+ metadata?: components["schemas"]["MetadataField"] | null;
/**
- * Workflow Id
- * @description The ID of the workflow
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
*/
- workflow_id: string;
+ id: string;
/**
- * User Id
- * @description The owner of the workflow
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
*/
- user_id: string;
+ is_intermediate?: boolean;
/**
- * Old Is Public
- * @description Whether the workflow was shared before the update
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
*/
- old_is_public: boolean;
+ use_cache?: boolean;
/**
- * New Is Public
- * @description Whether the workflow is shared after the update
+ * @description Latents tensor
+ * @default null
*/
- new_is_public: boolean;
- };
- /** WorkflowWithoutID */
- WorkflowWithoutID: {
+ latents?: components["schemas"]["LatentsField"] | null;
/**
- * Name
- * @description The name of the workflow.
+ * @description VAE
+ * @default null
*/
- name: string;
+ vae?: components["schemas"]["VAEField"] | null;
/**
- * Author
- * @description The author of the workflow.
+ * type
+ * @default wan_l2i
+ * @constant
*/
- author: string;
+ type: "wan_l2i";
+ };
+ /**
+ * Latents to Video - Wan 2.2
+ * @description Decode 5D Wan latents to RGB frames and encode an MP4.
+ */
+ WanLatentsToVideoInvocation: {
/**
- * Description
- * @description The description of the workflow.
+ * @description The board to save the image to
+ * @default null
*/
- description: string;
+ board?: components["schemas"]["BoardField"] | null;
/**
- * Version
- * @description The version of the workflow.
+ * @description Optional metadata to be saved with the image
+ * @default null
*/
- version: string;
+ metadata?: components["schemas"]["MetadataField"] | null;
/**
- * Contact
- * @description The contact of the workflow.
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
*/
- contact: string;
+ id: string;
/**
- * Tags
- * @description The tags of the workflow.
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
*/
- tags: string;
+ is_intermediate?: boolean;
/**
- * Notes
- * @description The notes of the workflow.
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
*/
- notes: string;
+ use_cache?: boolean;
/**
- * Exposedfields
- * @description The exposed fields of the workflow.
+ * @description Latents tensor
+ * @default null
*/
- exposedFields: components["schemas"]["ExposedField"][];
- /** @description The meta of the workflow. */
- meta: components["schemas"]["WorkflowMeta"];
+ latents?: components["schemas"]["LatentsField"] | null;
/**
- * Nodes
- * @description The nodes of the workflow.
+ * @description VAE
+ * @default null
*/
- nodes: {
- [key: string]: components["schemas"]["JsonValue"];
- }[];
+ vae?: components["schemas"]["VAEField"] | null;
/**
- * Edges
- * @description The edges of the workflow.
+ * Fps
+ * @description Frames-per-second for the encoded MP4. Wan 2.2 was trained at 16 FPS.
+ * @default 16
*/
- edges: {
- [key: string]: components["schemas"]["JsonValue"];
- }[];
+ fps?: number;
/**
- * Form
- * @description The form of the workflow.
+ * type
+ * @default wan_l2v
+ * @constant
*/
- form?: {
- [key: string]: components["schemas"]["JsonValue"];
- } | null;
+ type: "wan_l2v";
};
/**
- * ZImageConditioningField
- * @description A Z-Image conditioning tensor primitive value
+ * Apply LoRA Collection - Wan 2.2
+ * @description Apply a collection of LoRAs to the Wan 2.2 transformer(s).
+ *
+ * Each LoRA is routed to the primary and/or low-noise list based on its
+ * recorded ``expert`` tag (set by the probe from the filename). Untagged
+ * LoRAs go to both lists.
*/
- ZImageConditioningField: {
+ WanLoRACollectionLoader: {
/**
- * Conditioning Name
- * @description The name of conditioning tensor
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
*/
- conditioning_name: string;
+ id: string;
/**
- * @description The mask associated with this conditioning tensor for regional prompting. Excluded regions should be set to False, included regions should be set to True.
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * LoRAs
+ * @description LoRAs to apply. May be a single LoRA or a collection.
* @default null
*/
- mask?: components["schemas"]["TensorField"] | null;
- };
- /**
- * ZImageConditioningOutput
- * @description Base class for nodes that output a Z-Image text conditioning tensor.
- */
- ZImageConditioningOutput: {
- /** @description Conditioning tensor */
- conditioning: components["schemas"]["ZImageConditioningField"];
+ loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null;
+ /**
+ * Wan Transformer
+ * @description Transformer
+ * @default null
+ */
+ transformer?: components["schemas"]["WanTransformerField"] | null;
/**
* type
- * @default z_image_conditioning_output
+ * @default wan_lora_collection_loader
* @constant
*/
- type: "z_image_conditioning_output";
+ type: "wan_lora_collection_loader";
};
/**
- * ZImageControlField
- * @description A Z-Image control conditioning field for spatial control (Canny, HED, Depth, Pose, MLSD).
+ * Apply LoRA - Wan 2.2
+ * @description Apply a LoRA to the Wan 2.2 transformer(s).
+ *
+ * For A14B (dual expert) the LoRA's recorded ``expert`` field determines
+ * which expert list it lands in: ``"high"`` -> primary list, ``"low"`` ->
+ * low-noise list, ``None`` (untagged) -> both lists. Use the ``target``
+ * field to override.
+ *
+ * For TI2V-5B (single transformer) only the primary list is used at denoise
+ * time; the low-noise routing is harmless but ignored.
*/
- ZImageControlField: {
+ WanLoRALoaderInvocation: {
/**
- * Image Name
- * @description The name of the preprocessed control image
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
*/
- image_name: string;
- /** @description The Z-Image ControlNet adapter model */
- control_model: components["schemas"]["ModelIdentifierField"];
+ id: string;
/**
- * Control Context Scale
- * @description The strength of the control signal. Recommended range: 0.65-0.80.
- * @default 0.75
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
*/
- control_context_scale?: number;
- /**
- * Begin Step Percent
- * @description When the control is first applied (% of total steps)
- * @default 0
- */
- begin_step_percent?: number;
- /**
- * End Step Percent
- * @description When the control is last applied (% of total steps)
- * @default 1
- */
- end_step_percent?: number;
- };
- /**
- * Z-Image ControlNet
- * @description Configure Z-Image ControlNet for spatial conditioning.
- *
- * Takes a preprocessed control image (e.g., Canny edges, depth map, pose)
- * and a Z-Image ControlNet adapter model to enable spatial control.
- *
- * Supports 5 control modes: Canny, HED, Depth, Pose, MLSD.
- * Recommended control_context_scale: 0.65-0.80.
- */
- ZImageControlInvocation: {
- /**
- * Id
- * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
- */
- id: string;
- /**
- * Is Intermediate
- * @description Whether or not this is an intermediate invocation.
- * @default false
- */
- is_intermediate?: boolean;
+ is_intermediate?: boolean;
/**
* Use Cache
* @description Whether or not to use the cache
@@ -33301,62 +34477,87 @@ export type components = {
*/
use_cache?: boolean;
/**
- * @description The preprocessed control image (Canny, HED, Depth, Pose, or MLSD)
- * @default null
- */
- image?: components["schemas"]["ImageField"] | null;
- /**
- * Control Model
- * @description ControlNet model to load
+ * LoRA
+ * @description LoRA model to load
* @default null
*/
- control_model?: components["schemas"]["ModelIdentifierField"] | null;
+ lora?: components["schemas"]["ModelIdentifierField"] | null;
/**
- * Control Scale
- * @description Strength of the control signal. Recommended range: 0.65-0.80.
+ * Weight
+ * @description The weight at which the LoRA is applied to each model
* @default 0.75
*/
- control_context_scale?: number;
+ weight?: number;
/**
- * Begin Step Percent
- * @description When the control is first applied (% of total steps)
- * @default 0
+ * Target
+ * @description Which expert(s) to apply this LoRA to. 'auto' uses the LoRA's recorded expert tag (or both if untagged); 'both'/'high'/'low' override it.
+ * @default auto
+ * @enum {string}
*/
- begin_step_percent?: number;
+ target?: "auto" | "both" | "high" | "low";
/**
- * End Step Percent
- * @description When the control is last applied (% of total steps)
- * @default 1
+ * Wan Transformer
+ * @description Transformer
+ * @default null
*/
- end_step_percent?: number;
+ transformer?: components["schemas"]["WanTransformerField"] | null;
/**
* type
- * @default z_image_control
+ * @default wan_lora_loader
* @constant
*/
- type: "z_image_control";
+ type: "wan_lora_loader";
};
/**
- * ZImageControlOutput
- * @description Z-Image Control output containing control configuration.
+ * WanLoRALoaderOutput
+ * @description Wan 2.2 LoRA loader output.
*/
- ZImageControlOutput: {
- /** @description Z-Image control conditioning */
- control: components["schemas"]["ZImageControlField"];
+ WanLoRALoaderOutput: {
+ /**
+ * Wan Transformer
+ * @description Transformer
+ * @default null
+ */
+ transformer: components["schemas"]["WanTransformerField"] | null;
/**
* type
- * @default z_image_control_output
+ * @default wan_lora_loader_output
* @constant
*/
- type: "z_image_control_output";
+ type: "wan_lora_loader_output";
};
/**
- * Denoise - Z-Image
- * @description Run the denoising process with a Z-Image model.
+ * WanLoRAVariantType
+ * @description Wan 2.2 LoRA variants, identifying which model family a LoRA targets.
*
- * Supports regional prompting by connecting multiple conditioning inputs with masks.
+ * Detected from the LoRA's inner attention dim: A14B has ``inner_dim=5120``,
+ * TI2V-5B has ``inner_dim=3072``. A14B and 5B LoRAs are NOT interchangeable —
+ * applying one against the wrong main model crashes in the layer patcher
+ * with a tensor-shape error.
+ * @enum {string}
*/
- ZImageDenoiseInvocation: {
+ WanLoRAVariantType: "a14b" | "5b";
+ /**
+ * Main Model - Wan 2.2
+ * @description Loads a Wan 2.2 model, outputting its submodels.
+ *
+ * Components can be mixed and matched, mirroring the Qwen Image loader pattern:
+ *
+ * - Transformer(s):
+ * * Diffusers main: emits ``transformer/`` and (for A14B) ``transformer_2/``
+ * from the same model record.
+ * * GGUF main: emits the single GGUF as the primary transformer; for A14B
+ * the second-expert GGUF must be wired to ``Transformer (Low Noise)``.
+ * - VAE: standalone Wan VAE > main (if Diffusers) > Component Source (Diffusers).
+ * - UMT5-XXL encoder: standalone Wan T5 encoder > main (if Diffusers) >
+ * Component Source (Diffusers).
+ *
+ * The Component Source slot lets users supply a Diffusers Wan main model purely
+ * for VAE / encoder extraction when the actual transformer is in a single-file
+ * format. Together, the standalone VAE + standalone encoder let a GGUF
+ * transformer run without a full ~30 GB Diffusers install.
+ */
+ WanModelLoaderInvocation: {
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -33375,126 +34576,125 @@ export type components = {
*/
use_cache?: boolean;
/**
- * @description Latents tensor
- * @default null
- */
- latents?: components["schemas"]["LatentsField"] | null;
- /**
- * @description Noise tensor
- * @default null
+ * Transformer
+ * @description Wan 2.2 model (Transformer) to load
*/
- noise?: components["schemas"]["LatentsField"] | null;
+ model: components["schemas"]["ModelIdentifierField"];
/**
- * @description A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.
+ * Transformer (Low Noise)
+ * @description Optional second GGUF transformer for the A14B low-noise expert. Only relevant when the main model is a single-file GGUF and the variant is A14B; ignored when the main is a Diffusers A14B (both experts are pulled from transformer/ and transformer_2/ already) or when the variant is TI2V-5B.
* @default null
*/
- denoise_mask?: components["schemas"]["DenoiseMaskField"] | null;
- /**
- * Denoising Start
- * @description When to start denoising, expressed a percentage of total steps
- * @default 0
- */
- denoising_start?: number;
- /**
- * Denoising End
- * @description When to stop denoising, expressed a percentage of total steps
- * @default 1
- */
- denoising_end?: number;
- /**
- * Add Noise
- * @description Add noise based on denoising start.
- * @default true
- */
- add_noise?: boolean;
+ transformer_low_noise_model?: components["schemas"]["ModelIdentifierField"] | null;
/**
- * Transformer
- * @description Z-Image model (Transformer) to load
+ * VAE
+ * @description Standalone Wan VAE model. If not set, the VAE is loaded from the main model (when in Diffusers format) or from the Component Source.
* @default null
*/
- transformer?: components["schemas"]["TransformerField"] | null;
+ vae_model?: components["schemas"]["ModelIdentifierField"] | null;
/**
- * Positive Conditioning
- * @description Positive conditioning tensor
+ * Wan T5 Encoder
+ * @description Standalone Wan UMT5-XXL encoder. If not set, the encoder is loaded from the main model (when in Diffusers format) or from the Component Source.
* @default null
*/
- positive_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
+ wan_t5_encoder_model?: components["schemas"]["ModelIdentifierField"] | null;
/**
- * Negative Conditioning
- * @description Negative conditioning tensor
+ * Component Source (Diffusers)
+ * @description Diffusers Wan main model to extract VAE and/or encoder from. Use this if you don't have separate VAE/encoder models. Ignored for any submodel that is provided separately.
* @default null
*/
- negative_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
- /**
- * Guidance Scale
- * @description Guidance scale for classifier-free guidance. 1.0 = no CFG (recommended for Z-Image-Turbo). Values > 1.0 amplify guidance.
- * @default 1
- */
- guidance_scale?: number;
+ component_source?: components["schemas"]["ModelIdentifierField"] | null;
/**
- * Width
- * @description Width of the generated image.
- * @default 1024
+ * type
+ * @default wan_model_loader
+ * @constant
*/
- width?: number;
+ type: "wan_model_loader";
+ };
+ /**
+ * WanModelLoaderOutput
+ * @description Wan 2.2 model loader output.
+ */
+ WanModelLoaderOutput: {
/**
- * Height
- * @description Height of the generated image.
- * @default 1024
+ * Transformer
+ * @description Wan transformer (one or two experts depending on the variant)
*/
- height?: number;
+ transformer: components["schemas"]["WanTransformerField"];
/**
- * Steps
- * @description Number of denoising steps. 8 recommended for Z-Image-Turbo.
- * @default 8
+ * UMT5-XXL Encoder
+ * @description UMT5-XXL tokenizer and text encoder for Wan 2.2
*/
- steps?: number;
+ wan_t5_encoder: components["schemas"]["WanT5EncoderField"];
/**
- * Seed
- * @description Randomness seed for reproducibility.
- * @default 0
+ * VAE
+ * @description VAE
*/
- seed?: number;
+ vae: components["schemas"]["VAEField"];
/**
- * @description Z-Image control conditioning for spatial control (Canny, HED, Depth, Pose, MLSD).
- * @default null
+ * type
+ * @default wan_model_loader_output
+ * @constant
*/
- control?: components["schemas"]["ZImageControlField"] | null;
+ type: "wan_model_loader_output";
+ };
+ /**
+ * WanRefImageConditioningField
+ * @description Reference-image conditioning for Wan 2.2 I2V.
+ *
+ * Carries the 20-channel VAE-latent condition tensor (4-channel first-frame
+ * mask + 16-channel ref-image latents). The denoise loop concatenates this
+ * to the 16-channel noise latents along the channel dim each step, producing
+ * the 36-channel input the I2V-A14B transformer expects.
+ *
+ * Also carries the spatial dims and frame count used to encode the image so
+ * the denoise node can sanity-check the user's width/height/num_frames — a
+ * latent temporal-dim mismatch is hard to debug from the downstream error.
+ */
+ WanRefImageConditioningField: {
/**
- * @description VAE Required for control conditioning.
- * @default null
+ * Condition Tensor Name
+ * @description Name of the saved [1, 20, T_lat, H/8, W/8] condition tensor.
*/
- vae?: components["schemas"]["VAEField"] | null;
+ condition_tensor_name: string;
/**
- * Shift
- * @description Override the timestep shift (mu) for the sigma schedule. Leave blank to auto-calculate based on image dimensions (recommended). Lower values (~0.5) produce less noise shifting, higher values (~1.15) produce more.
- * @default null
+ * Width
+ * @description Image width used during VAE encoding (matches denoise width).
*/
- shift?: number | null;
+ width: number;
/**
- * Scheduler
- * @description Scheduler (sampler) for the denoising process. Euler is the default and recommended. Heun is 2nd-order (better quality, 2x slower). LCM works with Turbo only (not Base).
- * @default euler
- * @enum {string}
+ * Height
+ * @description Image height used during VAE encoding (matches denoise height).
*/
- scheduler?: "euler" | "heun" | "lcm";
+ height: number;
/**
- * type
- * @default z_image_denoise
- * @constant
+ * Num Frames
+ * @description Pixel-frame count the condition was built for. 1 for single-frame I2V (image output), 81+ for video.
+ * @default 1
*/
- type: "z_image_denoise";
+ num_frames?: number;
};
/**
- * Denoise - Z-Image + Metadata
- * @description Run denoising process with a Z-Image transformer model + metadata.
+ * Reference Image - Wan 2.2
+ * @description VAE-encode a reference image into Wan 2.2 I2V conditioning.
+ *
+ * Output is a ``[1, 20, T_lat, height // 8, width // 8]`` condition tensor
+ * that the denoise loop concatenates to the 16-channel noise latents each
+ * step, producing the 36-channel input the I2V-A14B transformer expects.
+ *
+ * For image (single-frame) I2V leave ``num_frames=1`` (T_lat=1). For video
+ * I2V set ``num_frames`` to match the value on the video-denoise node
+ * (e.g. 81 for the Wan 2.2 reference defaults).
+ *
+ * Supply an optional ``end_image`` for **first-last-frame interpolation
+ * (FLF2V)** — the model then interpolates the motion from ``image`` (first
+ * frame) to ``end_image`` (final frame). FLF2V is I2V-A14B video only
+ * (``num_frames > 1``); it is not supported for TI2V-5B or single-frame I2V.
+ *
+ * Only works with I2V-A14B (the denoise loop's variant gate enforces this).
+ * For T2V or TI2V-5B, omit this node entirely.
*/
- ZImageDenoiseMetaInvocation: {
- /**
- * @description Optional metadata to be saved with the image
- * @default null
- */
- metadata?: components["schemas"]["MetadataField"] | null;
+ WanRefImageEncoderInvocation: {
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -33513,219 +34713,176 @@ export type components = {
*/
use_cache?: boolean;
/**
- * @description Latents tensor
- * @default null
- */
- latents?: components["schemas"]["LatentsField"] | null;
- /**
- * @description Noise tensor
- * @default null
- */
- noise?: components["schemas"]["LatentsField"] | null;
- /**
- * @description A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.
- * @default null
- */
- denoise_mask?: components["schemas"]["DenoiseMaskField"] | null;
- /**
- * Denoising Start
- * @description When to start denoising, expressed a percentage of total steps
- * @default 0
- */
- denoising_start?: number;
- /**
- * Denoising End
- * @description When to stop denoising, expressed a percentage of total steps
- * @default 1
- */
- denoising_end?: number;
- /**
- * Add Noise
- * @description Add noise based on denoising start.
- * @default true
- */
- add_noise?: boolean;
- /**
- * Transformer
- * @description Z-Image model (Transformer) to load
- * @default null
- */
- transformer?: components["schemas"]["TransformerField"] | null;
- /**
- * Positive Conditioning
- * @description Positive conditioning tensor
+ * @description Reference image to condition on (the first frame).
* @default null
*/
- positive_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
+ image?: components["schemas"]["ImageField"] | null;
/**
- * Negative Conditioning
- * @description Negative conditioning tensor
+ * VAE
+ * @description VAE
* @default null
*/
- negative_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
- /**
- * Guidance Scale
- * @description Guidance scale for classifier-free guidance. 1.0 = no CFG (recommended for Z-Image-Turbo). Values > 1.0 amplify guidance.
- * @default 1
- */
- guidance_scale?: number;
+ vae?: components["schemas"]["VAEField"] | null;
/**
* Width
- * @description Width of the generated image.
+ * @description Width to resize the reference image to (must match denoise width).
* @default 1024
*/
width?: number;
/**
* Height
- * @description Height of the generated image.
+ * @description Height to resize the reference image to (must match denoise height).
* @default 1024
*/
height?: number;
/**
- * Steps
- * @description Number of denoising steps. 8 recommended for Z-Image-Turbo.
- * @default 8
- */
- steps?: number;
- /**
- * Seed
- * @description Randomness seed for reproducibility.
- * @default 0
- */
- seed?: number;
- /**
- * @description Z-Image control conditioning for spatial control (Canny, HED, Depth, Pose, MLSD).
- * @default null
- */
- control?: components["schemas"]["ZImageControlField"] | null;
- /**
- * @description VAE Required for control conditioning.
- * @default null
+ * Number of Frames
+ * @description Pixel-frame count to build the condition for. Use 1 for single-frame image I2V. For video I2V, set this to match the video-denoise node's num_frames (and ensure (num_frames - 1) %% 4 == 0, e.g. 81).
+ * @default 1
*/
- vae?: components["schemas"]["VAEField"] | null;
+ num_frames?: number;
/**
- * Shift
- * @description Override the timestep shift (mu) for the sigma schedule. Leave blank to auto-calculate based on image dimensions (recommended). Lower values (~0.5) produce less noise shifting, higher values (~1.15) produce more.
+ * End Image (FLF2V)
+ * @description Optional end frame for first-last-frame interpolation (FLF2V). When set, the video interpolates from the reference image (first frame) to this image (final frame). I2V-A14B video only (num_frames > 1); not supported for TI2V-5B or single-frame I2V.
* @default null
*/
- shift?: number | null;
- /**
- * Scheduler
- * @description Scheduler (sampler) for the denoising process. Euler is the default and recommended. Heun is 2nd-order (better quality, 2x slower). LCM works with Turbo only (not Base).
- * @default euler
- * @enum {string}
- */
- scheduler?: "euler" | "heun" | "lcm";
+ end_image?: components["schemas"]["ImageField"] | null;
/**
* type
- * @default z_image_denoise_meta
+ * @default wan_ref_image_encoder
* @constant
*/
- type: "z_image_denoise_meta";
+ type: "wan_ref_image_encoder";
};
/**
- * Image to Latents - Z-Image
- * @description Generates latents from an image using Z-Image VAE (supports both Diffusers and FLUX VAE).
+ * WanRefImageOutput
+ * @description Output of a Wan 2.2 reference-image VAE-encoder.
*/
- ZImageImageToLatentsInvocation: {
+ WanRefImageOutput: {
/**
- * @description The board to save the image to
- * @default null
+ * Reference Image
+ * @description VAE-latent reference-image conditioning for Wan 2.2 I2V.
*/
- board?: components["schemas"]["BoardField"] | null;
+ ref_image: components["schemas"]["WanRefImageConditioningField"];
/**
- * @description Optional metadata to be saved with the image
- * @default null
+ * type
+ * @default wan_ref_image_output
+ * @constant
*/
- metadata?: components["schemas"]["MetadataField"] | null;
+ type: "wan_ref_image_output";
+ };
+ /**
+ * WanT5EncoderField
+ * @description Field for the UMT5-XXL text encoder used by Wan 2.2 models.
+ */
+ WanT5EncoderField: {
+ /** @description Info to load tokenizer submodel */
+ tokenizer: components["schemas"]["ModelIdentifierField"];
+ /** @description Info to load text_encoder submodel */
+ text_encoder: components["schemas"]["ModelIdentifierField"];
/**
- * Id
- * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ * Loras
+ * @description LoRAs to apply on model loading
*/
- id: string;
+ loras?: components["schemas"]["LoRAField"][];
+ };
+ /**
+ * WanT5Encoder_WanT5Encoder_Config
+ * @description UMT5-XXL encoder in diffusers folder layout.
+ *
+ * Accepts either:
+ * - A directory containing ``text_encoder/`` (and typically ``tokenizer/``) ─ the
+ * shape produced by ``Wan-AI/Wan2.2-T2V-A14B::text_encoder+tokenizer``.
+ * - A bare ``text_encoder/`` directory whose own ``config.json`` declares
+ * ``model_type: umt5``.
+ */
+ WanT5Encoder_WanT5Encoder_Config: {
/**
- * Is Intermediate
- * @description Whether or not this is an intermediate invocation.
- * @default false
+ * Key
+ * @description A unique key for this model.
*/
- is_intermediate?: boolean;
+ key: string;
/**
- * Use Cache
- * @description Whether or not to use the cache
- * @default true
+ * Hash
+ * @description The hash of the model file(s).
*/
- use_cache?: boolean;
+ hash: string;
/**
- * @description The image to encode.
- * @default null
+ * Path
+ * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory.
*/
- image?: components["schemas"]["ImageField"] | null;
+ path: string;
/**
- * @description VAE
- * @default null
+ * File Size
+ * @description The size of the model in bytes.
*/
- vae?: components["schemas"]["VAEField"] | null;
+ file_size: number;
/**
- * type
- * @default z_image_i2l
- * @constant
+ * Name
+ * @description Name of the model.
*/
- type: "z_image_i2l";
- };
- /**
- * Latents to Image - Z-Image
- * @description Generates an image from latents using Z-Image VAE (supports both Diffusers and FLUX VAE).
- */
- ZImageLatentsToImageInvocation: {
+ name: string;
/**
- * @description The board to save the image to
- * @default null
+ * Description
+ * @description Model description
*/
- board?: components["schemas"]["BoardField"] | null;
+ description: string | null;
/**
- * @description Optional metadata to be saved with the image
- * @default null
+ * Source
+ * @description The original source of the model (path, URL or repo_id).
*/
- metadata?: components["schemas"]["MetadataField"] | null;
+ source: string;
+ /** @description The type of source */
+ source_type: components["schemas"]["ModelSourceType"];
/**
- * Id
- * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ * Source Api Response
+ * @description The original API response from the source, as stringified JSON.
*/
- id: string;
+ source_api_response: string | null;
/**
- * Is Intermediate
- * @description Whether or not this is an intermediate invocation.
- * @default false
+ * Source Url
+ * @description Optional URL for the model (e.g. download page or model page).
*/
- is_intermediate?: boolean;
+ source_url: string | null;
/**
- * Use Cache
- * @description Whether or not to use the cache
- * @default true
+ * Cover Image
+ * @description Url for image to preview model
*/
- use_cache?: boolean;
+ cover_image: string | null;
/**
- * @description Latents tensor
- * @default null
+ * Base
+ * @default any
+ * @constant
*/
- latents?: components["schemas"]["LatentsField"] | null;
+ base: "any";
/**
- * @description VAE
- * @default null
+ * Type
+ * @default wan_t5_encoder
+ * @constant
*/
- vae?: components["schemas"]["VAEField"] | null;
+ type: "wan_t5_encoder";
/**
- * type
- * @default z_image_l2i
+ * Format
+ * @default wan_t5_encoder
* @constant
*/
- type: "z_image_l2i";
+ format: "wan_t5_encoder";
};
/**
- * Apply LoRA Collection - Z-Image
- * @description Applies a collection of LoRAs to a Z-Image transformer.
+ * Wan 2.2 TI2V Ideal Dimensions (5B)
+ * @description Ideal dimensions for the Wan 2.2 TI2V-5B model.
+ *
+ * Use this node for TI2V-5B only. For the A14B models (I2V-A14B / T2V-A14B) use
+ * "Wan 2.2 I2V Ideal Dimensions" instead — those need multiples of 16, and this
+ * node's multiples-of-32 dims would overshoot their pixel grid.
+ *
+ * Identical to the A14B node but snaps each dimension to a multiple of 32 instead
+ * of 16: the Wan 2.2-VAE used by TI2V-5B applies 16x spatial compression and the
+ * transformer adds a 2x patch on top, so pixel dims must divide by 32 for the
+ * patchify step. Wire from ``Image Primitive``'s width/height outputs and into
+ * the matching ``wan_denoise`` inputs.
*/
- ZImageLoRACollectionLoader: {
+ WanTI2VIdealDimensionsInvocation: {
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -33744,35 +34901,47 @@ export type components = {
*/
use_cache?: boolean;
/**
- * LoRAs
- * @description LoRA models and weights. May be a single LoRA or collection.
- * @default null
+ * Width
+ * @description Source image width in pixels.
+ * @default 1024
*/
- loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null;
+ width?: number;
/**
- * Transformer
- * @description Transformer
- * @default null
+ * Height
+ * @description Source image height in pixels.
+ * @default 1024
*/
- transformer?: components["schemas"]["TransformerField"] | null;
+ height?: number;
/**
- * Qwen3 Encoder
- * @description Qwen3 tokenizer and text encoder
- * @default null
+ * Target Resolution
+ * @description Short-side resolution preset. 480p and 720p are Wan 2.2's native training resolutions; 1080p works but is extrapolation and costs ~2.25x the memory of 720p.
+ * @default 720p
+ * @enum {string}
*/
- qwen3_encoder?: components["schemas"]["Qwen3EncoderField"] | null;
+ target_resolution?: "480p" | "720p" | "1080p";
+ /**
+ * Rounding
+ * @description How to snap each dimension to a multiple of 32. 'floor' rounds down — safest for VRAM, guaranteed not to exceed the unsnapped target. 'ceiling' rounds up. 'nearest' minimizes aspect-ratio drift (default).
+ * @default nearest
+ * @enum {string}
+ */
+ rounding?: "nearest" | "floor" | "ceiling";
/**
* type
- * @default z_image_lora_collection_loader
+ * @default wan_ti2v_ideal_dimensions
* @constant
*/
- type: "z_image_lora_collection_loader";
+ type: "wan_ti2v_ideal_dimensions";
};
/**
- * Apply LoRA - Z-Image
- * @description Apply a LoRA model to a Z-Image transformer and/or Qwen3 text encoder.
+ * Prompt - Wan 2.2
+ * @description Encodes a text prompt for Wan 2.2 using the UMT5-XXL encoder.
+ *
+ * Output is the encoder's last hidden state (shape: [seq_len=226, 4096]) plus
+ * an attention mask marking valid (non-padding) tokens. The Wan transformer
+ * consumes these directly as ``encoder_hidden_states``.
*/
- ZImageLoRALoaderInvocation: {
+ WanTextEncoderInvocation: {
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -33791,70 +34960,104 @@ export type components = {
*/
use_cache?: boolean;
/**
- * LoRA
- * @description LoRA model to load
- * @default null
- */
- lora?: components["schemas"]["ModelIdentifierField"] | null;
- /**
- * Weight
- * @description The weight at which the LoRA is applied to each model
- * @default 0.75
- */
- weight?: number;
- /**
- * Z-Image Transformer
- * @description Transformer
+ * Prompt
+ * @description Text prompt for Wan 2.2.
* @default null
*/
- transformer?: components["schemas"]["TransformerField"] | null;
+ prompt?: string | null;
/**
- * Qwen3 Encoder
- * @description Qwen3 tokenizer and text encoder
+ * UMT5-XXL Encoder
+ * @description UMT5-XXL tokenizer and text encoder for Wan 2.2
* @default null
*/
- qwen3_encoder?: components["schemas"]["Qwen3EncoderField"] | null;
+ wan_t5_encoder?: components["schemas"]["WanT5EncoderField"] | null;
/**
* type
- * @default z_image_lora_loader
+ * @default wan_text_encoder
* @constant
*/
- type: "z_image_lora_loader";
+ type: "wan_text_encoder";
};
/**
- * ZImageLoRALoaderOutput
- * @description Z-Image LoRA Loader Output
+ * WanTransformerField
+ * @description Transformer field for Wan 2.2 models.
+ *
+ * Wan 2.2 A14B is a Mixture-of-Experts model with two transformer experts:
+ * a high-noise expert (active at large timesteps) and a low-noise expert
+ * (active at small timesteps). TI2V-5B is a single-transformer model and only
+ * populates ``transformer``.
+ *
+ * ``boundary_ratio`` matches Diffusers' ``WanPipeline`` semantics: it's the
+ * boundary timestep as a fraction of ``num_train_timesteps`` (typically 1000),
+ * so ``boundary_ratio=0.875`` means the high-noise expert handles t >= 875 and
+ * the low-noise expert handles t < 875.
*/
- ZImageLoRALoaderOutput: {
+ WanTransformerField: {
+ /** @description Primary transformer submodel. For A14B this is the high-noise expert. */
+ transformer: components["schemas"]["ModelIdentifierField"];
/**
- * Z-Image Transformer
- * @description Transformer
+ * @description Low-noise transformer expert (Wan 2.2 A14B only). None for TI2V-5B.
* @default null
*/
- transformer: components["schemas"]["TransformerField"] | null;
+ transformer_low_noise?: components["schemas"]["ModelIdentifierField"] | null;
/**
- * Qwen3 Encoder
- * @description Qwen3 tokenizer and text encoder
- * @default null
+ * Loras
+ * @description LoRAs to apply to the primary transformer. For A14B applied to the high-noise expert.
*/
- qwen3_encoder: components["schemas"]["Qwen3EncoderField"] | null;
+ loras?: components["schemas"]["LoRAField"][];
/**
- * type
- * @default z_image_lora_loader_output
- * @constant
+ * Loras Low Noise
+ * @description Optional separate LoRAs for the low-noise expert (Wan 2.2 A14B). If empty and transformer_low_noise is set, the primary 'loras' list is reused.
*/
- type: "z_image_lora_loader_output";
+ loras_low_noise?: components["schemas"]["LoRAField"][];
+ /**
+ * Boundary Ratio
+ * @description Boundary timestep as a fraction of num_train_timesteps (Wan 2.2 A14B only). High-noise expert: t >= boundary_ratio * num_train_timesteps. Low-noise expert: t below. Ignored for TI2V-5B.
+ * @default 0.875
+ */
+ boundary_ratio?: number;
};
/**
- * Main Model - Z-Image
- * @description Loads a Z-Image model, outputting its submodels.
+ * WanVariantType
+ * @description Wan 2.2 model variants.
*
- * Similar to FLUX, you can mix and match components:
- * - Transformer: From Z-Image main model (GGUF quantized or Diffusers format)
- * - VAE: Separate FLUX VAE (shared with FLUX models) or from a Diffusers Z-Image model
- * - Qwen3 Encoder: Separate Qwen3Encoder model or from a Diffusers Z-Image model
+ * All variants are used for image generation at num_frames=1. The A14B family
+ * is a Mixture-of-Experts (high-noise + low-noise) totalling ~28B params; the
+ * T2V sub-variant takes text only, while the I2V sub-variant additionally
+ * conditions on a reference image (encoded by the VAE and concatenated to the
+ * noise latents along the channel dim — its transformer has ``in_channels=36``
+ * instead of ``16``). TI2V-5B is a single ~5B transformer with a
+ * higher-compression VAE (z_dim=48).
+ * @enum {string}
*/
- ZImageModelLoaderInvocation: {
+ WanVariantType: "t2v_a14b" | "i2v_a14b" | "ti2v_5b";
+ /**
+ * Denoise Video - Wan 2.2
+ * @description Run the Wan 2.2 denoising loop on a multi-frame latent tensor.
+ *
+ * The output is a 5D ``[1, C, T_lat, H/8, W/8]`` latent tensor ready for
+ * :class:`WanLatentsToVideoInvocation` to VAE-decode and encode as MP4.
+ *
+ * Mirrors :class:`WanDenoiseInvocation` for the per-step logic (CFG, MoE
+ * expert swap at the boundary timestep, LoRA patching, scheduler selection).
+ * Differences from the image denoise:
+ *
+ * * The noise tensor has a real temporal dim built from ``num_frames``.
+ * * The I2V condition is built across all latent frames (frame 0
+ * conditioned, rest zero) via
+ * :func:`encode_reference_image_to_video_condition` upstream — the
+ * ``ref_image`` field on this node carries a tensor of shape
+ * ``[1, 20, T_lat, H_lat, W_lat]`` instead of ``[1, 20, 1, ...]``.
+ * * No ``denoising_start`` / ``denoising_end`` / initial-latents inputs.
+ * The image denoise node uses those for img2img (noise injection on an
+ * existing latent), but image-conditioned video generation flows through
+ * the reference-frame conditioning mechanism instead — the first frame
+ * drives subsequent frames at every step, so a partial-schedule run from
+ * an initial latent has no analogue here. Run the schedule from t=1
+ * to t=0 every time. The base ``WanDenoiseInvocation`` still handles
+ * the img2img case for stills.
+ */
+ WanVideoDenoiseInvocation: {
/**
* Id
* @description The id of this instance of an invocation. Must be unique among all instances of invocations.
@@ -33874,23 +35077,1535 @@ export type components = {
use_cache?: boolean;
/**
* Transformer
- * @description Z-Image model (Transformer) to load
+ * @description Wan transformer field. Supported: T2V-A14B / I2V-A14B (dual-expert) and TI2V-5B (single-expert, handles both T2V and I2V). All three accept a Reference Image input for image-to-video; A14B uses the 36-channel concat scheme while TI2V-5B uses the expand_timesteps first-frame-mask blend.
+ * @default null
*/
- model: components["schemas"]["ModelIdentifierField"];
+ transformer?: components["schemas"]["WanTransformerField"] | null;
/**
- * VAE
- * @description Standalone VAE model. Z-Image uses the same VAE as FLUX (16-channel). If not provided, VAE will be loaded from the Qwen3 Source model.
+ * @description Positive conditioning tensor
* @default null
*/
- vae_model?: components["schemas"]["ModelIdentifierField"] | null;
+ positive_conditioning?: components["schemas"]["WanConditioningField"] | null;
/**
- * Qwen3 Encoder
- * @description Standalone Qwen3 Encoder model. If not provided, encoder will be loaded from the Qwen3 Source model.
+ * @description Negative conditioning tensor
* @default null
*/
- qwen3_encoder_model?: components["schemas"]["ModelIdentifierField"] | null;
+ negative_conditioning?: components["schemas"]["WanConditioningField"] | null;
/**
- * Qwen3 Source (Diffusers)
+ * Reference Image
+ * @description Reference-image (VAE-latent) conditioning for Wan 2.2 I2V.
+ * @default null
+ */
+ ref_image?: components["schemas"]["WanRefImageConditioningField"] | null;
+ /**
+ * Guidance Scale
+ * @description Classifier-free guidance scale. Wan 2.2 video reference uses 5.0 for the high-noise expert and 4.0 for the low-noise expert.
+ * @default 5
+ */
+ guidance_scale?: number;
+ /**
+ * Guidance Scale (Low Noise)
+ * @description Optional separate CFG scale for the low-noise expert (Wan 2.2 A14B only). Values below 1.0 fall back to the primary 'Guidance Scale'.
+ * @default 4
+ */
+ guidance_scale_low_noise?: number | null;
+ /**
+ * Width
+ * @description Width of the generated video.
+ * @default 832
+ */
+ width?: number;
+ /**
+ * Height
+ * @description Height of the generated video.
+ * @default 480
+ */
+ height?: number;
+ /**
+ * Number of Frames
+ * @description Number of output frames. Must satisfy (num_frames - 1) %% 4 == 0 so the latent temporal dim divides cleanly. Wan 2.2 was trained at 81 frames @ 16 FPS (~5 s).
+ * @default 81
+ */
+ num_frames?: number;
+ /**
+ * Steps
+ * @description Number of denoising steps.
+ * @default 40
+ */
+ steps?: number;
+ /**
+ * Seed
+ * @description Randomness seed for reproducibility.
+ * @default 0
+ */
+ seed?: number;
+ /**
+ * type
+ * @default wan_video_denoise
+ * @constant
+ */
+ type: "wan_video_denoise";
+ };
+ /** Workflow */
+ Workflow: {
+ /**
+ * Name
+ * @description The name of the workflow.
+ */
+ name: string;
+ /**
+ * Author
+ * @description The author of the workflow.
+ */
+ author: string;
+ /**
+ * Description
+ * @description The description of the workflow.
+ */
+ description: string;
+ /**
+ * Version
+ * @description The version of the workflow.
+ */
+ version: string;
+ /**
+ * Contact
+ * @description The contact of the workflow.
+ */
+ contact: string;
+ /**
+ * Tags
+ * @description The tags of the workflow.
+ */
+ tags: string;
+ /**
+ * Notes
+ * @description The notes of the workflow.
+ */
+ notes: string;
+ /**
+ * Exposedfields
+ * @description The exposed fields of the workflow.
+ */
+ exposedFields: components["schemas"]["ExposedField"][];
+ /** @description The meta of the workflow. */
+ meta: components["schemas"]["WorkflowMeta"];
+ /**
+ * Nodes
+ * @description The nodes of the workflow.
+ */
+ nodes: {
+ [key: string]: components["schemas"]["JsonValue"];
+ }[];
+ /**
+ * Edges
+ * @description The edges of the workflow.
+ */
+ edges: {
+ [key: string]: components["schemas"]["JsonValue"];
+ }[];
+ /**
+ * Form
+ * @description The form of the workflow.
+ */
+ form?: {
+ [key: string]: components["schemas"]["JsonValue"];
+ } | null;
+ /**
+ * Id
+ * @description The id of the workflow.
+ */
+ id: string;
+ };
+ /**
+ * WorkflowAccessRevokedEvent
+ * @description Event model for workflow_access_revoked.
+ */
+ WorkflowAccessRevokedEvent: {
+ /**
+ * Timestamp
+ * @description The timestamp of the event
+ */
+ timestamp: number;
+ /**
+ * Workflow Id
+ * @description The ID of the workflow
+ */
+ workflow_id: string;
+ /**
+ * User Id
+ * @description The owner of the workflow
+ */
+ user_id: string;
+ };
+ /** WorkflowAndGraphResponse */
+ WorkflowAndGraphResponse: {
+ /**
+ * Workflow
+ * @description The workflow used to generate the image, as stringified JSON
+ */
+ workflow: string | null;
+ /**
+ * Graph
+ * @description The graph used to generate the image, as stringified JSON
+ */
+ graph: string | null;
+ };
+ /** WorkflowCallCompatibility */
+ WorkflowCallCompatibility: {
+ /**
+ * Is Callable
+ * @description Whether the workflow can currently be executed by call_saved_workflow.
+ */
+ is_callable: boolean;
+ /** @description Structured compatibility result. */
+ reason: components["schemas"]["WorkflowCallCompatibilityReason"];
+ /**
+ * Message
+ * @description Human-readable compatibility detail when unavailable.
+ */
+ message?: string | null;
+ };
+ /**
+ * WorkflowCallCompatibilityReason
+ * @enum {string}
+ */
+ WorkflowCallCompatibilityReason: "ok" | "missing_workflow_return" | "multiple_workflow_return" | "unsupported_node" | "unsupported_batch_input" | "invalid_graph" | "invalid_inputs" | "exceeds_capacity" | "unknown";
+ /**
+ * WorkflowCallExecution
+ * @description Tracks one parent/child workflow-call relationship and its lifecycle.
+ */
+ WorkflowCallExecution: {
+ /**
+ * Id
+ * @description The workflow-call execution id.
+ */
+ id?: string;
+ /**
+ * Parent Session Id
+ * @description The parent graph execution state id.
+ */
+ parent_session_id: string;
+ /**
+ * Child Session Id
+ * @description The child graph execution state id, if any.
+ */
+ child_session_id?: string | null;
+ /**
+ * Prepared Call Node Id
+ * @description The prepared exec node id for the parent call site.
+ */
+ prepared_call_node_id: string;
+ /**
+ * Source Call Node Id
+ * @description The source graph node id for the parent call site.
+ */
+ source_call_node_id: string;
+ /**
+ * Workflow Id
+ * @description The saved workflow being called.
+ */
+ workflow_id: string;
+ /**
+ * Depth
+ * @description The 1-based depth of this call frame.
+ */
+ depth: number;
+ /**
+ * Status
+ * @description The current workflow-call lifecycle state.
+ * @enum {string}
+ */
+ status: "waiting_for_child" | "running_child" | "completed" | "failed";
+ /**
+ * Error Message
+ * @description Failure reason, if the call failed.
+ */
+ error_message?: string | null;
+ /**
+ * Child Session Ids
+ * @description All child graph execution state ids.
+ */
+ child_session_ids?: string[];
+ /**
+ * Child Item Ids
+ * @description Child queue item ids in enqueue order.
+ */
+ child_item_ids?: number[];
+ /**
+ * Expected Child Count
+ * @description The number of child executions for this call.
+ * @default 1
+ */
+ expected_child_count?: number;
+ /**
+ * Completed Child Item Ids
+ * @description The child queue item ids whose workflow_return outputs have been aggregated.
+ */
+ completed_child_item_ids?: number[];
+ /**
+ * Aggregated Values
+ * @description The aggregated workflow_return values accumulated from child executions.
+ */
+ aggregated_values?: {
+ [key: string]: unknown[];
+ };
+ /**
+ * Child Outputs
+ * @description Workflow return values keyed by child queue item id.
+ */
+ child_outputs?: {
+ [key: string]: {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /**
+ * WorkflowCallFrame
+ * @description Represents one workflow-call frame in a nested call chain.
+ */
+ WorkflowCallFrame: {
+ /**
+ * Prepared Call Node Id
+ * @description The prepared exec node id for the call site.
+ */
+ prepared_call_node_id: string;
+ /**
+ * Source Call Node Id
+ * @description The source graph node id for the call site.
+ */
+ source_call_node_id: string;
+ /**
+ * Workflow Id
+ * @description The saved workflow being called.
+ */
+ workflow_id: string;
+ /**
+ * Depth
+ * @description The 1-based depth of this call frame.
+ */
+ depth: number;
+ };
+ /**
+ * WorkflowCallParentRef
+ * @description Reference from a child execution state back to its parent workflow-call relationship.
+ */
+ WorkflowCallParentRef: {
+ /**
+ * Workflow Call Id
+ * @description The workflow-call execution id.
+ */
+ workflow_call_id: string;
+ /**
+ * Parent Session Id
+ * @description The parent graph execution state id.
+ */
+ parent_session_id: string;
+ /**
+ * Prepared Call Node Id
+ * @description The prepared exec node id for the parent call site.
+ */
+ prepared_call_node_id: string;
+ /**
+ * Source Call Node Id
+ * @description The source graph node id for the parent call site.
+ */
+ source_call_node_id: string;
+ /**
+ * Workflow Id
+ * @description The saved workflow being called.
+ */
+ workflow_id: string;
+ /**
+ * Depth
+ * @description The 1-based depth of this call frame.
+ */
+ depth: number;
+ };
+ /**
+ * WorkflowCategory
+ * @enum {string}
+ */
+ WorkflowCategory: "user" | "default";
+ /**
+ * WorkflowCreatedEvent
+ * @description Event model for workflow_created
+ */
+ WorkflowCreatedEvent: {
+ /**
+ * Timestamp
+ * @description The timestamp of the event
+ */
+ timestamp: number;
+ /**
+ * Workflow Id
+ * @description The ID of the workflow
+ */
+ workflow_id: string;
+ /**
+ * User Id
+ * @description The owner of the workflow
+ */
+ user_id: string;
+ /**
+ * Is Public
+ * @description Whether the workflow is shared with all users
+ */
+ is_public: boolean;
+ };
+ /**
+ * WorkflowDeletedEvent
+ * @description Event model for workflow_deleted
+ */
+ WorkflowDeletedEvent: {
+ /**
+ * Timestamp
+ * @description The timestamp of the event
+ */
+ timestamp: number;
+ /**
+ * Workflow Id
+ * @description The ID of the workflow
+ */
+ workflow_id: string;
+ /**
+ * User Id
+ * @description The owner of the workflow
+ */
+ user_id: string;
+ /**
+ * Is Public
+ * @description Whether the workflow was shared when it was deleted
+ */
+ is_public: boolean;
+ };
+ /** WorkflowMeta */
+ WorkflowMeta: {
+ /**
+ * Version
+ * @description The version of the workflow schema.
+ */
+ version: string;
+ /** @description The category of the workflow (user or default). */
+ category: components["schemas"]["WorkflowCategory"];
+ };
+ /** WorkflowRecordDTO */
+ WorkflowRecordDTO: {
+ /**
+ * Workflow Id
+ * @description The id of the workflow.
+ */
+ workflow_id: string;
+ /**
+ * Name
+ * @description The name of the workflow.
+ */
+ name: string;
+ /**
+ * Created At
+ * @description The created timestamp of the workflow.
+ */
+ created_at: string;
+ /**
+ * Updated At
+ * @description The updated timestamp of the workflow.
+ */
+ updated_at: string;
+ /**
+ * Opened At
+ * @description The opened timestamp of the workflow.
+ */
+ opened_at?: string | null;
+ /**
+ * User Id
+ * @description The id of the user who owns this workflow.
+ */
+ user_id: string;
+ /**
+ * Is Public
+ * @description Whether this workflow is shared with all users.
+ */
+ is_public: boolean;
+ /** @description The workflow. */
+ workflow: components["schemas"]["Workflow"];
+ };
+ /** WorkflowRecordListItemWithThumbnailDTO */
+ WorkflowRecordListItemWithThumbnailDTO: {
+ /**
+ * Workflow Id
+ * @description The id of the workflow.
+ */
+ workflow_id: string;
+ /**
+ * Name
+ * @description The name of the workflow.
+ */
+ name: string;
+ /**
+ * Created At
+ * @description The created timestamp of the workflow.
+ */
+ created_at: string;
+ /**
+ * Updated At
+ * @description The updated timestamp of the workflow.
+ */
+ updated_at: string;
+ /**
+ * Opened At
+ * @description The opened timestamp of the workflow.
+ */
+ opened_at?: string | null;
+ /**
+ * User Id
+ * @description The id of the user who owns this workflow.
+ */
+ user_id: string;
+ /**
+ * Is Public
+ * @description Whether this workflow is shared with all users.
+ */
+ is_public: boolean;
+ /**
+ * Description
+ * @description The description of the workflow.
+ */
+ description: string;
+ /** @description The description of the workflow. */
+ category: components["schemas"]["WorkflowCategory"];
+ /**
+ * Tags
+ * @description The tags of the workflow.
+ */
+ tags: string;
+ /**
+ * Thumbnail Url
+ * @description The URL of the workflow thumbnail.
+ */
+ thumbnail_url?: string | null;
+ /** @description Whether this workflow is currently callable by call_saved_workflow. */
+ call_saved_workflow_compatibility?: components["schemas"]["WorkflowCallCompatibility"] | null;
+ };
+ /**
+ * WorkflowRecordOrderBy
+ * @description The order by options for workflow records
+ * @enum {string}
+ */
+ WorkflowRecordOrderBy: "created_at" | "updated_at" | "opened_at" | "name" | "is_public";
+ /** WorkflowRecordWithThumbnailDTO */
+ WorkflowRecordWithThumbnailDTO: {
+ /**
+ * Workflow Id
+ * @description The id of the workflow.
+ */
+ workflow_id: string;
+ /**
+ * Name
+ * @description The name of the workflow.
+ */
+ name: string;
+ /**
+ * Created At
+ * @description The created timestamp of the workflow.
+ */
+ created_at: string;
+ /**
+ * Updated At
+ * @description The updated timestamp of the workflow.
+ */
+ updated_at: string;
+ /**
+ * Opened At
+ * @description The opened timestamp of the workflow.
+ */
+ opened_at?: string | null;
+ /**
+ * User Id
+ * @description The id of the user who owns this workflow.
+ */
+ user_id: string;
+ /**
+ * Is Public
+ * @description Whether this workflow is shared with all users.
+ */
+ is_public: boolean;
+ /** @description The workflow. */
+ workflow: components["schemas"]["Workflow"];
+ /**
+ * Thumbnail Url
+ * @description The URL of the workflow thumbnail.
+ */
+ thumbnail_url?: string | null;
+ /** @description Whether this workflow is currently callable by call_saved_workflow. */
+ call_saved_workflow_compatibility?: components["schemas"]["WorkflowCallCompatibility"] | null;
+ };
+ /**
+ * Get Workflow Return Value
+ * @description Extracts one named value from a callable workflow return.
+ */
+ WorkflowReturnGetInvocation: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default false
+ */
+ use_cache?: boolean;
+ /**
+ * Values
+ * @description The named workflow return values.
+ * @default {}
+ */
+ values?: {
+ [key: string]: unknown;
+ };
+ /**
+ * Key
+ * @description The return key to extract.
+ * @default
+ */
+ key?: string;
+ /**
+ * type
+ * @default workflow_return_get
+ * @constant
+ */
+ type: "workflow_return_get";
+ };
+ /**
+ * WorkflowReturnGetOutput
+ * @description A value extracted from named workflow return values.
+ */
+ WorkflowReturnGetOutput: {
+ /**
+ * Value
+ * @description The extracted workflow return value.
+ */
+ value: unknown;
+ /**
+ * type
+ * @default workflow_return_get_output
+ * @constant
+ */
+ type: "workflow_return_get_output";
+ };
+ /**
+ * Workflow Return
+ * @description Defines the explicit named result returned by a callable workflow.
+ */
+ WorkflowReturnInvocation: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default false
+ */
+ use_cache?: boolean;
+ /**
+ * Values
+ * @description The named values returned to a calling workflow.
+ * @default []
+ */
+ values?: components["schemas"]["WorkflowReturnValueField"] | components["schemas"]["WorkflowReturnValueField"][];
+ /**
+ * type
+ * @default workflow_return
+ * @constant
+ */
+ type: "workflow_return";
+ };
+ /**
+ * WorkflowReturnOutput
+ * @description The explicit named values returned from a callable workflow.
+ */
+ WorkflowReturnOutput: {
+ /**
+ * Values
+ * @description The workflow return values, keyed by return name.
+ * @default {}
+ */
+ values: {
+ [key: string]: unknown;
+ };
+ /**
+ * type
+ * @default workflow_return_output
+ * @constant
+ */
+ type: "workflow_return_output";
+ };
+ /**
+ * WorkflowReturnValueField
+ * @description One named workflow return value.
+ */
+ WorkflowReturnValueField: {
+ /**
+ * Key
+ * @description The workflow return key.
+ */
+ key: string;
+ /**
+ * Value
+ * @description The workflow return value.
+ * @default null
+ */
+ value?: unknown;
+ };
+ /**
+ * Workflow Return Value
+ * @description Creates one named value for a callable workflow return.
+ */
+ WorkflowReturnValueInvocation: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default false
+ */
+ use_cache?: boolean;
+ /**
+ * Key
+ * @description The return key.
+ * @default
+ */
+ key?: string;
+ /**
+ * Value
+ * @description The value returned under this key.
+ * @default null
+ */
+ value?: unknown | null;
+ /**
+ * type
+ * @default workflow_return_value
+ * @constant
+ */
+ type: "workflow_return_value";
+ };
+ /**
+ * WorkflowReturnValueOutput
+ * @description A named workflow return value.
+ */
+ WorkflowReturnValueOutput: {
+ /**
+ * Return Value
+ * @description The named workflow return value.
+ */
+ value: components["schemas"]["WorkflowReturnValueField"];
+ /**
+ * type
+ * @default workflow_return_value_output
+ * @constant
+ */
+ type: "workflow_return_value_output";
+ };
+ /**
+ * WorkflowUpdatedEvent
+ * @description Event model for workflow_updated
+ */
+ WorkflowUpdatedEvent: {
+ /**
+ * Timestamp
+ * @description The timestamp of the event
+ */
+ timestamp: number;
+ /**
+ * Workflow Id
+ * @description The ID of the workflow
+ */
+ workflow_id: string;
+ /**
+ * User Id
+ * @description The owner of the workflow
+ */
+ user_id: string;
+ /**
+ * Old Is Public
+ * @description Whether the workflow was shared before the update
+ */
+ old_is_public: boolean;
+ /**
+ * New Is Public
+ * @description Whether the workflow is shared after the update
+ */
+ new_is_public: boolean;
+ };
+ /** WorkflowWithoutID */
+ WorkflowWithoutID: {
+ /**
+ * Name
+ * @description The name of the workflow.
+ */
+ name: string;
+ /**
+ * Author
+ * @description The author of the workflow.
+ */
+ author: string;
+ /**
+ * Description
+ * @description The description of the workflow.
+ */
+ description: string;
+ /**
+ * Version
+ * @description The version of the workflow.
+ */
+ version: string;
+ /**
+ * Contact
+ * @description The contact of the workflow.
+ */
+ contact: string;
+ /**
+ * Tags
+ * @description The tags of the workflow.
+ */
+ tags: string;
+ /**
+ * Notes
+ * @description The notes of the workflow.
+ */
+ notes: string;
+ /**
+ * Exposedfields
+ * @description The exposed fields of the workflow.
+ */
+ exposedFields: components["schemas"]["ExposedField"][];
+ /** @description The meta of the workflow. */
+ meta: components["schemas"]["WorkflowMeta"];
+ /**
+ * Nodes
+ * @description The nodes of the workflow.
+ */
+ nodes: {
+ [key: string]: components["schemas"]["JsonValue"];
+ }[];
+ /**
+ * Edges
+ * @description The edges of the workflow.
+ */
+ edges: {
+ [key: string]: components["schemas"]["JsonValue"];
+ }[];
+ /**
+ * Form
+ * @description The form of the workflow.
+ */
+ form?: {
+ [key: string]: components["schemas"]["JsonValue"];
+ } | null;
+ };
+ /**
+ * ZImageConditioningField
+ * @description A Z-Image conditioning tensor primitive value
+ */
+ ZImageConditioningField: {
+ /**
+ * Conditioning Name
+ * @description The name of conditioning tensor
+ */
+ conditioning_name: string;
+ /**
+ * @description The mask associated with this conditioning tensor for regional prompting. Excluded regions should be set to False, included regions should be set to True.
+ * @default null
+ */
+ mask?: components["schemas"]["TensorField"] | null;
+ };
+ /**
+ * ZImageConditioningOutput
+ * @description Base class for nodes that output a Z-Image text conditioning tensor.
+ */
+ ZImageConditioningOutput: {
+ /** @description Conditioning tensor */
+ conditioning: components["schemas"]["ZImageConditioningField"];
+ /**
+ * type
+ * @default z_image_conditioning_output
+ * @constant
+ */
+ type: "z_image_conditioning_output";
+ };
+ /**
+ * ZImageControlField
+ * @description A Z-Image control conditioning field for spatial control (Canny, HED, Depth, Pose, MLSD).
+ */
+ ZImageControlField: {
+ /**
+ * Image Name
+ * @description The name of the preprocessed control image
+ */
+ image_name: string;
+ /** @description The Z-Image ControlNet adapter model */
+ control_model: components["schemas"]["ModelIdentifierField"];
+ /**
+ * Control Context Scale
+ * @description The strength of the control signal. Recommended range: 0.65-0.80.
+ * @default 0.75
+ */
+ control_context_scale?: number;
+ /**
+ * Begin Step Percent
+ * @description When the control is first applied (% of total steps)
+ * @default 0
+ */
+ begin_step_percent?: number;
+ /**
+ * End Step Percent
+ * @description When the control is last applied (% of total steps)
+ * @default 1
+ */
+ end_step_percent?: number;
+ };
+ /**
+ * Z-Image ControlNet
+ * @description Configure Z-Image ControlNet for spatial conditioning.
+ *
+ * Takes a preprocessed control image (e.g., Canny edges, depth map, pose)
+ * and a Z-Image ControlNet adapter model to enable spatial control.
+ *
+ * Supports 5 control modes: Canny, HED, Depth, Pose, MLSD.
+ * Recommended control_context_scale: 0.65-0.80.
+ */
+ ZImageControlInvocation: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description The preprocessed control image (Canny, HED, Depth, Pose, or MLSD)
+ * @default null
+ */
+ image?: components["schemas"]["ImageField"] | null;
+ /**
+ * Control Model
+ * @description ControlNet model to load
+ * @default null
+ */
+ control_model?: components["schemas"]["ModelIdentifierField"] | null;
+ /**
+ * Control Scale
+ * @description Strength of the control signal. Recommended range: 0.65-0.80.
+ * @default 0.75
+ */
+ control_context_scale?: number;
+ /**
+ * Begin Step Percent
+ * @description When the control is first applied (% of total steps)
+ * @default 0
+ */
+ begin_step_percent?: number;
+ /**
+ * End Step Percent
+ * @description When the control is last applied (% of total steps)
+ * @default 1
+ */
+ end_step_percent?: number;
+ /**
+ * type
+ * @default z_image_control
+ * @constant
+ */
+ type: "z_image_control";
+ };
+ /**
+ * ZImageControlOutput
+ * @description Z-Image Control output containing control configuration.
+ */
+ ZImageControlOutput: {
+ /** @description Z-Image control conditioning */
+ control: components["schemas"]["ZImageControlField"];
+ /**
+ * type
+ * @default z_image_control_output
+ * @constant
+ */
+ type: "z_image_control_output";
+ };
+ /**
+ * Denoise - Z-Image
+ * @description Run the denoising process with a Z-Image model.
+ *
+ * Supports regional prompting by connecting multiple conditioning inputs with masks.
+ */
+ ZImageDenoiseInvocation: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description Latents tensor
+ * @default null
+ */
+ latents?: components["schemas"]["LatentsField"] | null;
+ /**
+ * @description Noise tensor
+ * @default null
+ */
+ noise?: components["schemas"]["LatentsField"] | null;
+ /**
+ * @description A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.
+ * @default null
+ */
+ denoise_mask?: components["schemas"]["DenoiseMaskField"] | null;
+ /**
+ * Denoising Start
+ * @description When to start denoising, expressed a percentage of total steps
+ * @default 0
+ */
+ denoising_start?: number;
+ /**
+ * Denoising End
+ * @description When to stop denoising, expressed a percentage of total steps
+ * @default 1
+ */
+ denoising_end?: number;
+ /**
+ * Add Noise
+ * @description Add noise based on denoising start.
+ * @default true
+ */
+ add_noise?: boolean;
+ /**
+ * Transformer
+ * @description Z-Image model (Transformer) to load
+ * @default null
+ */
+ transformer?: components["schemas"]["TransformerField"] | null;
+ /**
+ * Positive Conditioning
+ * @description Positive conditioning tensor
+ * @default null
+ */
+ positive_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
+ /**
+ * Negative Conditioning
+ * @description Negative conditioning tensor
+ * @default null
+ */
+ negative_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
+ /**
+ * Guidance Scale
+ * @description Guidance scale for classifier-free guidance. 1.0 = no CFG (recommended for Z-Image-Turbo). Values > 1.0 amplify guidance.
+ * @default 1
+ */
+ guidance_scale?: number;
+ /**
+ * Width
+ * @description Width of the generated image.
+ * @default 1024
+ */
+ width?: number;
+ /**
+ * Height
+ * @description Height of the generated image.
+ * @default 1024
+ */
+ height?: number;
+ /**
+ * Steps
+ * @description Number of denoising steps. 8 recommended for Z-Image-Turbo.
+ * @default 8
+ */
+ steps?: number;
+ /**
+ * Seed
+ * @description Randomness seed for reproducibility.
+ * @default 0
+ */
+ seed?: number;
+ /**
+ * @description Z-Image control conditioning for spatial control (Canny, HED, Depth, Pose, MLSD).
+ * @default null
+ */
+ control?: components["schemas"]["ZImageControlField"] | null;
+ /**
+ * @description VAE Required for control conditioning.
+ * @default null
+ */
+ vae?: components["schemas"]["VAEField"] | null;
+ /**
+ * Shift
+ * @description Override the timestep shift (mu) for the sigma schedule. Leave blank to auto-calculate based on image dimensions (recommended). Lower values (~0.5) produce less noise shifting, higher values (~1.15) produce more.
+ * @default null
+ */
+ shift?: number | null;
+ /**
+ * Scheduler
+ * @description Scheduler (sampler) for the denoising process. Euler is the default and recommended. Heun is 2nd-order (better quality, 2x slower). LCM works with Turbo only (not Base).
+ * @default euler
+ * @enum {string}
+ */
+ scheduler?: "euler" | "heun" | "lcm";
+ /**
+ * type
+ * @default z_image_denoise
+ * @constant
+ */
+ type: "z_image_denoise";
+ };
+ /**
+ * Denoise - Z-Image + Metadata
+ * @description Run denoising process with a Z-Image transformer model + metadata.
+ */
+ ZImageDenoiseMetaInvocation: {
+ /**
+ * @description Optional metadata to be saved with the image
+ * @default null
+ */
+ metadata?: components["schemas"]["MetadataField"] | null;
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description Latents tensor
+ * @default null
+ */
+ latents?: components["schemas"]["LatentsField"] | null;
+ /**
+ * @description Noise tensor
+ * @default null
+ */
+ noise?: components["schemas"]["LatentsField"] | null;
+ /**
+ * @description A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.
+ * @default null
+ */
+ denoise_mask?: components["schemas"]["DenoiseMaskField"] | null;
+ /**
+ * Denoising Start
+ * @description When to start denoising, expressed a percentage of total steps
+ * @default 0
+ */
+ denoising_start?: number;
+ /**
+ * Denoising End
+ * @description When to stop denoising, expressed a percentage of total steps
+ * @default 1
+ */
+ denoising_end?: number;
+ /**
+ * Add Noise
+ * @description Add noise based on denoising start.
+ * @default true
+ */
+ add_noise?: boolean;
+ /**
+ * Transformer
+ * @description Z-Image model (Transformer) to load
+ * @default null
+ */
+ transformer?: components["schemas"]["TransformerField"] | null;
+ /**
+ * Positive Conditioning
+ * @description Positive conditioning tensor
+ * @default null
+ */
+ positive_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
+ /**
+ * Negative Conditioning
+ * @description Negative conditioning tensor
+ * @default null
+ */
+ negative_conditioning?: components["schemas"]["ZImageConditioningField"] | components["schemas"]["ZImageConditioningField"][] | null;
+ /**
+ * Guidance Scale
+ * @description Guidance scale for classifier-free guidance. 1.0 = no CFG (recommended for Z-Image-Turbo). Values > 1.0 amplify guidance.
+ * @default 1
+ */
+ guidance_scale?: number;
+ /**
+ * Width
+ * @description Width of the generated image.
+ * @default 1024
+ */
+ width?: number;
+ /**
+ * Height
+ * @description Height of the generated image.
+ * @default 1024
+ */
+ height?: number;
+ /**
+ * Steps
+ * @description Number of denoising steps. 8 recommended for Z-Image-Turbo.
+ * @default 8
+ */
+ steps?: number;
+ /**
+ * Seed
+ * @description Randomness seed for reproducibility.
+ * @default 0
+ */
+ seed?: number;
+ /**
+ * @description Z-Image control conditioning for spatial control (Canny, HED, Depth, Pose, MLSD).
+ * @default null
+ */
+ control?: components["schemas"]["ZImageControlField"] | null;
+ /**
+ * @description VAE Required for control conditioning.
+ * @default null
+ */
+ vae?: components["schemas"]["VAEField"] | null;
+ /**
+ * Shift
+ * @description Override the timestep shift (mu) for the sigma schedule. Leave blank to auto-calculate based on image dimensions (recommended). Lower values (~0.5) produce less noise shifting, higher values (~1.15) produce more.
+ * @default null
+ */
+ shift?: number | null;
+ /**
+ * Scheduler
+ * @description Scheduler (sampler) for the denoising process. Euler is the default and recommended. Heun is 2nd-order (better quality, 2x slower). LCM works with Turbo only (not Base).
+ * @default euler
+ * @enum {string}
+ */
+ scheduler?: "euler" | "heun" | "lcm";
+ /**
+ * type
+ * @default z_image_denoise_meta
+ * @constant
+ */
+ type: "z_image_denoise_meta";
+ };
+ /**
+ * Image to Latents - Z-Image
+ * @description Generates latents from an image using Z-Image VAE (supports both Diffusers and FLUX VAE).
+ */
+ ZImageImageToLatentsInvocation: {
+ /**
+ * @description The board to save the image to
+ * @default null
+ */
+ board?: components["schemas"]["BoardField"] | null;
+ /**
+ * @description Optional metadata to be saved with the image
+ * @default null
+ */
+ metadata?: components["schemas"]["MetadataField"] | null;
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description The image to encode.
+ * @default null
+ */
+ image?: components["schemas"]["ImageField"] | null;
+ /**
+ * @description VAE
+ * @default null
+ */
+ vae?: components["schemas"]["VAEField"] | null;
+ /**
+ * type
+ * @default z_image_i2l
+ * @constant
+ */
+ type: "z_image_i2l";
+ };
+ /**
+ * Latents to Image - Z-Image
+ * @description Generates an image from latents using Z-Image VAE (supports both Diffusers and FLUX VAE).
+ */
+ ZImageLatentsToImageInvocation: {
+ /**
+ * @description The board to save the image to
+ * @default null
+ */
+ board?: components["schemas"]["BoardField"] | null;
+ /**
+ * @description Optional metadata to be saved with the image
+ * @default null
+ */
+ metadata?: components["schemas"]["MetadataField"] | null;
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description Latents tensor
+ * @default null
+ */
+ latents?: components["schemas"]["LatentsField"] | null;
+ /**
+ * @description VAE
+ * @default null
+ */
+ vae?: components["schemas"]["VAEField"] | null;
+ /**
+ * type
+ * @default z_image_l2i
+ * @constant
+ */
+ type: "z_image_l2i";
+ };
+ /**
+ * Apply LoRA Collection - Z-Image
+ * @description Applies a collection of LoRAs to a Z-Image transformer.
+ */
+ ZImageLoRACollectionLoader: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * LoRAs
+ * @description LoRA models and weights. May be a single LoRA or collection.
+ * @default null
+ */
+ loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null;
+ /**
+ * Transformer
+ * @description Transformer
+ * @default null
+ */
+ transformer?: components["schemas"]["TransformerField"] | null;
+ /**
+ * Qwen3 Encoder
+ * @description Qwen3 tokenizer and text encoder
+ * @default null
+ */
+ qwen3_encoder?: components["schemas"]["Qwen3EncoderField"] | null;
+ /**
+ * type
+ * @default z_image_lora_collection_loader
+ * @constant
+ */
+ type: "z_image_lora_collection_loader";
+ };
+ /**
+ * Apply LoRA - Z-Image
+ * @description Apply a LoRA model to a Z-Image transformer and/or Qwen3 text encoder.
+ */
+ ZImageLoRALoaderInvocation: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * LoRA
+ * @description LoRA model to load
+ * @default null
+ */
+ lora?: components["schemas"]["ModelIdentifierField"] | null;
+ /**
+ * Weight
+ * @description The weight at which the LoRA is applied to each model
+ * @default 0.75
+ */
+ weight?: number;
+ /**
+ * Z-Image Transformer
+ * @description Transformer
+ * @default null
+ */
+ transformer?: components["schemas"]["TransformerField"] | null;
+ /**
+ * Qwen3 Encoder
+ * @description Qwen3 tokenizer and text encoder
+ * @default null
+ */
+ qwen3_encoder?: components["schemas"]["Qwen3EncoderField"] | null;
+ /**
+ * type
+ * @default z_image_lora_loader
+ * @constant
+ */
+ type: "z_image_lora_loader";
+ };
+ /**
+ * ZImageLoRALoaderOutput
+ * @description Z-Image LoRA Loader Output
+ */
+ ZImageLoRALoaderOutput: {
+ /**
+ * Z-Image Transformer
+ * @description Transformer
+ * @default null
+ */
+ transformer: components["schemas"]["TransformerField"] | null;
+ /**
+ * Qwen3 Encoder
+ * @description Qwen3 tokenizer and text encoder
+ * @default null
+ */
+ qwen3_encoder: components["schemas"]["Qwen3EncoderField"] | null;
+ /**
+ * type
+ * @default z_image_lora_loader_output
+ * @constant
+ */
+ type: "z_image_lora_loader_output";
+ };
+ /**
+ * Main Model - Z-Image
+ * @description Loads a Z-Image model, outputting its submodels.
+ *
+ * Similar to FLUX, you can mix and match components:
+ * - Transformer: From Z-Image main model (GGUF quantized or Diffusers format)
+ * - VAE: Separate FLUX VAE (shared with FLUX models) or from a Diffusers Z-Image model
+ * - Qwen3 Encoder: Separate Qwen3Encoder model or from a Diffusers Z-Image model
+ */
+ ZImageModelLoaderInvocation: {
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * Transformer
+ * @description Z-Image model (Transformer) to load
+ */
+ model: components["schemas"]["ModelIdentifierField"];
+ /**
+ * VAE
+ * @description Standalone VAE model. Z-Image uses the same VAE as FLUX (16-channel). If not provided, VAE will be loaded from the Qwen3 Source model.
+ * @default null
+ */
+ vae_model?: components["schemas"]["ModelIdentifierField"] | null;
+ /**
+ * Qwen3 Encoder
+ * @description Standalone Qwen3 Encoder model. If not provided, encoder will be loaded from the Qwen3 Source model.
+ * @default null
+ */
+ qwen3_encoder_model?: components["schemas"]["ModelIdentifierField"] | null;
+ /**
+ * Qwen3 Source (Diffusers)
* @description Diffusers Z-Image model to extract VAE and/or Qwen3 encoder from. Use this if you don't have separate VAE/Qwen3 models. Ignored if both VAE and Qwen3 Encoder are provided separately.
* @default null
*/
@@ -34038,22 +36753,201 @@ export type components = {
*/
type: "z_image_text_encoder";
};
- /**
- * ZImageVariantType
- * @description Z-Image model variants.
- * @enum {string}
- */
- ZImageVariantType: "turbo" | "zbase";
+ /**
+ * ZImageVariantType
+ * @description Z-Image model variants.
+ * @enum {string}
+ */
+ ZImageVariantType: "turbo" | "zbase";
+ };
+ responses: never;
+ parameters: never;
+ requestBodies: never;
+ headers: never;
+ pathItems: never;
+};
+export type $defs = Record;
+export interface operations {
+ get_setup_status_api_v1_auth_status_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SetupStatusResponse"];
+ };
+ };
+ };
+ };
+ login_api_v1_auth_login_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["LoginRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["LoginResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ logout_api_v1_auth_logout_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["LogoutResponse"];
+ };
+ };
+ };
+ };
+ get_current_user_info_api_v1_auth_me_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["UserDTO"];
+ };
+ };
+ };
+ };
+ update_current_user_api_v1_auth_me_patch: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["UserProfileUpdateRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["UserDTO"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ setup_admin_api_v1_auth_setup_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["SetupRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SetupResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ generate_password_api_v1_auth_generate_password_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GeneratePasswordResponse"];
+ };
+ };
+ };
};
- responses: never;
- parameters: never;
- requestBodies: never;
- headers: never;
- pathItems: never;
-};
-export type $defs = Record;
-export interface operations {
- get_setup_status_api_v1_auth_status_get: {
+ list_users_api_v1_auth_users_get: {
parameters: {
query?: never;
header?: never;
@@ -34068,12 +36962,12 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["SetupStatusResponse"];
+ "application/json": components["schemas"]["UserDTO"][];
};
};
};
};
- login_api_v1_auth_login_post: {
+ create_user_api_v1_auth_users_post: {
parameters: {
query?: never;
header?: never;
@@ -34082,17 +36976,17 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["LoginRequest"];
+ "application/json": components["schemas"]["AdminUserCreateRequest"];
};
};
responses: {
/** @description Successful Response */
- 200: {
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["LoginResponse"];
+ "application/json": components["schemas"]["UserDTO"];
};
};
/** @description Validation Error */
@@ -34106,11 +37000,14 @@ export interface operations {
};
};
};
- logout_api_v1_auth_logout_post: {
+ get_user_api_v1_auth_users__user_id__get: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description User ID */
+ user_id: string;
+ };
cookie?: never;
};
requestBody?: never;
@@ -34121,19 +37018,65 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["LogoutResponse"];
+ "application/json": components["schemas"]["UserDTO"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- get_current_user_info_api_v1_auth_me_get: {
+ delete_user_api_v1_auth_users__user_id__delete: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description User ID */
+ user_id: string;
+ };
cookie?: never;
};
requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ update_user_api_v1_auth_users__user_id__patch: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ /** @description User ID */
+ user_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["AdminUserUpdateRequest"];
+ };
+ };
responses: {
/** @description Successful Response */
200: {
@@ -34144,9 +37087,18 @@ export interface operations {
"application/json": components["schemas"]["UserDTO"];
};
};
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
};
};
- update_current_user_api_v1_auth_me_patch: {
+ parse_dynamicprompts: {
parameters: {
query?: never;
header?: never;
@@ -34155,7 +37107,7 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["UserProfileUpdateRequest"];
+ "application/json": components["schemas"]["Body_parse_dynamicprompts"];
};
};
responses: {
@@ -34165,7 +37117,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["UserDTO"];
+ "application/json": components["schemas"]["DynamicPromptsResponse"];
};
};
/** @description Validation Error */
@@ -34179,7 +37131,7 @@ export interface operations {
};
};
};
- setup_admin_api_v1_auth_setup_post: {
+ expand_prompt: {
parameters: {
query?: never;
header?: never;
@@ -34188,18 +37140,427 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["SetupRequest"];
+ "application/json": components["schemas"]["ExpandPromptRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ExpandPromptResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ image_to_prompt: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ImageToPromptRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ImageToPromptResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_model_records: {
+ parameters: {
+ query?: {
+ /** @description Base models to include */
+ base_models?: components["schemas"]["BaseModelType"][] | null;
+ /** @description The type of model to get */
+ model_type?: components["schemas"]["ModelType"] | null;
+ /** @description Exact match on the name of the model */
+ model_name?: string | null;
+ /** @description Exact match on the format of the model (e.g. 'diffusers') */
+ model_format?: components["schemas"]["ModelFormat"] | null;
+ /** @description The field to order by */
+ order_by?: components["schemas"]["ModelRecordOrderBy"];
+ /** @description The direction to order by */
+ direction?: components["schemas"]["SQLiteDirection"];
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ModelsList"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_missing_models: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description List of models with missing files */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ModelsList"];
+ };
+ };
+ };
+ };
+ get_model_records_by_attrs: {
+ parameters: {
+ query: {
+ /** @description The name of the model */
+ name: string;
+ /** @description The type of the model */
+ type: components["schemas"]["ModelType"];
+ /** @description The base model of the model */
+ base: components["schemas"]["BaseModelType"];
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ get_model_records_by_hash: {
+ parameters: {
+ query: {
+ /** @description The hash of the model */
+ hash: string;
};
+ header?: never;
+ path?: never;
+ cookie?: never;
};
+ requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
- content: {
- "application/json": components["schemas"]["SetupResponse"];
- };
+ content: {
+ "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ get_model_record: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ /** @description Key of the model record to fetch. */
+ key: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description The model configuration was retrieved successfully */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ /**
+ * @example {
+ * "path": "string",
+ * "name": "string",
+ * "base": "sd-1",
+ * "type": "main",
+ * "format": "checkpoint",
+ * "config_path": "string",
+ * "key": "string",
+ * "hash": "string",
+ * "file_size": 1,
+ * "description": "string",
+ * "source": "string",
+ * "converted_at": 0,
+ * "variant": "normal",
+ * "prediction_type": "epsilon",
+ * "repo_variant": "fp16",
+ * "upcast_attention": false
+ * }
+ */
+ "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ };
+ };
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description The model could not be found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ delete_model: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ /** @description Unique key of model to remove from model registry. */
+ key: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Model deleted successfully */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Model not found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ update_model_record: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ /** @description Unique key of model */
+ key: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ModelRecordChanges"];
+ };
+ };
+ responses: {
+ /** @description The model was updated successfully */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ /**
+ * @example {
+ * "path": "string",
+ * "name": "string",
+ * "base": "sd-1",
+ * "type": "main",
+ * "format": "checkpoint",
+ * "config_path": "string",
+ * "key": "string",
+ * "hash": "string",
+ * "file_size": 1,
+ * "description": "string",
+ * "source": "string",
+ * "converted_at": 0,
+ * "variant": "normal",
+ * "prediction_type": "epsilon",
+ * "repo_variant": "fp16",
+ * "upcast_attention": false
+ * }
+ */
+ "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ };
+ };
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description The model could not be found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description There is already a model corresponding to the new name */
+ 409: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ reidentify_model: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ /** @description Key of the model to reidentify. */
+ key: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description The model configuration was retrieved successfully */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ /**
+ * @example {
+ * "path": "string",
+ * "name": "string",
+ * "base": "sd-1",
+ * "type": "main",
+ * "format": "checkpoint",
+ * "config_path": "string",
+ * "key": "string",
+ * "hash": "string",
+ * "file_size": 1,
+ * "description": "string",
+ * "source": "string",
+ * "converted_at": 0,
+ * "variant": "normal",
+ * "prediction_type": "epsilon",
+ * "repo_variant": "fp16",
+ * "upcast_attention": false
+ * }
+ */
+ "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ };
+ };
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description The model could not be found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -34212,67 +37573,72 @@ export interface operations {
};
};
};
- generate_password_api_v1_auth_generate_password_get: {
+ scan_for_models: {
parameters: {
- query?: never;
+ query?: {
+ /** @description Directory path to search for models */
+ scan_path?: string;
+ };
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Successful Response */
+ /** @description Directory scanned successfully */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["GeneratePasswordResponse"];
+ "application/json": components["schemas"]["FoundModel"][];
};
};
- };
- };
- list_users_api_v1_auth_users_get: {
- parameters: {
- query?: never;
- header?: never;
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description Successful Response */
- 200: {
+ /** @description Invalid directory path */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Validation Error */
+ 422: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["UserDTO"][];
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- create_user_api_v1_auth_users_post: {
+ get_hugging_face_models: {
parameters: {
- query?: never;
+ query?: {
+ /** @description Hugging face repo to search for models */
+ hugging_face_repo?: string;
+ };
header?: never;
path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": components["schemas"]["AdminUserCreateRequest"];
- };
- };
+ requestBody?: never;
responses: {
- /** @description Successful Response */
- 201: {
+ /** @description Hugging Face repo scanned successfully */
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["UserDTO"];
+ "application/json": components["schemas"]["HuggingFaceModels"];
+ };
+ };
+ /** @description Invalid hugging face repo */
+ 400: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -34285,26 +37651,40 @@ export interface operations {
};
};
};
- get_user_api_v1_auth_users__user_id__get: {
+ get_model_image: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description User ID */
- user_id: string;
+ /** @description The name of model image file to get */
+ key: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Successful Response */
+ /** @description The model image was fetched successfully */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["UserDTO"];
+ "application/json": unknown;
+ };
+ };
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description The model image could not be found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -34317,25 +37697,32 @@ export interface operations {
};
};
};
- delete_user_api_v1_auth_users__user_id__delete: {
+ delete_model_image: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description User ID */
- user_id: string;
+ /** @description Unique key of model image to remove from model_images directory. */
+ key: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Successful Response */
+ /** @description Model image deleted successfully */
204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
+ /** @description Model image not found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
/** @description Validation Error */
422: {
headers: {
@@ -34347,30 +37734,37 @@ export interface operations {
};
};
};
- update_user_api_v1_auth_users__user_id__patch: {
+ update_model_image: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description User ID */
- user_id: string;
+ /** @description Unique key of model */
+ key: string;
};
cookie?: never;
};
requestBody: {
content: {
- "application/json": components["schemas"]["AdminUserUpdateRequest"];
+ "multipart/form-data": components["schemas"]["Body_update_model_image"];
};
};
responses: {
- /** @description Successful Response */
+ /** @description The model image was updated successfully */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["UserDTO"];
+ "application/json": unknown;
+ };
+ };
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -34383,7 +37777,7 @@ export interface operations {
};
};
};
- parse_dynamicprompts: {
+ bulk_delete_models: {
parameters: {
query?: never;
header?: never;
@@ -34392,17 +37786,17 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["Body_parse_dynamicprompts"];
+ "application/json": components["schemas"]["BulkDeleteModelsRequest"];
};
};
responses: {
- /** @description Successful Response */
+ /** @description Models deleted (possibly with some failures) */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DynamicPromptsResponse"];
+ "application/json": components["schemas"]["BulkDeleteModelsResponse"];
};
};
/** @description Validation Error */
@@ -34416,7 +37810,7 @@ export interface operations {
};
};
};
- expand_prompt: {
+ bulk_reidentify_models: {
parameters: {
query?: never;
header?: never;
@@ -34425,17 +37819,17 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["ExpandPromptRequest"];
+ "application/json": components["schemas"]["BulkReidentifyModelsRequest"];
};
};
responses: {
- /** @description Successful Response */
+ /** @description Models reidentified (possibly with some failures) */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ExpandPromptResponse"];
+ "application/json": components["schemas"]["BulkReidentifyModelsResponse"];
};
};
/** @description Validation Error */
@@ -34449,18 +37843,14 @@ export interface operations {
};
};
};
- image_to_prompt: {
+ list_model_installs: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": components["schemas"]["ImageToPromptRequest"];
- };
- };
+ requestBody?: never;
responses: {
/** @description Successful Response */
200: {
@@ -34468,50 +37858,53 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageToPromptResponse"];
- };
- };
- /** @description Validation Error */
- 422: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["HTTPValidationError"];
+ "application/json": components["schemas"]["ModelInstallJob"][];
};
};
};
};
- list_model_records: {
+ install_model: {
parameters: {
- query?: {
- /** @description Base models to include */
- base_models?: components["schemas"]["BaseModelType"][] | null;
- /** @description The type of model to get */
- model_type?: components["schemas"]["ModelType"] | null;
- /** @description Exact match on the name of the model */
- model_name?: string | null;
- /** @description Exact match on the format of the model (e.g. 'diffusers') */
- model_format?: components["schemas"]["ModelFormat"] | null;
- /** @description The field to order by */
- order_by?: components["schemas"]["ModelRecordOrderBy"];
- /** @description The direction to order by */
- direction?: components["schemas"]["SQLiteDirection"];
+ query: {
+ /** @description Model source to install, can be a local path, repo_id, or remote URL */
+ source: string;
+ /** @description Whether or not to install a local model in place */
+ inplace?: boolean | null;
+ /** @description access token for the remote resource */
+ access_token?: string | null;
};
header?: never;
path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["ModelRecordChanges"];
+ };
+ };
responses: {
- /** @description Successful Response */
- 200: {
+ /** @description The model imported successfully */
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ModelsList"];
+ "application/json": components["schemas"]["ModelInstallJob"];
+ };
+ };
+ /** @description There is already a model corresponding to this path or repo_id */
+ 409: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Unrecognized file/folder format */
+ 415: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -34522,38 +37915,18 @@ export interface operations {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
- };
- };
- list_missing_models: {
- parameters: {
- query?: never;
- header?: never;
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description List of models with missing files */
- 200: {
+ /** @description The model appeared to import successfully, but could not be found in the model manager */
+ 424: {
headers: {
[name: string]: unknown;
};
- content: {
- "application/json": components["schemas"]["ModelsList"];
- };
+ content?: never;
};
};
};
- get_model_records_by_attrs: {
+ prune_model_install_jobs: {
parameters: {
- query: {
- /** @description The name of the model */
- name: string;
- /** @description The type of the model */
- type: components["schemas"]["ModelType"];
- /** @description The base model of the model */
- base: components["schemas"]["BaseModelType"];
- };
+ query?: never;
header?: never;
path?: never;
cookie?: never;
@@ -34566,25 +37939,30 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ "application/json": unknown;
};
};
- /** @description Validation Error */
- 422: {
+ /** @description All completed and errored jobs have been pruned */
+ 204: {
headers: {
[name: string]: unknown;
};
- content: {
- "application/json": components["schemas"]["HTTPValidationError"];
+ content?: never;
+ };
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
};
};
- get_model_records_by_hash: {
+ install_hugging_face_model: {
parameters: {
query: {
- /** @description The hash of the model */
- hash: string;
+ /** @description HuggingFace repo_id to install */
+ source: string;
};
header?: never;
path?: never;
@@ -34592,14 +37970,28 @@ export interface operations {
};
requestBody?: never;
responses: {
- /** @description Successful Response */
- 200: {
+ /** @description The model is being installed */
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
+ "text/html": string;
+ };
+ };
+ /** @description Bad request */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description There is already a model corresponding to this path or repo_id */
+ 409: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -34612,55 +38004,28 @@ export interface operations {
};
};
};
- get_model_record: {
+ get_model_install_job: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description Key of the model record to fetch. */
- key: string;
+ /** @description Model install id */
+ id: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description The model configuration was retrieved successfully */
+ /** @description Success */
200: {
headers: {
[name: string]: unknown;
};
content: {
- /**
- * @example {
- * "path": "string",
- * "name": "string",
- * "base": "sd-1",
- * "type": "main",
- * "format": "checkpoint",
- * "config_path": "string",
- * "key": "string",
- * "hash": "string",
- * "file_size": 1,
- * "description": "string",
- * "source": "string",
- * "converted_at": 0,
- * "variant": "normal",
- * "prediction_type": "epsilon",
- * "repo_variant": "fp16",
- * "upcast_attention": false
- * }
- */
- "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
- };
- };
- /** @description Bad request */
- 400: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["ModelInstallJob"];
};
- content?: never;
};
- /** @description The model could not be found */
+ /** @description No such job */
404: {
headers: {
[name: string]: unknown;
@@ -34678,27 +38043,29 @@ export interface operations {
};
};
};
- delete_model: {
+ cancel_model_install_job: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description Unique key of model to remove from model registry. */
- key: string;
+ /** @description Model install job ID */
+ id: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Model deleted successfully */
- 204: {
+ /** @description The job was cancelled successfully */
+ 201: {
headers: {
[name: string]: unknown;
};
- content?: never;
+ content: {
+ "application/json": unknown;
+ };
};
- /** @description Model not found */
- 404: {
+ /** @description No such job */
+ 415: {
headers: {
[name: string]: unknown;
};
@@ -34715,67 +38082,29 @@ export interface operations {
};
};
};
- update_model_record: {
+ pause_model_install_job: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description Unique key of model */
- key: string;
+ /** @description Model install job ID */
+ id: number;
};
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": components["schemas"]["ModelRecordChanges"];
- };
- };
+ requestBody?: never;
responses: {
- /** @description The model was updated successfully */
- 200: {
+ /** @description The job was paused successfully */
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- /**
- * @example {
- * "path": "string",
- * "name": "string",
- * "base": "sd-1",
- * "type": "main",
- * "format": "checkpoint",
- * "config_path": "string",
- * "key": "string",
- * "hash": "string",
- * "file_size": 1,
- * "description": "string",
- * "source": "string",
- * "converted_at": 0,
- * "variant": "normal",
- * "prediction_type": "epsilon",
- * "repo_variant": "fp16",
- * "upcast_attention": false
- * }
- */
- "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
- };
- };
- /** @description Bad request */
- 400: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description The model could not be found */
- 404: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["ModelInstallJob"];
};
- content?: never;
};
- /** @description There is already a model corresponding to the new name */
- 409: {
+ /** @description No such job */
+ 415: {
headers: {
[name: string]: unknown;
};
@@ -34792,56 +38121,29 @@ export interface operations {
};
};
};
- reidentify_model: {
+ resume_model_install_job: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description Key of the model to reidentify. */
- key: string;
+ /** @description Model install job ID */
+ id: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description The model configuration was retrieved successfully */
- 200: {
+ /** @description The job was resumed successfully */
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- /**
- * @example {
- * "path": "string",
- * "name": "string",
- * "base": "sd-1",
- * "type": "main",
- * "format": "checkpoint",
- * "config_path": "string",
- * "key": "string",
- * "hash": "string",
- * "file_size": 1,
- * "description": "string",
- * "source": "string",
- * "converted_at": 0,
- * "variant": "normal",
- * "prediction_type": "epsilon",
- * "repo_variant": "fp16",
- * "upcast_attention": false
- * }
- */
- "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
- };
- };
- /** @description Bad request */
- 400: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["ModelInstallJob"];
};
- content?: never;
};
- /** @description The model could not be found */
- 404: {
+ /** @description No such job */
+ 415: {
headers: {
[name: string]: unknown;
};
@@ -34858,29 +38160,29 @@ export interface operations {
};
};
};
- scan_for_models: {
+ restart_failed_model_install_job: {
parameters: {
- query?: {
- /** @description Directory path to search for models */
- scan_path?: string;
- };
+ query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description Model install job ID */
+ id: number;
+ };
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Directory scanned successfully */
- 200: {
+ /** @description Failed files restarted successfully */
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["FoundModel"][];
+ "application/json": components["schemas"]["ModelInstallJob"];
};
};
- /** @description Invalid directory path */
- 400: {
+ /** @description No such job */
+ 415: {
headers: {
[name: string]: unknown;
};
@@ -34897,29 +38199,33 @@ export interface operations {
};
};
};
- get_hugging_face_models: {
+ restart_model_install_file: {
parameters: {
- query?: {
- /** @description Hugging face repo to search for models */
- hugging_face_repo?: string;
- };
+ query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description Model install job ID */
+ id: number;
+ };
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": string;
+ };
+ };
responses: {
- /** @description Hugging Face repo scanned successfully */
- 200: {
+ /** @description File restarted successfully */
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HuggingFaceModels"];
+ "application/json": components["schemas"]["ModelInstallJob"];
};
};
- /** @description Invalid hugging face repo */
- 400: {
+ /** @description No such job */
+ 415: {
headers: {
[name: string]: unknown;
};
@@ -34936,25 +38242,45 @@ export interface operations {
};
};
};
- get_model_image: {
+ convert_model: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of model image file to get */
+ /** @description Unique key of the safetensors main model to convert to diffusers format. */
key: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description The model image was fetched successfully */
+ /** @description Model converted successfully */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": unknown;
+ /**
+ * @example {
+ * "path": "string",
+ * "name": "string",
+ * "base": "sd-1",
+ * "type": "main",
+ * "format": "checkpoint",
+ * "config_path": "string",
+ * "key": "string",
+ * "hash": "string",
+ * "file_size": 1,
+ * "description": "string",
+ * "source": "string",
+ * "converted_at": 0,
+ * "variant": "normal",
+ * "prediction_type": "epsilon",
+ * "repo_variant": "fp16",
+ * "upcast_attention": false
+ * }
+ */
+ "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_Wan_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_Wan_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_Wan_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["VAE_Diffusers_Wan_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Wan_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["WanT5Encoder_WanT5Encoder_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
};
};
/** @description Bad request */
@@ -34964,13 +38290,20 @@ export interface operations {
};
content?: never;
};
- /** @description The model image could not be found */
+ /** @description Model not found */
404: {
headers: {
[name: string]: unknown;
};
content?: never;
};
+ /** @description There is already a model registered at this location */
+ 409: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
/** @description Validation Error */
422: {
headers: {
@@ -34982,60 +38315,56 @@ export interface operations {
};
};
};
- delete_model_image: {
+ get_starter_models: {
parameters: {
query?: never;
header?: never;
- path: {
- /** @description Unique key of model image to remove from model_images directory. */
- key: string;
- };
+ path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Model image deleted successfully */
- 204: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
- content?: never;
- };
- /** @description Model image not found */
- 404: {
- headers: {
- [name: string]: unknown;
+ content: {
+ "application/json": components["schemas"]["StarterModelResponse"];
};
- content?: never;
};
- /** @description Validation Error */
- 422: {
+ };
+ };
+ get_stats: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HTTPValidationError"];
+ "application/json": components["schemas"]["CacheStats"] | null;
};
};
};
};
- update_model_image: {
+ empty_model_cache: {
parameters: {
query?: never;
header?: never;
- path: {
- /** @description Unique key of model */
- key: string;
- };
+ path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "multipart/form-data": components["schemas"]["Body_update_model_image"];
- };
- };
+ requestBody?: never;
responses: {
- /** @description The model image was updated successfully */
+ /** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
@@ -35044,25 +38373,29 @@ export interface operations {
"application/json": unknown;
};
};
- /** @description Bad request */
- 400: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description Validation Error */
- 422: {
+ };
+ };
+ get_hf_login_status: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HTTPValidationError"];
+ "application/json": components["schemas"]["HFTokenStatus"];
};
};
};
};
- bulk_delete_models: {
+ do_hf_login: {
parameters: {
query?: never;
header?: never;
@@ -35071,17 +38404,17 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["BulkDeleteModelsRequest"];
+ "application/json": components["schemas"]["Body_do_hf_login"];
};
};
responses: {
- /** @description Models deleted (possibly with some failures) */
+ /** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["BulkDeleteModelsResponse"];
+ "application/json": components["schemas"]["HFTokenStatus"];
};
};
/** @description Validation Error */
@@ -35095,40 +38428,27 @@ export interface operations {
};
};
};
- bulk_reidentify_models: {
+ reset_hf_token: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": components["schemas"]["BulkReidentifyModelsRequest"];
- };
- };
+ requestBody?: never;
responses: {
- /** @description Models reidentified (possibly with some failures) */
+ /** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["BulkReidentifyModelsResponse"];
- };
- };
- /** @description Validation Error */
- 422: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["HTTPValidationError"];
+ "application/json": components["schemas"]["HFTokenStatus"];
};
};
};
};
- list_model_installs: {
+ get_orphaned_models: {
parameters: {
query?: never;
header?: never;
@@ -35143,53 +38463,32 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ModelInstallJob"][];
+ "application/json": components["schemas"]["OrphanedModelInfo"][];
};
};
};
};
- install_model: {
+ delete_orphaned_models: {
parameters: {
- query: {
- /** @description Model source to install, can be a local path, repo_id, or remote URL */
- source: string;
- /** @description Whether or not to install a local model in place */
- inplace?: boolean | null;
- /** @description access token for the remote resource */
- access_token?: string | null;
- };
+ query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
- "application/json": components["schemas"]["ModelRecordChanges"];
+ "application/json": components["schemas"]["DeleteOrphanedModelsRequest"];
};
};
responses: {
- /** @description The model imported successfully */
- 201: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ModelInstallJob"];
- };
- };
- /** @description There is already a model corresponding to this path or repo_id */
- 409: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description Unrecognized file/folder format */
- 415: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["DeleteOrphanedModelsResponse"];
};
- content?: never;
};
/** @description Validation Error */
422: {
@@ -35200,16 +38499,29 @@ export interface operations {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
- /** @description The model appeared to import successfully, but could not be found in the model manager */
- 424: {
+ };
+ };
+ list_downloads: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
- content?: never;
+ content: {
+ "application/json": components["schemas"]["DownloadJob"][];
+ };
};
};
};
- prune_model_install_jobs: {
+ prune_downloads: {
parameters: {
query?: never;
header?: never;
@@ -35227,7 +38539,7 @@ export interface operations {
"application/json": unknown;
};
};
- /** @description All completed and errored jobs have been pruned */
+ /** @description All completed jobs have been pruned */
204: {
headers: {
[name: string]: unknown;
@@ -35243,40 +38555,27 @@ export interface operations {
};
};
};
- install_hugging_face_model: {
+ download: {
parameters: {
- query: {
- /** @description HuggingFace repo_id to install */
- source: string;
- };
+ query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["Body_download"];
+ };
+ };
responses: {
- /** @description The model is being installed */
- 201: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "text/html": string;
- };
- };
- /** @description Bad request */
- 400: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description There is already a model corresponding to this path or repo_id */
- 409: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["DownloadJob"];
};
- content?: never;
};
/** @description Validation Error */
422: {
@@ -35289,12 +38588,12 @@ export interface operations {
};
};
};
- get_model_install_job: {
+ get_download_job: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description Model install id */
+ /** @description ID of the download job to fetch. */
id: number;
};
cookie?: never;
@@ -35307,10 +38606,10 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ModelInstallJob"];
+ "application/json": components["schemas"]["DownloadJob"];
};
};
- /** @description No such job */
+ /** @description The requested download JobID could not be found */
404: {
headers: {
[name: string]: unknown;
@@ -35328,20 +38627,20 @@ export interface operations {
};
};
};
- cancel_model_install_job: {
+ cancel_download_job: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description Model install job ID */
+ /** @description ID of the download job to cancel. */
id: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description The job was cancelled successfully */
- 201: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
@@ -35349,8 +38648,15 @@ export interface operations {
"application/json": unknown;
};
};
- /** @description No such job */
- 415: {
+ /** @description Job has been cancelled */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description The requested download JobID could not be found */
+ 404: {
headers: {
[name: string]: unknown;
};
@@ -35367,149 +38673,127 @@ export interface operations {
};
};
};
- pause_model_install_job: {
+ cancel_all_download_jobs: {
parameters: {
query?: never;
header?: never;
- path: {
- /** @description Model install job ID */
- id: number;
- };
+ path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description The job was paused successfully */
- 201: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ModelInstallJob"];
+ "application/json": unknown;
};
};
- /** @description No such job */
- 415: {
+ /** @description Download jobs have been cancelled */
+ 204: {
headers: {
[name: string]: unknown;
};
content?: never;
};
- /** @description Validation Error */
- 422: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["HTTPValidationError"];
- };
- };
};
};
- resume_model_install_job: {
+ start_image_move: {
parameters: {
query?: never;
header?: never;
- path: {
- /** @description Model install job ID */
- id: number;
- };
+ path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description The job was resumed successfully */
- 201: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": components["schemas"]["ModelInstallJob"];
- };
- };
- /** @description No such job */
- 415: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description Validation Error */
- 422: {
+ /** @description Successful Response */
+ 202: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HTTPValidationError"];
+ "application/json": components["schemas"]["ImageMoveStatusResponse"];
};
};
};
};
- restart_failed_model_install_job: {
+ start_image_move_recovery: {
parameters: {
query?: never;
header?: never;
- path: {
- /** @description Model install job ID */
- id: number;
- };
+ path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Failed files restarted successfully */
- 201: {
+ /** @description Successful Response */
+ 202: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ModelInstallJob"];
- };
- };
- /** @description No such job */
- 415: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["ImageMoveStatusResponse"];
};
- content?: never;
};
- /** @description Validation Error */
- 422: {
+ };
+ };
+ get_image_move_status: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HTTPValidationError"];
+ "application/json": components["schemas"]["ImageMoveStatusResponse"];
};
};
};
};
- restart_model_install_file: {
+ upload_image: {
parameters: {
- query?: never;
- header?: never;
- path: {
- /** @description Model install job ID */
- id: number;
+ query: {
+ /** @description The category of the image */
+ image_category: components["schemas"]["ImageCategory"];
+ /** @description Whether this is an intermediate image */
+ is_intermediate: boolean;
+ /** @description The board to add this image to, if any */
+ board_id?: string | null;
+ /** @description The session ID associated with this upload, if any */
+ session_id?: string | null;
+ /** @description Whether to crop the image */
+ crop_visible?: boolean | null;
};
+ header?: never;
+ path?: never;
cookie?: never;
};
requestBody: {
content: {
- "application/json": string;
+ "multipart/form-data": components["schemas"]["Body_upload_image"];
};
};
responses: {
- /** @description File restarted successfully */
+ /** @description The image was uploaded successfully */
201: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ModelInstallJob"];
+ "application/json": components["schemas"]["ImageDTO"];
};
};
- /** @description No such job */
+ /** @description Image upload failed */
415: {
headers: {
[name: string]: unknown;
@@ -35527,67 +38811,42 @@ export interface operations {
};
};
};
- convert_model: {
+ list_image_dtos: {
parameters: {
- query?: never;
- header?: never;
- path: {
- /** @description Unique key of the safetensors main model to convert to diffusers format. */
- key: string;
+ query?: {
+ /** @description The origin of images to list. */
+ image_origin?: components["schemas"]["ResourceOrigin"] | null;
+ /** @description The categories of image to include. */
+ categories?: components["schemas"]["ImageCategory"][] | null;
+ /** @description Whether to list intermediate images. */
+ is_intermediate?: boolean | null;
+ /** @description The board id to filter by. Use 'none' to find images without a board. */
+ board_id?: string | null;
+ /** @description The page offset */
+ offset?: number;
+ /** @description The number of images per page */
+ limit?: number;
+ /** @description The order of sort */
+ order_dir?: components["schemas"]["SQLiteDirection"];
+ /** @description Whether to sort by starred images first */
+ starred_first?: boolean;
+ /** @description The term to search for */
+ search_term?: string | null;
};
+ header?: never;
+ path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Model converted successfully */
+ /** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
- /**
- * @example {
- * "path": "string",
- * "name": "string",
- * "base": "sd-1",
- * "type": "main",
- * "format": "checkpoint",
- * "config_path": "string",
- * "key": "string",
- * "hash": "string",
- * "file_size": 1,
- * "description": "string",
- * "source": "string",
- * "converted_at": 0,
- * "variant": "normal",
- * "prediction_type": "epsilon",
- * "repo_variant": "fp16",
- * "upcast_attention": false
- * }
- */
- "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"];
- };
- };
- /** @description Bad request */
- 400: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description Model not found */
- 404: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description There is already a model registered at this location */
- 409: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["OffsetPaginatedResults_ImageDTO_"];
};
- content?: never;
};
/** @description Validation Error */
422: {
@@ -35600,14 +38859,18 @@ export interface operations {
};
};
};
- get_starter_models: {
+ create_image_upload_entry: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["Body_create_image_upload_entry"];
+ };
+ };
responses: {
/** @description Successful Response */
200: {
@@ -35615,36 +38878,28 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["StarterModelResponse"];
+ "application/json": components["schemas"]["ImageUploadEntry"];
};
};
- };
- };
- get_stats: {
- parameters: {
- query?: never;
- header?: never;
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description Successful Response */
- 200: {
+ /** @description Validation Error */
+ 422: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["CacheStats"] | null;
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- empty_model_cache: {
+ get_image_dto: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description The name of image to get */
+ image_name: string;
+ };
cookie?: never;
};
requestBody?: never;
@@ -35655,16 +38910,28 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": unknown;
+ "application/json": components["schemas"]["ImageDTO"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- get_hf_login_status: {
+ delete_image: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description The name of the image to delete */
+ image_name: string;
+ };
cookie?: never;
};
requestBody?: never;
@@ -35675,21 +38942,33 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HFTokenStatus"];
+ "application/json": components["schemas"]["DeleteImagesResult"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- do_hf_login: {
+ update_image: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description The name of the image to update */
+ image_name: string;
+ };
cookie?: never;
};
requestBody: {
content: {
- "application/json": components["schemas"]["Body_do_hf_login"];
+ "application/json": components["schemas"]["ImageRecordChanges"];
};
};
responses: {
@@ -35699,7 +38978,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HFTokenStatus"];
+ "application/json": components["schemas"]["ImageDTO"];
};
};
/** @description Validation Error */
@@ -35713,7 +38992,7 @@ export interface operations {
};
};
};
- reset_hf_token: {
+ get_intermediates_count: {
parameters: {
query?: never;
header?: never;
@@ -35728,12 +39007,12 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["HFTokenStatus"];
+ "application/json": number;
};
};
};
};
- get_orphaned_models: {
+ clear_intermediates: {
parameters: {
query?: never;
header?: never;
@@ -35748,23 +39027,22 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["OrphanedModelInfo"][];
+ "application/json": number;
};
};
};
};
- delete_orphaned_models: {
- parameters: {
- query?: never;
- header?: never;
- path?: never;
- cookie?: never;
- };
- requestBody: {
- content: {
- "application/json": components["schemas"]["DeleteOrphanedModelsRequest"];
+ get_image_metadata: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ /** @description The name of image to get */
+ image_name: string;
};
+ cookie?: never;
};
+ requestBody?: never;
responses: {
/** @description Successful Response */
200: {
@@ -35772,7 +39050,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DeleteOrphanedModelsResponse"];
+ "application/json": components["schemas"]["MetadataField"] | null;
};
};
/** @description Validation Error */
@@ -35786,11 +39064,14 @@ export interface operations {
};
};
};
- list_downloads: {
+ get_image_workflow: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description The name of image whose workflow to get */
+ image_name: string;
+ };
cookie?: never;
};
requestBody?: never;
@@ -35801,66 +39082,86 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DownloadJob"][];
+ "application/json": components["schemas"]["WorkflowAndGraphResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- prune_downloads: {
+ get_image_full: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description The name of full-resolution image file to get */
+ image_name: string;
+ };
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Successful Response */
+ /** @description Return the full-resolution image */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": unknown;
+ "image/png": unknown;
};
};
- /** @description All completed jobs have been pruned */
- 204: {
+ /** @description Image not found */
+ 404: {
headers: {
[name: string]: unknown;
};
content?: never;
};
- /** @description Bad request */
- 400: {
+ /** @description Validation Error */
+ 422: {
headers: {
[name: string]: unknown;
};
- content?: never;
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
};
};
};
- download: {
+ get_image_full_head: {
parameters: {
query?: never;
header?: never;
- path?: never;
- cookie?: never;
- };
- requestBody: {
- content: {
- "application/json": components["schemas"]["Body_download"];
+ path: {
+ /** @description The name of full-resolution image file to get */
+ image_name: string;
};
+ cookie?: never;
};
+ requestBody?: never;
responses: {
- /** @description Successful Response */
+ /** @description Return the full-resolution image */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DownloadJob"];
+ "image/png": unknown;
+ };
+ };
+ /** @description Image not found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -35873,28 +39174,28 @@ export interface operations {
};
};
};
- get_download_job: {
+ get_image_thumbnail: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description ID of the download job to fetch. */
- id: number;
+ /** @description The name of thumbnail image file to get */
+ image_name: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Success */
+ /** @description Return the image thumbnail */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DownloadJob"];
+ "image/webp": unknown;
};
};
- /** @description The requested download JobID could not be found */
+ /** @description Image not found */
404: {
headers: {
[name: string]: unknown;
@@ -35912,13 +39213,13 @@ export interface operations {
};
};
};
- cancel_download_job: {
+ get_image_urls: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description ID of the download job to cancel. */
- id: number;
+ /** @description The name of the image whose URL to get */
+ image_name: string;
};
cookie?: never;
};
@@ -35930,22 +39231,8 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": unknown;
- };
- };
- /** @description Job has been cancelled */
- 204: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description The requested download JobID could not be found */
- 404: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["ImageUrlsDTO"];
};
- content?: never;
};
/** @description Validation Error */
422: {
@@ -35958,14 +39245,18 @@ export interface operations {
};
};
};
- cancel_all_download_jobs: {
+ delete_images_from_list: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["Body_delete_images_from_list"];
+ };
+ };
responses: {
/** @description Successful Response */
200: {
@@ -35973,19 +39264,21 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": unknown;
+ "application/json": components["schemas"]["DeleteImagesResult"];
};
};
- /** @description Download jobs have been cancelled */
- 204: {
+ /** @description Validation Error */
+ 422: {
headers: {
[name: string]: unknown;
};
- content?: never;
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
};
};
};
- start_image_move: {
+ delete_uncategorized_images: {
parameters: {
query?: never;
header?: never;
@@ -35995,44 +39288,61 @@ export interface operations {
requestBody?: never;
responses: {
/** @description Successful Response */
- 202: {
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageMoveStatusResponse"];
+ "application/json": components["schemas"]["DeleteImagesResult"];
};
};
};
};
- start_image_move_recovery: {
+ star_images_in_list: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["Body_star_images_in_list"];
+ };
+ };
responses: {
/** @description Successful Response */
- 202: {
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageMoveStatusResponse"];
+ "application/json": components["schemas"]["StarredImagesResult"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- get_image_move_status: {
+ unstar_images_in_list: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["Body_unstar_images_in_list"];
+ };
+ };
responses: {
/** @description Successful Response */
200: {
@@ -36040,46 +39350,76 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageMoveStatusResponse"];
+ "application/json": components["schemas"]["UnstarredImagesResult"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- upload_image: {
+ download_images_from_list: {
parameters: {
- query: {
- /** @description The category of the image */
- image_category: components["schemas"]["ImageCategory"];
- /** @description Whether this is an intermediate image */
- is_intermediate: boolean;
- /** @description The board to add this image to, if any */
- board_id?: string | null;
- /** @description The session ID associated with this upload, if any */
- session_id?: string | null;
- /** @description Whether to crop the image */
- crop_visible?: boolean | null;
- };
+ query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody: {
+ requestBody?: {
content: {
- "multipart/form-data": components["schemas"]["Body_upload_image"];
+ "application/json": components["schemas"]["Body_download_images_from_list"];
};
};
responses: {
- /** @description The image was uploaded successfully */
- 201: {
+ /** @description Successful Response */
+ 202: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageDTO"];
+ "application/json": components["schemas"]["ImagesDownloaded"];
};
};
- /** @description Image upload failed */
- 415: {
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ get_bulk_download_item: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ /** @description The bulk_download_item_name of the bulk download item to get */
+ bulk_download_item_name: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Return the complete bulk download item */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/zip": unknown;
+ };
+ };
+ /** @description Image not found */
+ 404: {
headers: {
[name: string]: unknown;
};
@@ -36096,7 +39436,7 @@ export interface operations {
};
};
};
- list_image_dtos: {
+ get_image_names: {
parameters: {
query?: {
/** @description The origin of images to list. */
@@ -36107,10 +39447,6 @@ export interface operations {
is_intermediate?: boolean | null;
/** @description The board id to filter by. Use 'none' to find images without a board. */
board_id?: string | null;
- /** @description The page offset */
- offset?: number;
- /** @description The number of images per page */
- limit?: number;
/** @description The order of sort */
order_dir?: components["schemas"]["SQLiteDirection"];
/** @description Whether to sort by starred images first */
@@ -36130,7 +39466,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["OffsetPaginatedResults_ImageDTO_"];
+ "application/json": components["schemas"]["ImageNamesResult"];
};
};
/** @description Validation Error */
@@ -36144,7 +39480,7 @@ export interface operations {
};
};
};
- create_image_upload_entry: {
+ get_images_by_names: {
parameters: {
query?: never;
header?: never;
@@ -36153,7 +39489,7 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["Body_create_image_upload_entry"];
+ "application/json": components["schemas"]["Body_get_images_by_names"];
};
};
responses: {
@@ -36163,7 +39499,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageUploadEntry"];
+ "application/json": components["schemas"]["ImageDTO"][];
};
};
/** @description Validation Error */
@@ -36177,26 +39513,43 @@ export interface operations {
};
};
};
- get_image_dto: {
+ upload_video: {
parameters: {
- query?: never;
- header?: never;
- path: {
- /** @description The name of image to get */
- image_name: string;
+ query: {
+ /** @description The category of the video */
+ video_category: components["schemas"]["ImageCategory"];
+ /** @description Whether this is an intermediate video */
+ is_intermediate: boolean;
+ /** @description The board to add this video to, if any */
+ board_id?: string | null;
+ /** @description The session ID associated with this upload, if any */
+ session_id?: string | null;
};
+ header?: never;
+ path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "multipart/form-data": components["schemas"]["Body_upload_video"];
+ };
+ };
responses: {
- /** @description Successful Response */
- 200: {
+ /** @description The video was uploaded successfully */
+ 201: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageDTO"];
+ "application/json": components["schemas"]["VideoDTO"];
+ };
+ };
+ /** @description Video upload failed */
+ 415: {
+ headers: {
+ [name: string]: unknown;
};
+ content?: never;
};
/** @description Validation Error */
422: {
@@ -36209,13 +39562,13 @@ export interface operations {
};
};
};
- delete_image: {
+ get_video_dto: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of the image to delete */
- image_name: string;
+ /** @description The name of video to get */
+ video_name: string;
};
cookie?: never;
};
@@ -36227,7 +39580,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DeleteImagesResult"];
+ "application/json": components["schemas"]["VideoDTO"];
};
};
/** @description Validation Error */
@@ -36241,21 +39594,17 @@ export interface operations {
};
};
};
- update_image: {
+ delete_video: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of the image to update */
- image_name: string;
+ /** @description The name of the video to delete */
+ video_name: string;
};
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": components["schemas"]["ImageRecordChanges"];
- };
- };
+ requestBody?: never;
responses: {
/** @description Successful Response */
200: {
@@ -36263,7 +39612,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageDTO"];
+ "application/json": components["schemas"]["DeleteVideosResult"];
};
};
/** @description Validation Error */
@@ -36277,14 +39626,21 @@ export interface operations {
};
};
};
- get_intermediates_count: {
+ update_video: {
parameters: {
query?: never;
header?: never;
- path?: never;
+ path: {
+ /** @description The name of the video to update */
+ video_name: string;
+ };
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["VideoRecordChanges"];
+ };
+ };
responses: {
/** @description Successful Response */
200: {
@@ -36292,42 +39648,32 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": number;
+ "application/json": components["schemas"]["VideoDTO"];
};
};
- };
- };
- clear_intermediates: {
- parameters: {
- query?: never;
- header?: never;
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description Successful Response */
- 200: {
+ /** @description Validation Error */
+ 422: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": number;
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- get_image_metadata: {
+ delete_videos_from_list: {
parameters: {
query?: never;
header?: never;
- path: {
- /** @description The name of image to get */
- image_name: string;
- };
+ path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["Body_delete_videos_from_list"];
+ };
+ };
responses: {
/** @description Successful Response */
200: {
@@ -36335,7 +39681,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["MetadataField"] | null;
+ "application/json": components["schemas"]["DeleteVideosResult"];
};
};
/** @description Validation Error */
@@ -36349,13 +39695,13 @@ export interface operations {
};
};
};
- get_image_workflow: {
+ get_video_metadata: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of image whose workflow to get */
- image_name: string;
+ /** @description The name of video to get */
+ video_name: string;
};
cookie?: never;
};
@@ -36367,7 +39713,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["WorkflowAndGraphResponse"];
+ "application/json": components["schemas"]["MetadataField"] | null;
};
};
/** @description Validation Error */
@@ -36381,28 +39727,39 @@ export interface operations {
};
};
};
- get_image_full: {
+ get_video_full: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of full-resolution image file to get */
- image_name: string;
+ /** @description The name of video file to get */
+ video_name: string;
+ };
+ cookie?: {
+ invokeai_media_token?: string | null;
};
- cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Return the full-resolution image */
+ /** @description Return the full video file */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "image/png": unknown;
+ "video/mp4": unknown;
};
};
- /** @description Image not found */
+ /** @description Return a byte-range of the video file */
+ 206: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "video/mp4": unknown;
+ };
+ };
+ /** @description Video not found */
404: {
headers: {
[name: string]: unknown;
@@ -36420,28 +39777,30 @@ export interface operations {
};
};
};
- get_image_full_head: {
+ get_video_full_head: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of full-resolution image file to get */
- image_name: string;
+ /** @description The name of video file to get */
+ video_name: string;
+ };
+ cookie?: {
+ invokeai_media_token?: string | null;
};
- cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Return the full-resolution image */
+ /** @description Return the full video file */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "image/png": unknown;
+ "video/mp4": unknown;
};
};
- /** @description Image not found */
+ /** @description Video not found */
404: {
headers: {
[name: string]: unknown;
@@ -36459,19 +39818,21 @@ export interface operations {
};
};
};
- get_image_thumbnail: {
+ get_video_thumbnail: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of thumbnail image file to get */
- image_name: string;
+ /** @description The name of thumbnail file to get */
+ video_name: string;
+ };
+ cookie?: {
+ invokeai_media_token?: string | null;
};
- cookie?: never;
};
requestBody?: never;
responses: {
- /** @description Return the image thumbnail */
+ /** @description Return the video thumbnail */
200: {
headers: {
[name: string]: unknown;
@@ -36480,7 +39841,7 @@ export interface operations {
"image/webp": unknown;
};
};
- /** @description Image not found */
+ /** @description Video not found */
404: {
headers: {
[name: string]: unknown;
@@ -36498,13 +39859,13 @@ export interface operations {
};
};
};
- get_image_urls: {
+ get_video_urls: {
parameters: {
query?: never;
header?: never;
path: {
- /** @description The name of the image whose URL to get */
- image_name: string;
+ /** @description The name of the video whose URL to get */
+ video_name: string;
};
cookie?: never;
};
@@ -36516,7 +39877,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageUrlsDTO"];
+ "application/json": components["schemas"]["VideoUrlsDTO"];
};
};
/** @description Validation Error */
@@ -36530,18 +39891,33 @@ export interface operations {
};
};
};
- delete_images_from_list: {
+ list_video_dtos: {
parameters: {
- query?: never;
+ query?: {
+ /** @description The origin of videos to list. */
+ video_origin?: components["schemas"]["ResourceOrigin"] | null;
+ /** @description The categories of video to include. */
+ categories?: components["schemas"]["ImageCategory"][] | null;
+ /** @description Whether to list intermediate videos. */
+ is_intermediate?: boolean | null;
+ /** @description The board id to filter by. Use 'none' to find videos without a board. */
+ board_id?: string | null;
+ /** @description The page offset */
+ offset?: number;
+ /** @description The number of videos per page */
+ limit?: number;
+ /** @description The order of sort */
+ order_dir?: components["schemas"]["SQLiteDirection"];
+ /** @description Whether to sort by starred videos first */
+ starred_first?: boolean;
+ /** @description The term to search for */
+ search_term?: string | null;
+ };
header?: never;
path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": components["schemas"]["Body_delete_images_from_list"];
- };
- };
+ requestBody?: never;
responses: {
/** @description Successful Response */
200: {
@@ -36549,7 +39925,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DeleteImagesResult"];
+ "application/json": components["schemas"]["OffsetPaginatedResults_VideoDTO_"];
};
};
/** @description Validation Error */
@@ -36563,9 +39939,24 @@ export interface operations {
};
};
};
- delete_uncategorized_images: {
+ get_video_names: {
parameters: {
- query?: never;
+ query?: {
+ /** @description The origin of videos to list. */
+ video_origin?: components["schemas"]["ResourceOrigin"] | null;
+ /** @description The categories of video to include. */
+ categories?: components["schemas"]["ImageCategory"][] | null;
+ /** @description Whether to list intermediate videos. */
+ is_intermediate?: boolean | null;
+ /** @description The board id to filter by. Use 'none' to find videos without a board. */
+ board_id?: string | null;
+ /** @description The order of sort */
+ order_dir?: components["schemas"]["SQLiteDirection"];
+ /** @description Whether to sort by starred videos first */
+ starred_first?: boolean;
+ /** @description The term to search for */
+ search_term?: string | null;
+ };
header?: never;
path?: never;
cookie?: never;
@@ -36578,12 +39969,21 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["DeleteImagesResult"];
+ "application/json": components["schemas"]["VideoNamesResult"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
- star_images_in_list: {
+ star_videos_in_list: {
parameters: {
query?: never;
header?: never;
@@ -36592,7 +39992,7 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["Body_star_images_in_list"];
+ "application/json": components["schemas"]["Body_star_videos_in_list"];
};
};
responses: {
@@ -36602,7 +40002,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["StarredImagesResult"];
+ "application/json": components["schemas"]["StarredVideosResult"];
};
};
/** @description Validation Error */
@@ -36616,7 +40016,7 @@ export interface operations {
};
};
};
- unstar_images_in_list: {
+ unstar_videos_in_list: {
parameters: {
query?: never;
header?: never;
@@ -36625,7 +40025,7 @@ export interface operations {
};
requestBody: {
content: {
- "application/json": components["schemas"]["Body_unstar_images_in_list"];
+ "application/json": components["schemas"]["Body_unstar_videos_in_list"];
};
};
responses: {
@@ -36635,7 +40035,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["UnstarredImagesResult"];
+ "application/json": components["schemas"]["UnstarredVideosResult"];
};
};
/** @description Validation Error */
@@ -36649,26 +40049,26 @@ export interface operations {
};
};
};
- download_images_from_list: {
+ add_video_to_board: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
- requestBody?: {
+ requestBody: {
content: {
- "application/json": components["schemas"]["Body_download_images_from_list"];
+ "application/json": components["schemas"]["VideoBoardArg"];
};
};
responses: {
/** @description Successful Response */
- 202: {
+ 200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImagesDownloaded"];
+ "application/json": components["schemas"]["AddVideosToBoardResult"];
};
};
/** @description Validation Error */
@@ -36682,33 +40082,27 @@ export interface operations {
};
};
};
- get_bulk_download_item: {
+ remove_video_from_board: {
parameters: {
query?: never;
header?: never;
- path: {
- /** @description The bulk_download_item_name of the bulk download item to get */
- bulk_download_item_name: string;
- };
+ path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["Body_remove_video_from_board"];
+ };
+ };
responses: {
- /** @description Return the complete bulk download item */
+ /** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
- "application/zip": unknown;
- };
- };
- /** @description Image not found */
- 404: {
- headers: {
- [name: string]: unknown;
+ "application/json": components["schemas"]["RemoveVideosFromBoardResult"];
};
- content?: never;
};
/** @description Validation Error */
422: {
@@ -36721,20 +40115,24 @@ export interface operations {
};
};
};
- get_image_names: {
+ list_gallery_items: {
parameters: {
query?: {
- /** @description The origin of images to list. */
- image_origin?: components["schemas"]["ResourceOrigin"] | null;
- /** @description The categories of image to include. */
+ /** @description The origin of items to list. */
+ origin?: components["schemas"]["ResourceOrigin"] | null;
+ /** @description The categories to include. Shared between images and videos. */
categories?: components["schemas"]["ImageCategory"][] | null;
- /** @description Whether to list intermediate images. */
+ /** @description Whether to list intermediate items. */
is_intermediate?: boolean | null;
- /** @description The board id to filter by. Use 'none' to find images without a board. */
+ /** @description The board id to filter by. Use 'none' to find items without a board. */
board_id?: string | null;
+ /** @description The page offset */
+ offset?: number;
+ /** @description The number of items per page */
+ limit?: number;
/** @description The order of sort */
order_dir?: components["schemas"]["SQLiteDirection"];
- /** @description Whether to sort by starred images first */
+ /** @description Whether to sort by starred items first */
starred_first?: boolean;
/** @description The term to search for */
search_term?: string | null;
@@ -36751,7 +40149,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageNamesResult"];
+ "application/json": components["schemas"]["OffsetPaginatedResults_GalleryItem_"];
};
};
/** @description Validation Error */
@@ -36765,18 +40163,29 @@ export interface operations {
};
};
};
- get_images_by_names: {
+ get_gallery_item_names: {
parameters: {
- query?: never;
+ query?: {
+ /** @description The origin of items to list. */
+ origin?: components["schemas"]["ResourceOrigin"] | null;
+ /** @description The categories to include. Shared between images and videos. */
+ categories?: components["schemas"]["ImageCategory"][] | null;
+ /** @description Whether to list intermediate items. */
+ is_intermediate?: boolean | null;
+ /** @description The board id to filter by. Use 'none' to find items without a board. */
+ board_id?: string | null;
+ /** @description The order of sort */
+ order_dir?: components["schemas"]["SQLiteDirection"];
+ /** @description Whether to sort by starred items first */
+ starred_first?: boolean;
+ /** @description The term to search for */
+ search_term?: string | null;
+ };
header?: never;
path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": components["schemas"]["Body_get_images_by_names"];
- };
- };
+ requestBody?: never;
responses: {
/** @description Successful Response */
200: {
@@ -36784,7 +40193,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- "application/json": components["schemas"]["ImageDTO"][];
+ "application/json": components["schemas"]["GalleryItemNamesResult"];
};
};
/** @description Validation Error */
@@ -36907,7 +40316,7 @@ export interface operations {
delete_board: {
parameters: {
query?: {
- /** @description Permanently delete all images on the board */
+ /** @description Permanently delete all images and videos on the board */
include_images?: boolean | null;
};
header?: never;
@@ -37205,6 +40614,47 @@ export interface operations {
};
};
};
+ list_virtual_board_item_names_by_date: {
+ parameters: {
+ query?: {
+ /** @description Whether to sort starred items first */
+ starred_first?: boolean;
+ /** @description The sort direction */
+ order_dir?: components["schemas"]["SQLiteDirection"];
+ /** @description The categories of items to include */
+ categories?: components["schemas"]["ImageCategory"][] | null;
+ /** @description Search term to filter items */
+ search_term?: string | null;
+ };
+ header?: never;
+ path: {
+ /** @description The ISO date string, e.g. '2026-03-18' */
+ date: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["GalleryItemNamesResult"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
get_related_models: {
parameters: {
query?: never;
diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts
index 34d0470db45..1b2bd7ac960 100644
--- a/invokeai/frontend/web/src/services/api/types.ts
+++ b/invokeai/frontend/web/src/services/api/types.ts
@@ -101,6 +101,7 @@ type FLUX2ModelConfig = Extract;
export type LoRAModelConfig = Extract;
+type WanLoRAModelConfig = Extract;
export type VAEModelConfig = Extract;
export type ControlNetModelConfig = Extract;
type AnimaControlNetModelConfig = Extract;
@@ -118,6 +119,7 @@ export type T5EncoderBnbQuantizedLlmInt8bModelConfig = Extract<
>;
export type Qwen3EncoderModelConfig = Extract;
export type QwenVLEncoderModelConfig = Extract;
+export type WanT5EncoderModelConfig = Extract;
export type SpandrelImageToImageModelConfig = Extract;
export type CheckpointModelConfig = Extract;
export type CLIPVisionModelConfig = Extract;
@@ -322,6 +324,13 @@ export const isQwenImageVAEModelConfig = (
);
};
+export const isWanVAEModelConfig = (config: AnyModelConfig, excludeSubmodels?: boolean): config is VAEModelConfig => {
+ return (
+ (config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config))) &&
+ config.base === 'wan'
+ );
+};
+
export const isControlNetModelConfig = (config: AnyModelConfig): config is ControlNetModelConfig => {
return config.type === 'controlnet';
};
@@ -397,6 +406,10 @@ export const isQwenVLEncoderModelConfig = (config: AnyModelConfig): config is Qw
return config.type === 'qwen_vl_encoder';
};
+export const isWanT5EncoderModelConfig = (config: AnyModelConfig): config is WanT5EncoderModelConfig => {
+ return config.type === 'wan_t5_encoder';
+};
+
export const isCLIPEmbedModelConfigOrSubmodel = (
config: AnyModelConfig,
excludeSubmodels?: boolean
@@ -504,6 +517,23 @@ export const isQwenImageDiffusersMainModelConfig = (config: AnyModelConfig): con
return config.type === 'main' && config.base === 'qwen-image' && config.format === 'diffusers';
};
+export const isWanDiffusersMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => {
+ return config.type === 'main' && config.base === 'wan' && config.format === 'diffusers';
+};
+
+/** Wan GGUF main models marked as the low-noise expert (the second half
+ * of the A14B MoE pair). Suitable for the Transformer (Low Noise) picker;
+ * also used to filter low-noise GGUFs out of the primary main dropdown. */
+export const isWanGGUFLowNoiseMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => {
+ return (
+ config.type === 'main' && config.base === 'wan' && config.format === 'gguf_quantized' && config.expert === 'low'
+ );
+};
+
+export const isWanLoRAModelConfig = (config: AnyModelConfig): config is WanLoRAModelConfig => {
+ return config.type === 'lora' && config.base === 'wan';
+};
+
export const isTIModelConfig = (config: AnyModelConfig): config is MainModelConfig => {
return config.type === 'embedding';
};
@@ -630,3 +660,51 @@ export type UploadImageArg = {
export type ImageUploadEntryResponse = S['ImageUploadEntry'];
export type ImageUploadEntryRequest = paths['/api/v1/images/']['post']['requestBody']['content']['application/json'];
+
+// Videos
+export type VideoDTO = S['VideoDTO'];
+/** @knipignore Used by Phase 4+ video gallery mutations. */
+export type VideoRecordChanges = S['VideoRecordChanges'];
+export type OffsetPaginatedResults_VideoDTO_ = S['OffsetPaginatedResults_VideoDTO_'];
+export type ListVideosArgs = NonNullable;
+export type ListVideosResponse = paths['/api/v1/videos/']['get']['responses']['200']['content']['application/json'];
+export type GetVideoNamesArgs = NonNullable;
+export type GetVideoNamesResult =
+ paths['/api/v1/videos/names']['get']['responses']['200']['content']['application/json'];
+
+export type UploadVideoArg = {
+ /** The MP4 (or other accepted video) file to upload. */
+ file: File;
+ /** The category of video to upload. Reuses the image category enum. */
+ video_category: ImageCategory;
+ /** Whether the uploaded video is an intermediate (intermediates are not shown in the gallery). */
+ is_intermediate: boolean;
+ /** The session with which to associate the uploaded video, if any. */
+ session_id?: string;
+ /** The board to add the video to, if any. */
+ board_id?: string;
+ /** Metadata JSON to attach to the video record. */
+ metadata?: JsonObject;
+ /** Suppress the upload toast / gallery navigation side effects. */
+ silent?: boolean;
+ /** Whether this is the first upload of a batch (used by toast logic). */
+ isFirstUploadOfBatch?: boolean;
+};
+
+// Polymorphic gallery items (images + videos). Consumed by the gallery wiring in Phase 4.
+/** @knipignore Consumed by gallery wiring in Phase 4. */
+export type GalleryItem = S['GalleryItem'];
+/** @knipignore Consumed by gallery wiring in Phase 4. */
+export type GalleryItemKind = S['GalleryItemKind'];
+/** @knipignore Consumed by gallery wiring in Phase 4. */
+export type GalleryItemRef = S['GalleryItemRef'];
+/** @knipignore Consumed by gallery wiring in Phase 4. */
+export type GalleryItemNamesResult = S['GalleryItemNamesResult'];
+/** @knipignore Consumed by gallery wiring in Phase 4. */
+export type OffsetPaginatedResults_GalleryItem_ = S['OffsetPaginatedResults_GalleryItem_'];
+export type ListGalleryItemsArgs = NonNullable;
+export type ListGalleryItemsResponse =
+ paths['/api/v1/gallery/items/']['get']['responses']['200']['content']['application/json'];
+export type GetGalleryItemNamesArgs = NonNullable;
+export type GetGalleryItemNamesResult =
+ paths['/api/v1/gallery/items/names']['get']['responses']['200']['content']['application/json'];
diff --git a/invokeai/frontend/web/src/services/api/util.ts b/invokeai/frontend/web/src/services/api/util.ts
index 3d92923f2c5..0f2a03407e0 100644
--- a/invokeai/frontend/web/src/services/api/util.ts
+++ b/invokeai/frontend/web/src/services/api/util.ts
@@ -2,7 +2,7 @@ import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/type
import queryString from 'query-string';
import { buildV1Url } from 'services/api';
-import type { ImageDTO, ListImagesArgs } from './types';
+import type { ImageDTO, ListGalleryItemsArgs, ListImagesArgs, ListVideosArgs } from './types';
export const getCategories = (imageDTO: ImageDTO) => {
if (IMAGE_CATEGORIES.includes(imageDTO.image_category)) {
@@ -14,3 +14,11 @@ export const getCategories = (imageDTO: ImageDTO) => {
// Helper to create the url for the listImages endpoint. Also we use it to create the cache key.
export const getListImagesUrl = (queryArgs: ListImagesArgs) =>
buildV1Url(`images/?${queryString.stringify(queryArgs, { arrayFormat: 'none' })}`);
+
+// Helper to create the url for the listVideos endpoint. Also we use it to create the cache key.
+export const getListVideosUrl = (queryArgs: ListVideosArgs) =>
+ buildV1Url(`videos/?${queryString.stringify(queryArgs, { arrayFormat: 'none' })}`);
+
+// Helper to create the url for the polymorphic listGalleryItems endpoint.
+export const getListGalleryItemsUrl = (queryArgs: ListGalleryItemsArgs) =>
+ buildV1Url(`gallery/items/?${queryString.stringify(queryArgs, { arrayFormat: 'none' })}`);
diff --git a/invokeai/frontend/web/src/services/api/util/tagInvalidation.ts b/invokeai/frontend/web/src/services/api/util/tagInvalidation.ts
index bac3130d312..bd040fe43b8 100644
--- a/invokeai/frontend/web/src/services/api/util/tagInvalidation.ts
+++ b/invokeai/frontend/web/src/services/api/util/tagInvalidation.ts
@@ -4,7 +4,16 @@ import { getListImagesUrl } from 'services/api/util';
import type { ApiTagDescription } from '..';
export const getTagsToInvalidateForBoardAffectingMutation = (affected_boards: string[]): ApiTagDescription[] => {
- const tags: ApiTagDescription[] = ['ImageNameList', 'VirtualBoards'];
+ // Whenever an image or video mutation changes a board's contents we also have to refresh
+ // the polymorphic gallery list (and its names companion) since that is what the gallery UI
+ // actually subscribes to once Phase 4 lands.
+ const tags: ApiTagDescription[] = [
+ 'ImageNameList',
+ 'VirtualBoards',
+ 'VideoNameList',
+ 'GalleryItemList',
+ 'GalleryItemNameList',
+ ];
for (const board_id of affected_boards) {
tags.push({
@@ -32,6 +41,22 @@ export const getTagsToInvalidateForBoardAffectingMutation = (affected_boards: st
type: 'BoardImagesTotal',
id: board_id,
});
+
+ tags.push({
+ type: 'BoardVideosTotal',
+ id: board_id,
+ });
+ }
+
+ return tags;
+};
+
+export const getTagsToInvalidateForVideoMutation = (video_names: string[]): ApiTagDescription[] => {
+ const tags: ApiTagDescription[] = [];
+
+ for (const video_name of video_names) {
+ tags.push({ type: 'Video', id: video_name });
+ tags.push({ type: 'VideoMetadata', id: video_name });
}
return tags;
diff --git a/invokeai/frontend/web/src/services/events/onInvocationComplete.test.ts b/invokeai/frontend/web/src/services/events/onInvocationComplete.test.ts
new file mode 100644
index 00000000000..6b1027b4ad5
--- /dev/null
+++ b/invokeai/frontend/web/src/services/events/onInvocationComplete.test.ts
@@ -0,0 +1,286 @@
+/**
+ * Regression test for the polymorphic gallery cache invalidation in
+ * ``addImagesToGallery``.
+ *
+ * The bug: ``onInvocationComplete`` only updated the image-only
+ * ``getImageNames`` RTK Query cache via an optimistic insert, but the gallery
+ * grid actually reads from the polymorphic ``getGalleryItemNames`` cache. So a
+ * freshly-generated image never appeared until the user reloaded the browser,
+ * even though it landed in board totals and the per-DTO cache correctly.
+ *
+ * The fix is a single line that dispatches
+ * ``galleryApi.util.invalidateTags(['GalleryItemNameList', 'GalleryItemList'])``
+ * after image outputs are processed. This test pins that behavior so a future
+ * refactor of the complete handler doesn't silently drop the invalidation.
+ */
+import type { S } from 'services/api/types';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+// Mock the modules that have heavy side effects on import or do real network work
+// when their selectors fire. The mocks return shape-compatible no-ops; we only care
+// about the dispatch trace.
+vi.mock('services/api/endpoints/images', () => ({
+ imagesApi: {
+ util: {
+ updateQueryData: vi.fn(() => ({ type: 'mock/imagesApi/updateQueryData' })),
+ invalidateTags: vi.fn((tags: unknown[]) => ({ type: 'imagesApi/invalidateTags', payload: tags })),
+ },
+ endpoints: {
+ getImageNames: { select: vi.fn(() => () => ({ data: { image_names: [] } })) },
+ },
+ },
+ getImageDTOSafe: vi.fn((image_name: string) =>
+ Promise.resolve({
+ image_name,
+ image_url: `mock://${image_name}`,
+ thumbnail_url: `mock://thumb/${image_name}`,
+ width: 1024,
+ height: 1024,
+ is_intermediate: false,
+ is_starred: false,
+ image_category: 'general',
+ image_origin: 'internal',
+ has_workflow: false,
+ board_id: null,
+ created_at: '2026-01-01',
+ updated_at: '2026-01-01',
+ session_id: 'test-session',
+ node_id: 'test-node',
+ })
+ ),
+}));
+
+vi.mock('services/api/endpoints/boards', () => ({
+ boardsApi: {
+ util: {
+ upsertQueryEntries: vi.fn(() => ({ type: 'mock/boardsApi/upsertQueryEntries' })),
+ updateQueryData: vi.fn(() => ({ type: 'mock/boardsApi/updateQueryData' })),
+ },
+ endpoints: {
+ getBoardImagesTotal: { select: vi.fn(() => () => ({ data: undefined })) },
+ },
+ },
+}));
+
+vi.mock('services/api/endpoints/queue', () => ({
+ queueApi: {
+ util: {
+ invalidateTags: vi.fn((tags: unknown[]) => ({ type: 'queueApi/invalidateTags', payload: tags })),
+ },
+ },
+}));
+
+vi.mock('services/api/endpoints/videos', () => ({
+ getVideoDTOSafe: vi.fn(() => Promise.resolve(null)),
+}));
+
+vi.mock('features/gallery/store/gallerySelectors', () => ({
+ selectAutoSwitch: vi.fn(() => false),
+ selectGalleryView: vi.fn(() => 'images'),
+ selectGetImageNamesQueryArgs: vi.fn(() => ({
+ board_id: 'none',
+ categories: ['general'],
+ search_term: '',
+ order_dir: 'DESC',
+ starred_first: true,
+ is_intermediate: false,
+ })),
+ selectListBoardsQueryArgs: vi.fn(() => ({
+ order_by: 'created_at',
+ direction: 'DESC',
+ })),
+ selectSelectedBoardId: vi.fn(() => 'none'),
+}));
+
+vi.mock('features/gallery/store/gallerySlice', () => ({
+ boardIdSelected: vi.fn(() => ({ type: 'mock/boardIdSelected' })),
+ galleryViewChanged: vi.fn(() => ({ type: 'mock/galleryViewChanged' })),
+ imageSelected: vi.fn(() => ({ type: 'mock/imageSelected' })),
+}));
+
+vi.mock('features/controlLayers/store/canvasWorkflowIntegrationSlice', () => ({
+ canvasWorkflowIntegrationProcessingCompleted: vi.fn(() => ({ type: 'mock/canvasComplete' })),
+}));
+
+vi.mock('features/nodes/hooks/useNodeExecutionState', () => ({
+ $nodeExecutionStates: { get: vi.fn(() => ({})) },
+ upsertExecutionState: vi.fn(),
+}));
+
+vi.mock('services/events/nodeExecutionState', () => ({
+ getUpdatedNodeExecutionStateOnInvocationComplete: vi.fn(() => null),
+}));
+
+vi.mock('services/events/stores', () => ({
+ $lastProgressEvent: { set: vi.fn() },
+}));
+
+// Import AFTER the mocks above are declared (vi.mock is hoisted; explicit ordering here
+// is for the human reader).
+import { getVideoDTOSafe } from 'services/api/endpoints/videos';
+
+import { buildOnInvocationComplete } from './onInvocationComplete';
+
+// Build a synthetic InvocationCompleteEvent whose result contains a single ImageField output.
+// The runtime ``isImageField`` discriminator matches on ``type === 'image_output'``.
+const buildImageCompleteEvent = (): S['InvocationCompleteEvent'] =>
+ ({
+ queue_id: 'default',
+ item_id: 1,
+ batch_id: 'batch-1',
+ origin: 'workflows',
+ destination: 'gallery',
+ user_id: 'user-1',
+ session_id: 'session-1',
+ invocation_source_id: 'node-1',
+ invocation: {
+ id: 'prepared-node-1',
+ // Not in nodeTypeDenylist (which contains 'load_image', 'image') — so the handler
+ // will proceed to extract image DTOs.
+ type: 'add',
+ },
+ // ``result`` is the node's OutputType serialized as a flat key→value map.
+ // ``isImageField`` accepts any object with a non-empty ``image_name`` string,
+ // which is what the ``image`` output field unwraps to.
+ result: {
+ image: { image_name: 'fresh-image.png' },
+ width: 1024,
+ height: 1024,
+ },
+ }) as unknown as S['InvocationCompleteEvent'];
+
+describe('onInvocationComplete polymorphic gallery cache', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('invalidates GalleryItemNameList + GalleryItemList when an image output completes', async () => {
+ const dispatched: unknown[] = [];
+ const dispatch = vi.fn((action: unknown) => {
+ dispatched.push(action);
+ // RTK Query thunks return unsubscribe promises; the handler does not chain on the
+ // return value of the invalidate dispatch, so we can synchronously return a stub.
+ return { unwrap: () => Promise.resolve(undefined) };
+ });
+ const getState = vi.fn(() => ({}));
+
+ const handler = buildOnInvocationComplete(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ getState as any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ dispatch as any,
+ new Map()
+ );
+
+ await handler(buildImageCompleteEvent());
+
+ // The handler emits many actions; the regression-critical one is the polymorphic
+ // gallery tag invalidation. We identify it by its payload — the real
+ // ``galleryApi.util.invalidateTags`` produces an action with this exact payload.
+ const galleryInvalidation = dispatched.find((action): action is { type: string; payload: string[] } => {
+ if (!action || typeof action !== 'object') {
+ return false;
+ }
+ const payload = (action as { payload?: unknown }).payload;
+ if (!Array.isArray(payload)) {
+ return false;
+ }
+ return payload.includes('GalleryItemNameList') && payload.includes('GalleryItemList');
+ });
+
+ expect(galleryInvalidation, 'addImagesToGallery must invalidate the polymorphic gallery cache').toBeDefined();
+ });
+
+ it('does not invalidate the polymorphic gallery cache for denylisted node types', async () => {
+ const dispatched: unknown[] = [];
+ const dispatch = vi.fn((action: unknown) => {
+ dispatched.push(action);
+ return { unwrap: () => Promise.resolve(undefined) };
+ });
+ const getState = vi.fn(() => ({}));
+
+ const handler = buildOnInvocationComplete(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ getState as any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ dispatch as any,
+ new Map()
+ );
+
+ // ``image`` is in the nodeTypeDenylist (passthrough node — doesn't add to gallery).
+ const denylisted = buildImageCompleteEvent();
+ denylisted.invocation.type = 'image';
+
+ await handler(denylisted);
+
+ const galleryInvalidation = dispatched.find((action): action is { type: string; payload: string[] } => {
+ if (!action || typeof action !== 'object') {
+ return false;
+ }
+ const payload = (action as { payload?: unknown }).payload;
+ return Array.isArray(payload) && payload.includes('GalleryItemNameList');
+ });
+ expect(galleryInvalidation, 'denylisted passthrough nodes must not trigger a gallery refetch').toBeUndefined();
+ });
+
+ it('invalidates board tags/totals in addition to the gallery cache when a video output completes', async () => {
+ // A generated video landing on a board must also refresh that board's DTO (video_count,
+ // cover_video_name via the ``Board`` tag), its ``BoardVideosTotal``, and the virtual board
+ // groupings — otherwise the boards list shows stale counts/covers until some unrelated
+ // mutation happens to refetch them.
+ vi.mocked(getVideoDTOSafe).mockResolvedValueOnce({
+ video_name: 'fresh-video.mp4',
+ video_url: 'mock://fresh-video.mp4',
+ thumbnail_url: 'mock://thumb/fresh-video.mp4',
+ width: 832,
+ height: 480,
+ duration_seconds: 3.4,
+ frame_count: 81,
+ is_intermediate: false,
+ is_starred: false,
+ board_id: 'board-123',
+ created_at: '2026-01-01',
+ updated_at: '2026-01-01',
+ session_id: 'test-session',
+ node_id: 'test-node',
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } as any);
+
+ const dispatched: unknown[] = [];
+ const dispatch = vi.fn((action: unknown) => {
+ dispatched.push(action);
+ return { unwrap: () => Promise.resolve(undefined) };
+ });
+ const getState = vi.fn(() => ({}));
+
+ const handler = buildOnInvocationComplete(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ getState as any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ dispatch as any,
+ new Map()
+ );
+
+ const videoEvent = buildImageCompleteEvent();
+ videoEvent.invocation.type = 'wan_l2v';
+ // ``isVideoField`` accepts any object with a non-empty ``video_name`` string.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (videoEvent as any).result = { video: { video_name: 'fresh-video.mp4' } };
+
+ await handler(videoEvent);
+
+ const galleryInvalidation = dispatched.find((action): action is { type: string; payload: unknown[] } => {
+ if (!action || typeof action !== 'object') {
+ return false;
+ }
+ const payload = (action as { payload?: unknown }).payload;
+ return Array.isArray(payload) && payload.includes('GalleryItemNameList');
+ });
+
+ expect(galleryInvalidation, 'video completion must invalidate the polymorphic gallery cache').toBeDefined();
+ // The same invalidation must cover the board caches for the video's board.
+ expect(galleryInvalidation?.payload).toContainEqual({ type: 'Board', id: 'board-123' });
+ expect(galleryInvalidation?.payload).toContainEqual({ type: 'BoardVideosTotal', id: 'board-123' });
+ expect(galleryInvalidation?.payload).toContain('VirtualBoards');
+ });
+});
diff --git a/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx b/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx
index ea6a237d4b2..f27e8d92433 100644
--- a/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx
+++ b/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx
@@ -10,14 +10,17 @@ import {
} from 'features/gallery/store/gallerySelectors';
import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gallery/store/gallerySlice';
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useNodeExecutionState';
-import { isImageField, isImageFieldCollection } from 'features/nodes/types/common';
+import { isImageField, isImageFieldCollection, isVideoField } from 'features/nodes/types/common';
import { LIST_ALL_TAG } from 'services/api';
import { boardsApi } from 'services/api/endpoints/boards';
+import { galleryApi } from 'services/api/endpoints/gallery';
import { getImageDTOSafe, imagesApi } from 'services/api/endpoints/images';
import { queueApi } from 'services/api/endpoints/queue';
-import type { ImageDTO, S } from 'services/api/types';
+import { getVideoDTOSafe } from 'services/api/endpoints/videos';
+import type { ImageDTO, S, VideoDTO } from 'services/api/types';
import { getCategories } from 'services/api/util';
import { insertImageIntoNamesResult } from 'services/api/util/optimisticUpdates';
+import { getTagsToInvalidateForBoardAffectingMutation } from 'services/api/util/tagInvalidation';
import { getUpdatedNodeExecutionStateOnInvocationComplete } from 'services/events/nodeExecutionState';
import { $lastProgressEvent } from 'services/events/stores';
import stableHash from 'stable-hash';
@@ -160,6 +163,17 @@ export const buildOnInvocationComplete = (
dispatch(imagesApi.util.invalidateTags(['VirtualBoards']));
}
+ // The optimistic updates above only touch the image-only ``getImageNames`` cache. The
+ // gallery grid actually subscribes to the polymorphic ``getGalleryItemNames`` endpoint
+ // (which interleaves images and videos by created_at), so without invalidating its tag
+ // a freshly generated image never appears in the grid until the user reloads — even
+ // though it lands correctly in board totals, image DTO cache, etc. Mirrors the same
+ // invalidation in addVideosToGallery below. A future optimization could insert into the
+ // polymorphic cache shape directly, but the refetch cost is a single HTTP round-trip.
+ if (imageDTOs.length > 0) {
+ dispatch(galleryApi.util.invalidateTags(['GalleryItemNameList', 'GalleryItemList']));
+ }
+
const autoSwitch = selectAutoSwitch(getState());
if (!autoSwitch) {
@@ -223,6 +237,92 @@ export const buildOnInvocationComplete = (
return imageDTOs;
};
+ const getResultVideoDTOs = async (data: S['InvocationCompleteEvent']): Promise => {
+ const { result } = data;
+ const videoDTOs: VideoDTO[] = [];
+ for (const [_name, value] of objectEntries(result)) {
+ if (isVideoField(value)) {
+ const videoDTO = await getVideoDTOSafe(value.video_name);
+ if (videoDTO) {
+ videoDTOs.push(videoDTO);
+ }
+ }
+ }
+ return videoDTOs;
+ };
+
+ // Counterpart to addImagesToGallery for VideoField outputs (e.g. Wan 2.2 latents-to-video).
+ // Two key differences from the image path:
+ // 1. The gallery view uses the polymorphic getGalleryItemNames endpoint and we have no
+ // cheap optimistic-insert here, so we invalidate the GalleryItemNameList/GalleryItemList
+ // tags to force a refetch.
+ // 2. The ImageViewerContext's local $progressEvent/$progressImage atoms expect onLoadImage
+ // (DndImage onLoad) to clear them. When auto-switching to a video, the viewer swaps
+ // CurrentImagePreview for CurrentVideoPreview, which unmounts the stale progress overlay
+ // so the stuck "Saving video" spinner goes away on its own.
+ const addVideosToGallery = async (data: S['InvocationCompleteEvent']) => {
+ if (nodeTypeDenylist.includes(data.invocation.type)) {
+ return;
+ }
+
+ const videoDTOs = await getResultVideoDTOs(data);
+ if (videoDTOs.length === 0) {
+ return;
+ }
+
+ const nonIntermediate = videoDTOs.filter((v) => !v.is_intermediate);
+ if (nonIntermediate.length === 0) {
+ return;
+ }
+
+ // Force the polymorphic gallery list to refetch so the new video shows up. Note: this is
+ // a tag invalidation, not an optimistic insert (the image path has a `insertImageIntoNamesResult`
+ // helper, but the polymorphic `GetGalleryItemNamesResult` has a different shape and we don't
+ // have an equivalent yet). The viewer selection below applies immediately, so the user sees
+ // their video right away; the *gallery grid* scroll-to-selection is delayed by one refetch
+ // because `useKeepSelectedImageInView` re-runs when `imageNames` updates and only then can
+ // it find the new name in the list. Worth a follow-up if the scroll lag becomes noticeable.
+ //
+ // The board-affecting helper also invalidates each board's `Board` tag (listAllBoards →
+ // video_count and cover_video_name), `BoardVideosTotal`, and `VirtualBoards`, so board
+ // counts and cover thumbnails don't lag behind the gallery grid until the next mutation.
+ const affectedBoards = [...new Set(nonIntermediate.map((v) => v.board_id ?? 'none'))];
+ dispatch(galleryApi.util.invalidateTags(getTagsToInvalidateForBoardAffectingMutation(affectedBoards)));
+
+ const autoSwitch = selectAutoSwitch(getState());
+ if (!autoSwitch) {
+ return;
+ }
+
+ const lastVideoDTO = nonIntermediate.at(-1);
+ if (!lastVideoDTO) {
+ return;
+ }
+
+ const { video_name } = lastVideoDTO;
+ const board_id = lastVideoDTO.board_id ?? 'none';
+
+ // Selection is a polymorphic string[]; useGalleryItemDTO discriminates by filename extension.
+ const selectedBoardId = selectSelectedBoardId(getState());
+ if (board_id !== selectedBoardId) {
+ dispatch(
+ boardIdSelected({
+ boardId: board_id,
+ select: {
+ selection: [video_name],
+ galleryView: 'images',
+ },
+ })
+ );
+ } else {
+ const galleryView = selectGalleryView(getState());
+ if (galleryView !== 'images') {
+ dispatch(galleryViewChanged('images'));
+ }
+ dispatch(imageSelected(video_name));
+ }
+ };
+
const clearCanvasWorkflowIntegrationProcessing = (data: S['InvocationCompleteEvent']) => {
// Check if this is a canvas workflow integration result
// Results go to staging area automatically via destination = canvasSessionId
@@ -270,6 +370,7 @@ export const buildOnInvocationComplete = (
// Add images to gallery (canvas workflow integration results go to staging area automatically)
await addImagesToGallery(data);
+ await addVideosToGallery(data);
$lastProgressEvent.set(null);
};
diff --git a/pyproject.toml b/pyproject.toml
index de665d621e3..cb1c35f058a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -43,6 +43,8 @@ dependencies = [
"onnx==1.16.1",
"onnxruntime==1.19.2",
"opencv-contrib-python",
+ "imageio[ffmpeg]", # video encode (for Wan 2.2 T2V/I2V output)
+ "psutil", # video decode worker process-tree termination
"safetensors",
"sentencepiece==0.2.0", # 0.2.1 coredumps windows when loading t5 tokenizer
"spandrel",
diff --git a/scripts/generate_openapi_schema.py b/scripts/generate_openapi_schema.py
index 70baa194dc1..4f59f1897b3 100644
--- a/scripts/generate_openapi_schema.py
+++ b/scripts/generate_openapi_schema.py
@@ -5,7 +5,16 @@
def main():
# Change working directory to the repo root
- os.chdir(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
+ repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+ os.chdir(repo_root)
+
+ # When invoked as a script, sys.path[0] is this script's directory rather than the repo
+ # root, so ``import invokeai`` would resolve via the venv install — which on systems with
+ # multi-worktree editable installs can pick up an `invokeai` namespace package missing
+ # some of this worktree's invocation modules. Prepending the repo root ensures we always
+ # import the local sources and so register every invocation declared here.
+ if sys.path[0] != repo_root:
+ sys.path.insert(0, repo_root)
from invokeai.app.api_app import app
from invokeai.app.util.custom_openapi import get_openapi_func
diff --git a/scripts/wan_diffusers_reference.py b/scripts/wan_diffusers_reference.py
new file mode 100644
index 00000000000..0e67ceac225
--- /dev/null
+++ b/scripts/wan_diffusers_reference.py
@@ -0,0 +1,85 @@
+"""Run TI2V-5B (or any Wan 2.2 Diffusers checkpoint) via the upstream
+WanPipeline directly, with the same arguments InvokeAI's wan_denoise uses.
+
+Use to A/B against InvokeAI output when image quality is questionable.
+Generates one image and saves it next to this script.
+
+Example:
+ python scripts/wan_diffusers_reference.py \
+ --model-path /home/lstein/invokeai-delete/models/ \
+ --prompt "a photograph of a young redheaded woman sitting on a three-legged stool next to a potted fern" \
+ --seed 42 --steps 40 --cfg 4.0 --width 1024 --height 1024
+"""
+
+import argparse
+from pathlib import Path
+
+import torch
+from diffusers import WanPipeline
+
+
+def main() -> None:
+ p = argparse.ArgumentParser()
+ p.add_argument("--model-path", required=True, help="Path to a Diffusers Wan model directory.")
+ p.add_argument("--prompt", required=True)
+ p.add_argument(
+ "--negative",
+ default="",
+ help="Negative prompt (default empty string — matches WanPipeline.encode_prompt behaviour).",
+ )
+ p.add_argument("--seed", type=int, default=42)
+ p.add_argument("--steps", type=int, default=40)
+ p.add_argument("--cfg", type=float, default=4.0)
+ p.add_argument("--width", type=int, default=1024)
+ p.add_argument("--height", type=int, default=1024)
+ p.add_argument("--output", default="wan_diffusers_reference.png")
+ p.add_argument(
+ "--offload",
+ choices=["model", "sequential", "none"],
+ default="model",
+ help="VRAM-saving strategy. 'model' (default) keeps one component on GPU at a time — fits TI2V-5B "
+ "in ~16 GB. 'sequential' is even more aggressive (per-module offload) and slower. "
+ "'none' loads everything to GPU at once (~24 GB+).",
+ )
+ args = p.parse_args()
+
+ print(f"Loading WanPipeline from {args.model_path} ...")
+ pipe = WanPipeline.from_pretrained(args.model_path, torch_dtype=torch.bfloat16)
+
+ if args.offload == "model":
+ # enable_model_cpu_offload puts each component (transformer, vae, text_encoder)
+ # on GPU only while it's actively running; the rest sit on CPU. Adds a little
+ # latency between stages but cuts peak VRAM dramatically.
+ pipe.enable_model_cpu_offload()
+ elif args.offload == "sequential":
+ pipe.enable_sequential_cpu_offload()
+ else:
+ pipe.to("cuda")
+
+ generator = torch.Generator(device="cuda").manual_seed(args.seed)
+
+ print(
+ f"Generating: prompt={args.prompt!r}\n"
+ f" steps={args.steps}, cfg={args.cfg}, size={args.width}x{args.height}, seed={args.seed}"
+ )
+ # num_frames=1 → image generation
+ result = pipe(
+ prompt=args.prompt,
+ negative_prompt=args.negative,
+ height=args.height,
+ width=args.width,
+ num_frames=1,
+ num_inference_steps=args.steps,
+ guidance_scale=args.cfg,
+ generator=generator,
+ output_type="pil",
+ )
+ # WanPipelineOutput.frames is a list of [PIL.Image] sequences (one per video).
+ image = result.frames[0][0]
+ out = Path(args.output)
+ image.save(out)
+ print(f"Saved {out.resolve()}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/app/invocations/test_video_concat.py b/tests/app/invocations/test_video_concat.py
new file mode 100644
index 00000000000..861a90dace5
--- /dev/null
+++ b/tests/app/invocations/test_video_concat.py
@@ -0,0 +1,190 @@
+"""Regression tests for VideoConcatInvocation._iter_joined_frames.
+
+Covers two JPPhoto code-review findings (PR #9163):
+
+1. ``fade_through_black`` claimed to emit ``transition_frames`` frames per boundary but
+ used a symmetric ``tf // 2`` split, dropping one frame for odd ``tf``. The fix splits
+ asymmetrically: ``tail_half = tf // 2`` consumed from the previous clip's tail,
+ ``head_half = tf - tail_half`` from the next clip's head, so the emitted total is
+ exactly ``tf`` for both parities.
+
+2. The node fully decoded every input into RAM before encoding, so a long upload (the
+ API admits 1 GB compressed) could expand to tens of gigabytes of frames. Joining is
+ now a generator that pulls frames lazily and buffers only the transition windows —
+ the streaming tests pin that output begins before the inputs are exhausted and that
+ look-ahead stays bounded by ``transition_frames``.
+
+We exercise ``_iter_joined_frames`` directly because it is the pure transformation
+implementing the contract (the surrounding ``invoke()`` deals with imageio
+encode/decode plumbing that isn't germane to the math).
+"""
+
+from typing import Iterator
+
+import numpy as np
+import pytest
+
+from invokeai.app.invocations.fields import VideoField
+from invokeai.app.invocations.video_concat import MAX_TRANSITION_MEMORY_BYTES, VideoConcatInvocation, _crossfade
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+
+
+def _invocation(transition: str, transition_frames: int) -> VideoConcatInvocation:
+ # ``videos`` requires min_length=2 to construct; values are unused by ``_iter_joined_frames``.
+ return VideoConcatInvocation(
+ videos=[VideoField(video_name="a"), VideoField(video_name="b")],
+ transition=transition, # type: ignore[arg-type]
+ transition_frames=transition_frames,
+ )
+
+
+def _clip(value: int, n: int) -> list[np.ndarray]:
+ return [np.full((4, 4, 3), value, dtype=np.uint8) for _ in range(n)]
+
+
+class TestFadeThroughBlackOddTf:
+ """fade_through_black must emit exactly tf frames per boundary, even for odd tf."""
+
+ @pytest.mark.parametrize("tf", [1, 2, 3, 4, 5, 7, 8])
+ def test_total_length_preserved(self, tf: int) -> None:
+ clip_a = _clip(200, 10)
+ clip_b = _clip(100, 10)
+ v = _invocation("fade_through_black", tf)
+ out = list(v._iter_joined_frames([clip_a, clip_b]))
+ # fade_through_black: each boundary consumes tf frames (tail_half from A + head_half from B)
+ # and emits exactly tf frames in their place, so the total length is preserved.
+ assert len(out) == len(clip_a) + len(clip_b)
+
+ def test_three_clip_chain(self) -> None:
+ clip_a = _clip(200, 10)
+ clip_b = _clip(150, 10)
+ clip_c = _clip(100, 10)
+ v = _invocation("fade_through_black", 3)
+ out = list(v._iter_joined_frames([clip_a, clip_b, clip_c]))
+ # 30 input frames - 2 boundaries each consuming 3 + emitting 3 = 30 output frames.
+ assert len(out) == 30
+
+
+class TestFadeThroughBlackTransitionFrames:
+ """Validation should accept odd tf when both halves fit within their adjacent clips."""
+
+ def test_odd_tf_validation_uses_correct_halves(self) -> None:
+ # tf=5 → tail_half=2, head_half=3. A 5-frame clip would fail with the previous
+ # symmetric split (2+2 ≤ 5 was fine, but the math now needs head_half=3 from
+ # clip[1] head + tail_half=2 from clip[1] tail = 5 ≤ 5 → still fits).
+ clip_a = _clip(200, 5)
+ clip_b = _clip(100, 5)
+ clip_c = _clip(50, 5)
+ v = _invocation("fade_through_black", 5)
+ out = list(v._iter_joined_frames([clip_a, clip_b, clip_c]))
+ assert len(out) == 15
+
+
+class TestCrossfadeTransitionLength:
+ """Crossfade behaviour is unchanged; pin its length for safety."""
+
+ def test_crossfade_shortens_by_tf_per_boundary(self) -> None:
+ clip_a = _clip(200, 10)
+ clip_b = _clip(100, 10)
+ v = _invocation("crossfade", 5)
+ out = list(v._iter_joined_frames([clip_a, clip_b]))
+ # 20 input - 5 frames consumed from each side (10 total) + 5 blended emitted = 15.
+ assert len(out) == 15
+
+
+class TestCutNoTransitionFrames:
+ """transition='cut' or tf=0 returns the raw concatenation."""
+
+ def test_cut_concatenates_directly(self) -> None:
+ clip_a = _clip(200, 4)
+ clip_b = _clip(100, 6)
+ v = _invocation("cut", 0)
+ out = list(v._iter_joined_frames([clip_a, clip_b]))
+ assert len(out) == 10
+
+ def test_fade_through_black_tf_zero(self) -> None:
+ clip_a = _clip(200, 4)
+ clip_b = _clip(100, 6)
+ v = _invocation("fade_through_black", 0)
+ out = list(v._iter_joined_frames([clip_a, clip_b]))
+ assert len(out) == 10
+
+
+class TestStreamingJoin:
+ """Joining must stream: output frames are yielded before the inputs are exhausted,
+ and look-ahead into the decoders stays bounded by the transition window."""
+
+ def _tracked_clip(self, value: int, n: int, pulled: list[int]) -> Iterator[np.ndarray]:
+ def gen() -> Iterator[np.ndarray]:
+ for _ in range(n):
+ pulled[0] += 1
+ yield np.full((4, 4, 3), value, dtype=np.uint8)
+
+ return gen()
+
+ def test_cut_emits_first_frame_after_single_pull(self) -> None:
+ pulled = [0]
+ v = _invocation("cut", 0)
+ gen = v._iter_joined_frames([self._tracked_clip(200, 100, pulled), self._tracked_clip(100, 100, pulled)])
+ next(gen)
+ # Producing the first output frame must not decode the rest of either clip.
+ assert pulled[0] == 1
+
+ @pytest.mark.parametrize("transition", ["crossfade", "fade_through_black"])
+ def test_lookahead_bounded_by_transition_window(self, transition: str) -> None:
+ tf = 5
+ pulled = [0]
+ v = _invocation(transition, tf)
+ gen = v._iter_joined_frames([self._tracked_clip(200, 60, pulled), self._tracked_clip(100, 60, pulled)])
+ emitted = 0
+ max_lookahead = 0
+ for _ in gen:
+ emitted += 1
+ max_lookahead = max(max_lookahead, pulled[0] - emitted)
+ # At any moment the generator holds at most the previous clip's tail window plus
+ # the next clip's head window — never a whole decoded clip.
+ assert max_lookahead <= 2 * tf + 1
+ assert pulled[0] == 120 # every input frame was still consumed exactly once
+
+ def test_streamed_output_matches_eager_reference(self) -> None:
+ # The join must be frame-for-frame identical whether clips arrive as one-shot
+ # iterators (the decoder) or materialized lists — guarding against any accidental
+ # reliance on len()/indexing sneaking back into the streaming path.
+ clip_a = [np.full((4, 4, 3), 20 + i, dtype=np.uint8) for i in range(12)]
+ clip_b = [np.full((4, 4, 3), 120 + i, dtype=np.uint8) for i in range(12)]
+ for transition, tf in [("cut", 0), ("crossfade", 4), ("fade_through_black", 5)]:
+ v = _invocation(transition, tf)
+ streamed = list(v._iter_joined_frames([iter(clip_a), iter(clip_b)]))
+ eager = list(v._iter_joined_frames([clip_a, clip_b]))
+ assert len(streamed) == len(eager)
+ for s, e in zip(streamed, eager, strict=True):
+ assert np.array_equal(s, e)
+
+ def test_short_clip_still_raises(self) -> None:
+ v = _invocation("crossfade", 8)
+ with pytest.raises(ValueError, match="transitions need"):
+ list(v._iter_joined_frames([iter(_clip(200, 4)), iter(_clip(100, 20))]))
+
+ def test_crossfade_yields_without_materializing_all_blends(self) -> None:
+ a = _clip(200, 8)
+ b = _clip(100, 8)
+ blended = _crossfade(a, b)
+ assert isinstance(blended, Iterator)
+ assert next(blended).shape == (4, 4, 3)
+
+
+class TestResourceBounds:
+ def test_rejects_transition_window_over_memory_budget(self) -> None:
+ v = _invocation("crossfade", 240)
+ with pytest.raises(ValueError, match="memory budget"):
+ v._validate_transition_memory(width=8192, height=8192)
+
+ def test_accepts_small_transition_window(self) -> None:
+ v = _invocation("crossfade", 8)
+ assert v._estimate_transition_memory(width=512, height=512) < MAX_TRANSITION_MEMORY_BYTES
+ v._validate_transition_memory(width=512, height=512)
+
+ def test_cancellation_stops_join(self) -> None:
+ v = _invocation("cut", 0)
+ with pytest.raises(CanceledException):
+ next(v._iter_joined_frames([iter(_clip(200, 10)), iter(_clip(100, 10))], is_canceled=lambda: True))
diff --git a/tests/app/invocations/test_video_frame_extract.py b/tests/app/invocations/test_video_frame_extract.py
new file mode 100644
index 00000000000..7c40b2a74f6
--- /dev/null
+++ b/tests/app/invocations/test_video_frame_extract.py
@@ -0,0 +1,48 @@
+"""Regression tests for VideoFrameExtractInvocation negative-index resolution.
+
+Covers JPPhoto's code-review finding (PR #9163): the old code computed
+``n_frames = round(duration * fps)`` to resolve ``frame_index=-1``. For uploads
+with inexact metadata that can overshoot the decoded frame count, requesting
+the last frame would fail. The fix queries ``iio.improps(...).shape[0]`` for
+the exact decoder count.
+
+We exercise the ``decoder_frame_count`` helper (now subprocess-backed) with a real synthetic
+MP4 so the iio integration is actually validated.
+"""
+
+import imageio.v3 as iio
+import numpy as np
+import pytest
+
+from invokeai.app.util.video_thumbnails import decoder_frame_count
+
+
+def _write_mp4(tmp_path, n_frames: int):
+ """Encode a tiny synthetic MP4 with exactly ``n_frames`` frames at 8 fps."""
+ path = tmp_path / "synth.mp4"
+ frames = [np.full((32, 32, 3), 64 + i * 8, dtype=np.uint8) for i in range(n_frames)]
+ iio.imwrite(path, frames, plugin="FFMPEG", codec="libx264", fps=8.0, macro_block_size=1)
+ return path
+
+
+class TestDecoderFrameCountExact:
+ """decoder_frame_count returns the actual decoded count from the container."""
+
+ @pytest.mark.parametrize("n", [1, 5, 16, 33])
+ def test_matches_encoded_frame_count(self, tmp_path, n: int) -> None:
+ path = _write_mp4(tmp_path, n)
+ assert decoder_frame_count(path) == n
+
+
+class TestDecoderFrameCountGracefulFallback:
+ """decoder_frame_count returns None on unreadable inputs so the caller can fall back."""
+
+ def test_missing_path_returns_none(self, tmp_path) -> None:
+ bogus = tmp_path / "does_not_exist.mp4"
+ # Either iio raises (caught) or returns props without shape — both must yield None.
+ assert decoder_frame_count(bogus) is None
+
+ def test_non_video_file_returns_none(self, tmp_path) -> None:
+ not_a_video = tmp_path / "junk.mp4"
+ not_a_video.write_bytes(b"not actually an mp4")
+ assert decoder_frame_count(not_a_video) is None
diff --git a/tests/app/invocations/test_video_frame_extract_range.py b/tests/app/invocations/test_video_frame_extract_range.py
new file mode 100644
index 00000000000..4c3f719aaa7
--- /dev/null
+++ b/tests/app/invocations/test_video_frame_extract_range.py
@@ -0,0 +1,113 @@
+"""Regression tests for ExtractVideoRangeInvocation streaming (PR #9163 review).
+
+The bug: the node collected every selected frame into a list before encoding, so the
+default ``start_frame=0, end_frame=-1`` materialized the whole source in RAM — and the
+upload API admits 1 GB compressed files whose decoded frames can run to tens of
+gigabytes. ``_write_frame_range`` now streams each frame straight into the encoder and
+stops decoding as soon as the range is written.
+"""
+
+from pathlib import Path
+from typing import Iterator
+from unittest.mock import MagicMock, patch
+
+import numpy as np
+import pytest
+
+from invokeai.app.invocations.fields import VideoField
+from invokeai.app.invocations.video_frame_extract_range import ExtractVideoRangeInvocation, _write_frame_range
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+
+
+class RecordingWriter:
+ """Stands in for the imageio writer; records what was appended and when."""
+
+ def __init__(self) -> None:
+ self.frames: list[np.ndarray] = []
+ self.pulled_at_first_append: int | None = None
+ self._pulled_ref: list[int] | None = None
+
+ def watch(self, pulled: list[int]) -> "RecordingWriter":
+ self._pulled_ref = pulled
+ return self
+
+ def append_data(self, frame: np.ndarray) -> None:
+ if self.pulled_at_first_append is None and self._pulled_ref is not None:
+ self.pulled_at_first_append = self._pulled_ref[0]
+ self.frames.append(frame)
+
+
+def _lazy_frames(n: int, pulled: list[int]) -> Iterator[np.ndarray]:
+ for i in range(n):
+ pulled[0] += 1
+ yield np.full((4, 4, 3), i % 255, dtype=np.uint8)
+
+
+def _fail_after(n: int) -> Iterator[np.ndarray]:
+ yield from _lazy_frames(n, [0])
+ raise RuntimeError("decoder advanced past requested range")
+
+
+class TestWriteFrameRangeStreams:
+ def test_encoding_begins_without_materializing_the_iterator(self) -> None:
+ pulled = [0]
+ writer = RecordingWriter().watch(pulled)
+ written = _write_frame_range(_lazy_frames(1000, pulled), writer, start=0, end=99)
+ assert written == 100
+ # The first frame must reach the encoder after a single decode, not after the
+ # whole range (let alone the whole file) has been buffered.
+ assert writer.pulled_at_first_append == 1
+
+ def test_decoding_stops_after_the_range(self) -> None:
+ pulled = [0]
+ writer = RecordingWriter().watch(pulled)
+ written = _write_frame_range(_lazy_frames(1000, pulled), writer, start=5, end=9)
+ assert written == 5
+ assert pulled[0] == 10
+
+ def test_does_not_pull_the_frame_after_end(self) -> None:
+ writer = RecordingWriter()
+ assert _write_frame_range(_fail_after(10), writer, start=5, end=9) == 5
+
+ def test_range_to_final_frame_consumes_input_exactly_once(self) -> None:
+ pulled = [0]
+ writer = RecordingWriter().watch(pulled)
+ written = _write_frame_range(_lazy_frames(24, pulled), writer, start=0, end=23)
+ assert written == 24
+ assert pulled[0] == 24
+ assert [int(f[0, 0, 0]) for f in writer.frames] == list(range(24))
+
+ def test_cancellation_stops_before_writing_more_frames(self) -> None:
+ pulled = [0]
+ writer = RecordingWriter()
+ with pytest.raises(CanceledException):
+ _write_frame_range(_lazy_frames(24, pulled), writer, start=0, end=23, is_canceled=lambda: pulled[0] >= 2)
+ assert len(writer.frames) == 1
+ assert pulled[0] == 2
+
+
+@pytest.mark.parametrize("written,should_raise", [(5, False), (3, True)])
+def test_invocation_only_saves_complete_requested_range(written: int, should_raise: bool) -> None:
+ invocation = ExtractVideoRangeInvocation(video=VideoField(video_name="input.mp4"), start_frame=0, end_frame=4)
+ context = MagicMock()
+ context.videos.get_path.return_value = Path("input.mp4")
+ context.util.is_canceled.return_value = False
+ base_output = MagicMock(
+ video=VideoField(video_name="output.mp4"), width=32, height=32, num_frames=5, fps=8.0, duration=0.625
+ )
+
+ with (
+ patch("invokeai.app.invocations.video_frame_extract_range.probe_video", return_value=(32, 32, 0.625, 8.0)),
+ patch("invokeai.app.invocations.video_frame_extract_range.decoder_frame_count", return_value=5),
+ patch("invokeai.app.invocations.video_frame_extract_range.iio2.get_writer", return_value=MagicMock()),
+ patch("invokeai.app.invocations.video_frame_extract_range._write_frame_range", return_value=written),
+ patch("invokeai.app.invocations.video_frame_extract_range.VideoOutput.build", return_value=base_output),
+ ):
+ if should_raise:
+ with pytest.raises(ValueError, match="Decoded only 3 of 5 requested frames"):
+ invocation.invoke(context)
+ context.videos.save.assert_not_called()
+ else:
+ output = invocation.invoke(context)
+ assert output.end_frame == 4
+ context.videos.save.assert_called_once()
diff --git a/tests/app/invocations/test_video_primitive.py b/tests/app/invocations/test_video_primitive.py
new file mode 100644
index 00000000000..1da7947d4ca
--- /dev/null
+++ b/tests/app/invocations/test_video_primitive.py
@@ -0,0 +1,19 @@
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from invokeai.app.invocations.fields import VideoField
+from invokeai.app.invocations.primitives import VideoInvocation
+
+
+@pytest.mark.parametrize("decoded_count,expected", [(7, 7), (None, 8)])
+def test_video_primitive_prefers_exact_decoder_frame_count(decoded_count: int | None, expected: int) -> None:
+ invocation = VideoInvocation(video=VideoField(video_name="input.mp4"))
+ context = MagicMock()
+ context.videos.get_dto.return_value = MagicMock(video_name="input.mp4", width=64, height=64, duration=1.0, fps=8.0)
+ context.videos.get_path.return_value = "input.mp4"
+
+ with patch("invokeai.app.util.video_thumbnails.decoder_frame_count", return_value=decoded_count):
+ output = invocation.invoke(context)
+
+ assert output.num_frames == expected
diff --git a/tests/app/invocations/test_wan_denoise.py b/tests/app/invocations/test_wan_denoise.py
new file mode 100644
index 00000000000..e4eba684c39
--- /dev/null
+++ b/tests/app/invocations/test_wan_denoise.py
@@ -0,0 +1,790 @@
+"""CPU-only integration tests for ``WanDenoiseInvocation``.
+
+These tests substitute a synthetic transformer (no weights) for the real
+``WanTransformer3DModel`` so the denoise loop's shape-handling, scheduler
+integration, CFG branch, and step-callback wiring can be exercised on a CPU
+runner. End-to-end tests against real Wan checkpoints are gated behind
+``INVOKEAI_HEAVY_TESTS=1`` and require a working CUDA install.
+"""
+
+from __future__ import annotations
+
+import os
+from contextlib import contextmanager
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
+
+import pytest
+import torch
+import torch.nn as nn
+
+from invokeai.app.invocations.fields import WanConditioningField, WanRefImageConditioningField
+from invokeai.app.invocations.model import WanTransformerField
+from invokeai.app.invocations.wan_denoise import WanDenoiseInvocation
+from invokeai.backend.model_manager.taxonomy import WanVariantType
+from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
+ ConditioningFieldData,
+ WanConditioningInfo,
+)
+
+
+class _ZeroTransformer(nn.Module):
+ """Stand-in for ``WanTransformer3DModel``.
+
+ Returns ``torch.zeros_like(hidden_states)`` so the flow-matching scheduler
+ treats every step as a no-op velocity. After N steps the latents equal the
+ initial noise — a useful invariant for shape correctness.
+
+ ``label`` lets dual-expert tests record which expert was invoked.
+ """
+
+ def __init__(self, label: str = "single") -> None:
+ super().__init__()
+ self.dtype = torch.float32
+ self.label = label
+ self.calls: list[tuple[int, ...]] = []
+ self.timesteps_seen: list[float] = []
+
+ def forward( # noqa: D401 — match diffusers signature
+ self,
+ hidden_states: torch.Tensor,
+ timestep: torch.Tensor,
+ encoder_hidden_states: torch.Tensor,
+ attention_kwargs=None,
+ return_dict: bool = True,
+ ):
+ # Record the call so assertions can verify shape contracts.
+ self.calls.append(
+ (
+ tuple(hidden_states.shape),
+ tuple(timestep.shape),
+ tuple(encoder_hidden_states.shape),
+ )
+ )
+ # Record the timestep (t.expand(B) → take first element).
+ self.timesteps_seen.append(float(timestep.flatten()[0].item()))
+ # Real Wan I2V transformer has in_channels=36 (16 noise + 20 ref-image
+ # condition) but out_channels=16. T2V is 16/16 and TI2V-5B is 48/48 —
+ # both have matching in/out. Mirror that by only collapsing the I2V
+ # input width back to 16 channels.
+ out_shape = list(hidden_states.shape)
+ if out_shape[1] == 36:
+ out_shape[1] = 16
+ out = torch.zeros(out_shape, dtype=hidden_states.dtype, device=hidden_states.device)
+ if return_dict:
+ return type("Out", (), {"sample": out})
+ return (out,)
+
+
+@contextmanager
+def _model_on_device_ctx(model: nn.Module):
+ yield (None, model)
+
+
+def _make_loaded_model(model: nn.Module) -> MagicMock:
+ """Mock ``LoadedModel`` exposing only the methods the denoise loop touches."""
+ loaded = MagicMock()
+ loaded.model_on_device = lambda: _model_on_device_ctx(model)
+ return loaded
+
+
+def _build_context(
+ transformer: nn.Module,
+ *,
+ variant: WanVariantType,
+ model_root: Path,
+ pos_cond: WanConditioningInfo,
+ neg_cond: WanConditioningInfo | None,
+ transformer_low: nn.Module | None = None,
+) -> MagicMock:
+ """Build a MagicMock InvocationContext sufficient for ``_run_diffusion``.
+
+ When ``transformer_low`` is provided, ``context.models.load`` routes the
+ request based on the ``ModelIdentifierField.submodel_type`` so dual-expert
+ code paths see two distinct loaded models.
+ """
+ config = MagicMock()
+ config.variant = variant
+ config.format = "diffusers"
+
+ context = MagicMock()
+ context.models.get_config.return_value = config
+ context.models.get_absolute_path.return_value = model_root
+
+ def _load(model_id) -> MagicMock:
+ submodel_type = getattr(model_id, "submodel_type", None)
+ if transformer_low is not None and str(submodel_type) == "SubModelType.Transformer2":
+ return _make_loaded_model(transformer_low)
+ return _make_loaded_model(transformer)
+
+ context.models.load.side_effect = _load
+
+ def _load_conditioning(name: str) -> ConditioningFieldData:
+ if name == "pos":
+ return ConditioningFieldData(conditionings=[pos_cond])
+ if name == "neg" and neg_cond is not None:
+ return ConditioningFieldData(conditionings=[neg_cond])
+ raise KeyError(name)
+
+ context.conditioning.load.side_effect = _load_conditioning
+ context.util.signal_progress = MagicMock()
+ context.util.sd_step_callback = MagicMock()
+ context.logger = MagicMock()
+ return context
+
+
+def _make_conditioning(seq_len: int = 226, hidden: int = 4096) -> WanConditioningInfo:
+ return WanConditioningInfo(
+ prompt_embeds=torch.zeros(seq_len, hidden),
+ prompt_attention_mask=None,
+ )
+
+
+def _make_invocation(
+ transformer_field: WanTransformerField,
+ pos_field: WanConditioningField,
+ neg_field: WanConditioningField | None,
+ *,
+ width: int,
+ height: int,
+ steps: int,
+ guidance_scale: float,
+ guidance_scale_low_noise: float | None = None,
+) -> WanDenoiseInvocation:
+ return WanDenoiseInvocation(
+ id="test",
+ transformer=transformer_field,
+ positive_conditioning=pos_field,
+ negative_conditioning=neg_field,
+ width=width,
+ height=height,
+ steps=steps,
+ guidance_scale=guidance_scale,
+ guidance_scale_low_noise=guidance_scale_low_noise,
+ seed=42,
+ )
+
+
+@pytest.fixture
+def fake_model_root():
+ """A directory layout the denoise helpers can read.
+
+ No ``scheduler/`` subfolder, so the scheduler falls back to defaults — that
+ keeps the test self-contained.
+ """
+ with TemporaryDirectory() as tmp:
+ yield Path(tmp)
+
+
+@pytest.fixture(autouse=True)
+def _force_cpu(monkeypatch):
+ """Pin TorchDevice to CPU + float32 for deterministic, GPU-free tests."""
+ from invokeai.backend.util.devices import TorchDevice
+
+ monkeypatch.setattr(TorchDevice, "choose_torch_device", classmethod(lambda cls: torch.device("cpu")))
+ monkeypatch.setattr(TorchDevice, "choose_bfloat16_safe_dtype", classmethod(lambda cls, device=None: torch.float32))
+
+
+def _wan_transformer_field(*, dual: bool = False, boundary_ratio: float = 0.875) -> WanTransformerField:
+ """Build a WanTransformerField. With ``dual=True`` a low-noise expert slot
+ is also populated so the denoise loop exercises the MoE swap path."""
+ base_id = {
+ "key": "wan-test",
+ "name": "wan-test",
+ "base": "wan",
+ "type": "main",
+ "hash": "h",
+ }
+ field_kwargs: dict = {
+ "transformer": {**base_id, "submodel_type": "transformer"},
+ "boundary_ratio": boundary_ratio,
+ }
+ if dual:
+ field_kwargs["transformer_low_noise"] = {**base_id, "submodel_type": "transformer_2"}
+ return WanTransformerField(**field_kwargs)
+
+
+class TestWanDenoiseShapes:
+ """Verify the denoise loop runs end-to-end on CPU for both variants."""
+
+ @pytest.mark.parametrize(
+ "variant,latent_channels,scale,height,width",
+ [
+ (WanVariantType.T2V_A14B, 16, 8, 64, 64),
+ (WanVariantType.TI2V_5B, 48, 16, 64, 64),
+ ],
+ )
+ def test_run_diffusion_returns_4d_finite(
+ self, variant, latent_channels, scale, height, width, fake_model_root
+ ) -> None:
+ transformer = _ZeroTransformer()
+ pos = _make_conditioning()
+ ctx = _build_context(
+ transformer,
+ variant=variant,
+ model_root=fake_model_root,
+ pos_cond=pos,
+ neg_cond=None,
+ )
+
+ inv = _make_invocation(
+ transformer_field=_wan_transformer_field(),
+ pos_field=WanConditioningField(conditioning_name="pos"),
+ neg_field=None,
+ width=width,
+ height=height,
+ steps=4,
+ guidance_scale=1.0, # disables CFG, so neg conditioning isn't required
+ )
+
+ latents = inv._run_diffusion(ctx)
+
+ # Output is 4D [B, C, H/scale, W/scale] — temporal dim squeezed.
+ assert latents.ndim == 4
+ assert latents.shape == (1, latent_channels, height // scale, width // scale)
+ assert torch.isfinite(latents).all()
+
+ # Transformer should have been called exactly steps times.
+ assert len(transformer.calls) == 4
+ # Hidden states are 5D with T=1.
+ h_shape, t_shape, ctx_shape = transformer.calls[0]
+ assert h_shape == (1, latent_channels, 1, height // scale, width // scale)
+ assert t_shape == (1,)
+ assert ctx_shape == (1, 226, 4096)
+
+ # Step callback invoked once per step.
+ assert ctx.util.sd_step_callback.call_count == 4
+
+ def test_cfg_doubles_transformer_calls(self, fake_model_root) -> None:
+ """With cfg_scale != 1.0 and a negative prompt, each step runs the model twice."""
+ transformer = _ZeroTransformer()
+ pos = _make_conditioning()
+ neg = _make_conditioning()
+ ctx = _build_context(
+ transformer,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ pos_cond=pos,
+ neg_cond=neg,
+ )
+
+ inv = _make_invocation(
+ transformer_field=_wan_transformer_field(),
+ pos_field=WanConditioningField(conditioning_name="pos"),
+ neg_field=WanConditioningField(conditioning_name="neg"),
+ width=64,
+ height=64,
+ steps=3,
+ guidance_scale=4.0,
+ )
+
+ inv._run_diffusion(ctx)
+ # 3 steps × 2 (cond + uncond) = 6 forward calls.
+ assert len(transformer.calls) == 6
+
+ def test_zero_velocity_preserves_initial_noise(self, fake_model_root) -> None:
+ """A zero-output transformer means the flow-match step never updates latents."""
+ transformer = _ZeroTransformer()
+ pos = _make_conditioning()
+ ctx = _build_context(
+ transformer,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ pos_cond=pos,
+ neg_cond=None,
+ )
+
+ inv = _make_invocation(
+ transformer_field=_wan_transformer_field(),
+ pos_field=WanConditioningField(conditioning_name="pos"),
+ neg_field=None,
+ width=64,
+ height=64,
+ steps=4,
+ guidance_scale=1.0,
+ )
+
+ latents = inv._run_diffusion(ctx)
+
+ # Reproduce the same noise the loop would have generated and compare.
+ from invokeai.backend.wan.sampling_utils import make_noise
+
+ expected = make_noise(
+ batch_size=1,
+ latent_channels=16,
+ height=64,
+ width=64,
+ spatial_scale_factor=8,
+ device=torch.device("cpu"),
+ dtype=torch.float32,
+ seed=42,
+ ).squeeze(2)
+
+ assert torch.allclose(latents, expected, atol=1e-5)
+
+
+class TestWanDenoiseDualExpert:
+ """Verify the A14B dual-expert MoE swap behaves correctly."""
+
+ def test_swap_fires_at_boundary(self, fake_model_root) -> None:
+ """High expert handles t >= boundary_timestep, low expert handles t < boundary_timestep."""
+ high = _ZeroTransformer(label="high")
+ low = _ZeroTransformer(label="low")
+ pos = _make_conditioning()
+ ctx = _build_context(
+ high,
+ transformer_low=low,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ pos_cond=pos,
+ neg_cond=None,
+ )
+
+ # boundary_ratio=0.5 → boundary_timestep=500 (default num_train_timesteps=1000).
+ inv = _make_invocation(
+ transformer_field=_wan_transformer_field(dual=True, boundary_ratio=0.5),
+ pos_field=WanConditioningField(conditioning_name="pos"),
+ neg_field=None,
+ width=64,
+ height=64,
+ steps=10,
+ guidance_scale=1.0,
+ )
+
+ inv._run_diffusion(ctx)
+
+ # Both experts called.
+ assert len(high.timesteps_seen) > 0, "high-noise expert never invoked"
+ assert len(low.timesteps_seen) > 0, "low-noise expert never invoked"
+
+ # Every high-noise timestep is >= 500; every low-noise timestep is < 500.
+ for t in high.timesteps_seen:
+ assert t >= 500.0, f"high-noise expert saw t={t}, should be >= 500"
+ for t in low.timesteps_seen:
+ assert t < 500.0, f"low-noise expert saw t={t}, should be < 500"
+
+ # Total steps adds up.
+ assert len(high.timesteps_seen) + len(low.timesteps_seen) == 10
+
+ def test_no_swap_when_boundary_skipped(self, fake_model_root) -> None:
+ """boundary_ratio=0.0 → boundary_timestep=0 → all timesteps go to high-noise expert."""
+ high = _ZeroTransformer(label="high")
+ low = _ZeroTransformer(label="low")
+ pos = _make_conditioning()
+ ctx = _build_context(
+ high,
+ transformer_low=low,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ pos_cond=pos,
+ neg_cond=None,
+ )
+
+ inv = _make_invocation(
+ transformer_field=_wan_transformer_field(dual=True, boundary_ratio=0.0),
+ pos_field=WanConditioningField(conditioning_name="pos"),
+ neg_field=None,
+ width=64,
+ height=64,
+ steps=4,
+ guidance_scale=1.0,
+ )
+
+ inv._run_diffusion(ctx)
+
+ # boundary_timestep=0 → t >= 0 always → high-noise expert handles every step.
+ assert len(high.timesteps_seen) == 4
+ assert len(low.timesteps_seen) == 0
+
+ def test_full_low_noise_when_boundary_at_max(self, fake_model_root) -> None:
+ """boundary_ratio=1.0 → boundary_timestep=1000 → almost all steps go to low-noise expert.
+
+ With FlowMatchEuler the first timestep is exactly 1000 so the high-noise
+ expert handles it (>= boundary), and every subsequent timestep is < 1000.
+ """
+ high = _ZeroTransformer(label="high")
+ low = _ZeroTransformer(label="low")
+ pos = _make_conditioning()
+ ctx = _build_context(
+ high,
+ transformer_low=low,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ pos_cond=pos,
+ neg_cond=None,
+ )
+
+ inv = _make_invocation(
+ transformer_field=_wan_transformer_field(dual=True, boundary_ratio=1.0),
+ pos_field=WanConditioningField(conditioning_name="pos"),
+ neg_field=None,
+ width=64,
+ height=64,
+ steps=4,
+ guidance_scale=1.0,
+ )
+
+ inv._run_diffusion(ctx)
+
+ # First step is t==1000 → high. All later steps are < 1000 → low.
+ assert len(high.timesteps_seen) == 1
+ assert high.timesteps_seen[0] == 1000.0
+ assert len(low.timesteps_seen) == 3
+
+ def test_cfg_with_dual_experts_doubles_calls_per_step(self, fake_model_root) -> None:
+ """With negative conditioning + cfg_scale != 1, every step runs the active expert twice."""
+ high = _ZeroTransformer(label="high")
+ low = _ZeroTransformer(label="low")
+ pos = _make_conditioning()
+ neg = _make_conditioning()
+ ctx = _build_context(
+ high,
+ transformer_low=low,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ pos_cond=pos,
+ neg_cond=neg,
+ )
+
+ inv = _make_invocation(
+ transformer_field=_wan_transformer_field(dual=True, boundary_ratio=0.5),
+ pos_field=WanConditioningField(conditioning_name="pos"),
+ neg_field=WanConditioningField(conditioning_name="neg"),
+ width=64,
+ height=64,
+ steps=6,
+ guidance_scale=4.0,
+ guidance_scale_low_noise=2.0, # Field accepted by the invocation; effect is implicit.
+ )
+
+ inv._run_diffusion(ctx)
+
+ # Total transformer invocations: 6 steps × 2 (cond + uncond) = 12, split across experts.
+ total = len(high.timesteps_seen) + len(low.timesteps_seen)
+ assert total == 12
+
+ # Each unique timestep appears twice (cond + uncond) on the same expert.
+ from collections import Counter
+
+ high_counts = Counter(high.timesteps_seen)
+ low_counts = Counter(low.timesteps_seen)
+ assert all(v == 2 for v in high_counts.values()), high_counts
+ assert all(v == 2 for v in low_counts.values()), low_counts
+
+ # And the swap actually happened — both experts saw work.
+ assert len(high_counts) > 0 and len(low_counts) > 0
+
+
+@pytest.mark.skipif(
+ os.environ.get("INVOKEAI_HEAVY_TESTS") != "1",
+ reason="End-to-end test requires real Wan weights and CUDA; opt in with INVOKEAI_HEAVY_TESTS=1",
+)
+class TestWanDenoiseHeavy:
+ """Placeholder for a real-weights smoke test once CUDA is available."""
+
+ def test_real_ti2v_5b_runs(self) -> None:
+ pytest.skip("Heavy test stub — implement once a TI2V-5B checkpoint is installable.")
+
+
+class TestWanDenoiseRefImage:
+ """Phase 7: VAE-latent reference-image conditioning for I2V-A14B.
+
+ The denoise loop must concatenate the 20-channel condition tensor to the
+ 16-channel noise latents at every transformer call, producing 36-channel
+ input. Variant gate must fast-fail when ref_image is wired to a non-I2V
+ transformer."""
+
+ def _build_ctx_with_condition(
+ self,
+ transformer: _ZeroTransformer,
+ variant: WanVariantType,
+ model_root: Path,
+ condition_tensor: torch.Tensor | None,
+ ) -> MagicMock:
+ ctx = _build_context(
+ transformer,
+ variant=variant,
+ model_root=model_root,
+ pos_cond=_make_conditioning(),
+ neg_cond=None,
+ )
+ if condition_tensor is not None:
+ ctx.tensors.load.return_value = condition_tensor
+ return ctx
+
+ def _make_inv_with_ref(
+ self,
+ ref_field: "WanRefImageConditioningField | None",
+ *,
+ width: int = 64,
+ height: int = 64,
+ ) -> WanDenoiseInvocation:
+ return WanDenoiseInvocation(
+ id="test",
+ transformer=_wan_transformer_field(dual=True),
+ positive_conditioning=WanConditioningField(conditioning_name="pos"),
+ negative_conditioning=None,
+ ref_image=ref_field,
+ width=width,
+ height=height,
+ steps=3,
+ guidance_scale=1.0,
+ seed=42,
+ )
+
+ def test_ref_image_concatenated_to_36_channels(self, fake_model_root: Path) -> None:
+ """I2V_A14B + ref_image → transformer sees [B, 36, T, H/8, W/8]."""
+ transformer = _ZeroTransformer()
+ # Build the 20-channel condition tensor the encoder would have saved:
+ # 4-ch first-frame mask + 16-ch VAE-encoded image latents.
+ # At 64x64 → 8x8 latent spatial dims.
+ condition = torch.zeros(1, 20, 1, 8, 8)
+ ctx = self._build_ctx_with_condition(transformer, WanVariantType.I2V_A14B, fake_model_root, condition)
+
+ ref_field = WanRefImageConditioningField(condition_tensor_name="condition", width=64, height=64)
+ inv = self._make_inv_with_ref(ref_field)
+ inv._run_diffusion(ctx)
+
+ assert len(transformer.calls) == 3
+ # Every call's hidden_states must have 36 channels (16 noise + 20 condition).
+ for h_shape, *_ in transformer.calls:
+ assert h_shape == (1, 36, 1, 8, 8), f"expected 36-channel input, got {h_shape}"
+
+ def test_no_ref_image_keeps_16_channels(self, fake_model_root: Path) -> None:
+ """Without ref_image → transformer sees [B, 16, T, H/8, W/8] as before."""
+ transformer = _ZeroTransformer()
+ ctx = self._build_ctx_with_condition(
+ transformer, WanVariantType.I2V_A14B, fake_model_root, condition_tensor=None
+ )
+
+ inv = self._make_inv_with_ref(ref_field=None)
+ inv._run_diffusion(ctx)
+
+ for h_shape, *_ in transformer.calls:
+ assert h_shape == (1, 16, 1, 8, 8), f"expected unchanged 16-channel input, got {h_shape}"
+
+ def test_variant_gate_rejects_ref_image_on_t2v(self, fake_model_root: Path) -> None:
+ """T2V_A14B + ref_image must raise — fast-fail before doing any work."""
+ transformer = _ZeroTransformer()
+ condition = torch.zeros(1, 20, 1, 8, 8)
+ ctx = self._build_ctx_with_condition(transformer, WanVariantType.T2V_A14B, fake_model_root, condition)
+
+ ref_field = WanRefImageConditioningField(condition_tensor_name="condition", width=64, height=64)
+ inv = self._make_inv_with_ref(ref_field)
+ with pytest.raises(ValueError, match="only supported by the Wan 2.2 I2V variant"):
+ inv._run_diffusion(ctx)
+
+ def test_variant_gate_rejects_ref_image_on_ti2v(self, fake_model_root: Path) -> None:
+ """TI2V-5B + ref_image must raise — TI2V uses a different image path."""
+ transformer = _ZeroTransformer()
+ condition = torch.zeros(1, 20, 1, 8, 8)
+ ctx = self._build_ctx_with_condition(transformer, WanVariantType.TI2V_5B, fake_model_root, condition)
+
+ ref_field = WanRefImageConditioningField(condition_tensor_name="condition", width=64, height=64)
+ inv = self._make_inv_with_ref(ref_field)
+ with pytest.raises(ValueError, match="only supported by the Wan 2.2 I2V variant"):
+ inv._run_diffusion(ctx)
+
+ def test_dim_mismatch_raises(self, fake_model_root: Path) -> None:
+ """If the encoder's width/height differ from denoise's, fail clearly."""
+ transformer = _ZeroTransformer()
+ condition = torch.zeros(1, 20, 1, 8, 8)
+ ctx = self._build_ctx_with_condition(transformer, WanVariantType.I2V_A14B, fake_model_root, condition)
+
+ ref_field = WanRefImageConditioningField(condition_tensor_name="condition", width=512, height=512)
+ inv = self._make_inv_with_ref(ref_field, width=64, height=64)
+ with pytest.raises(ValueError, match="must match denoise dimensions"):
+ inv._run_diffusion(ctx)
+
+
+class TestWanDenoiseInpaint:
+ """Phase 8: ``denoise_mask`` (inpaint) wiring via ``RectifiedFlowInpaintExtension``.
+
+ User-side mask convention (matches Anima / Flux): 1.0 = preserve,
+ 0.0 = regenerate. After ``_prep_inpaint_mask`` inverts, the extension
+ sees: 0.0 = preserve, 1.0 = regenerate.
+
+ With the synthetic zero-output transformer, the scheduler step is a
+ no-op (noise_pred=0 → latents unchanged). The init latents are placed
+ into the preserved regions at every step via the extension's merge
+ function; the regenerated regions stay as the original noise tensor
+ because the model never updates them.
+ """
+
+ def _build_inpaint_context(
+ self,
+ transformer: _ZeroTransformer,
+ variant: WanVariantType,
+ model_root: Path,
+ init_latents: torch.Tensor,
+ mask: torch.Tensor,
+ ) -> MagicMock:
+ ctx = _build_context(
+ transformer,
+ variant=variant,
+ model_root=model_root,
+ pos_cond=_make_conditioning(),
+ neg_cond=None,
+ )
+
+ # tensors.load needs to return different tensors for the init-latents
+ # and the mask, dispatched by the name field.
+ def _load_tensor(name: str) -> torch.Tensor:
+ if name == "init":
+ return init_latents
+ if name == "mask":
+ return mask
+ raise KeyError(name)
+
+ ctx.tensors.load.side_effect = _load_tensor
+ return ctx
+
+ def test_preserved_region_matches_init_exactly(self, fake_model_root: Path) -> None:
+ from invokeai.app.invocations.fields import DenoiseMaskField, LatentsField
+
+ transformer = _ZeroTransformer()
+ # 64x64 image -> 8x8 latents at scale 8 (T2V-A14B family).
+ # Init latents: fixed value 0.5 so the preserved region is detectable.
+ init_latents = torch.full((1, 16, 8, 8), 0.5)
+ # Mask: 8x8 spatial mask, half-1 (preserve left), half-0 (regenerate right).
+ # User-side convention: 1 = preserve, 0 = regenerate.
+ mask = torch.zeros(1, 1, 8, 8)
+ mask[..., :, :4] = 1.0 # left half preserved
+
+ ctx = self._build_inpaint_context(
+ transformer,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ init_latents=init_latents,
+ mask=mask,
+ )
+
+ inv = WanDenoiseInvocation(
+ id="test",
+ transformer=_wan_transformer_field(),
+ positive_conditioning=WanConditioningField(conditioning_name="pos"),
+ negative_conditioning=None,
+ latents=LatentsField(latents_name="init"),
+ denoise_mask=DenoiseMaskField(mask_name="mask", masked_latents_name=None, gradient=False),
+ width=64,
+ height=64,
+ steps=4,
+ guidance_scale=1.0,
+ denoising_start=0.0,
+ denoising_end=1.0,
+ seed=42,
+ )
+
+ out = inv._run_diffusion(ctx) # [B, C, H_lat, W_lat]
+ assert out.shape == (1, 16, 8, 8)
+
+ # Preserved (left) half: must exactly match the init latents at t_prev=0
+ # (final step's merge produces noised_init = noise*0 + 1*init = init).
+ assert torch.allclose(out[..., :, :4], torch.full_like(out[..., :, :4], 0.5)), (
+ "Preserved region must equal init latents at the end of denoise"
+ )
+
+ # Regenerated (right) half: model never changed anything (zero transformer)
+ # so this region stays equal to the original noise, NOT to init.
+ # Assert it's *not* equal to init — concrete proof the regions are
+ # being handled separately.
+ assert not torch.allclose(out[..., :, 4:], torch.full_like(out[..., :, 4:], 0.5)), (
+ "Regenerated region should NOT equal init — extension must route it through the model path"
+ )
+
+ def test_inpaint_requires_init_latents(self, fake_model_root: Path) -> None:
+ """Providing a mask without init latents must raise — there's nothing
+ to merge back into the preserved regions."""
+ from invokeai.app.invocations.fields import DenoiseMaskField
+
+ transformer = _ZeroTransformer()
+ mask = torch.ones(1, 1, 8, 8)
+ ctx = self._build_inpaint_context(
+ transformer,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ init_latents=torch.zeros(1, 16, 8, 8), # unused
+ mask=mask,
+ )
+
+ inv = WanDenoiseInvocation(
+ id="test",
+ transformer=_wan_transformer_field(),
+ positive_conditioning=WanConditioningField(conditioning_name="pos"),
+ negative_conditioning=None,
+ latents=None, # missing — error
+ denoise_mask=DenoiseMaskField(mask_name="mask", masked_latents_name=None, gradient=False),
+ width=64,
+ height=64,
+ steps=2,
+ guidance_scale=1.0,
+ seed=42,
+ )
+
+ with pytest.raises(ValueError, match="img2img inpainting"):
+ inv._run_diffusion(ctx)
+
+ def test_no_mask_path_is_unchanged(self, fake_model_root: Path) -> None:
+ """Without a denoise_mask, the loop behaves as before — sanity check
+ that adding the inpaint extension didn't introduce a regression on
+ the non-inpaint codepath."""
+ from invokeai.app.invocations.fields import LatentsField
+
+ transformer = _ZeroTransformer()
+ init_latents = torch.full((1, 16, 8, 8), 0.3)
+ ctx = self._build_inpaint_context(
+ transformer,
+ variant=WanVariantType.T2V_A14B,
+ model_root=fake_model_root,
+ init_latents=init_latents,
+ mask=torch.zeros(1, 1, 8, 8), # unused — no mask wired
+ )
+
+ inv = WanDenoiseInvocation(
+ id="test",
+ transformer=_wan_transformer_field(),
+ positive_conditioning=WanConditioningField(conditioning_name="pos"),
+ negative_conditioning=None,
+ latents=LatentsField(latents_name="init"),
+ denoise_mask=None, # no mask
+ width=64,
+ height=64,
+ steps=4,
+ guidance_scale=1.0,
+ denoising_start=0.5, # img2img-style partial denoise
+ denoising_end=1.0,
+ seed=42,
+ )
+
+ out = inv._run_diffusion(ctx)
+ assert out.shape == (1, 16, 8, 8)
+ assert torch.isfinite(out).all()
+
+
+class TestDefaultSchedulerForVariant:
+ """``_default_scheduler_for_variant`` returns the right class + config when no
+ on-disk ``scheduler/`` directory exists (the standalone GGUF / single-file case).
+ """
+
+ def test_ti2v_5b_returns_unipc_with_flow_config(self) -> None:
+ from diffusers import UniPCMultistepScheduler
+
+ from invokeai.app.invocations.wan_denoise import _default_scheduler_for_variant
+
+ s = _default_scheduler_for_variant(WanVariantType.TI2V_5B)
+ assert isinstance(s, UniPCMultistepScheduler)
+ # The combination below is what makes this a "Wan flow" UniPC rather than a
+ # generic UniPC schedule — wrong values here drift on TI2V-5B samples.
+ assert s.config.flow_shift == 5.0
+ assert s.config.prediction_type == "flow_prediction"
+ assert s.config.use_flow_sigmas is True
+ assert s.config.solver_type == "bh2"
+
+ def test_a14b_variants_return_flow_match_euler(self) -> None:
+ from diffusers import FlowMatchEulerDiscreteScheduler
+
+ from invokeai.app.invocations.wan_denoise import _default_scheduler_for_variant
+
+ for v in (WanVariantType.T2V_A14B, WanVariantType.I2V_A14B):
+ assert isinstance(_default_scheduler_for_variant(v), FlowMatchEulerDiscreteScheduler)
diff --git a/tests/app/invocations/test_wan_expert_swapper.py b/tests/app/invocations/test_wan_expert_swapper.py
new file mode 100644
index 00000000000..bb50bf7fe35
--- /dev/null
+++ b/tests/app/invocations/test_wan_expert_swapper.py
@@ -0,0 +1,565 @@
+"""Tests for ``_ExpertSwapper``'s LoRA-context lifecycle.
+
+The swapper is responsible for entering and exiting both the
+``model_on_device`` context and the ``LayerPatcher.apply_smart_model_patches``
+context in the right order across an expert swap:
+
+ enter HIGH: enter device(HIGH) -> enter lora(HIGH)
+ swap: exit lora(HIGH) -> exit device(HIGH)
+ enter device(LOW) -> enter lora(LOW)
+ close: exit lora(LOW) -> exit device(LOW)
+
+These tests use a tiny ``nn.Linear`` standing in for each transformer expert
+so we can verify the swapper hands back the right model and routes the right
+LoRA factory at each step.
+"""
+
+from typing import Iterable, Tuple
+from unittest.mock import patch
+
+import pytest
+import torch
+import torch.nn as nn
+
+from invokeai.app.invocations.wan_denoise import _ExpertSwapper
+from invokeai.backend.patches.model_patch_raw import ModelPatchRaw
+
+
+class _FakeModelOnDevice:
+ """Minimal stand-in for the model-cache record's ``model_on_device`` context.
+
+ Tracks enter/exit to verify the swapper's lifecycle invariants."""
+
+ def __init__(self, label: str, model: nn.Module, log: list[str]) -> None:
+ self._label = label
+ self._model = model
+ self._log = log
+
+ def __enter__(self):
+ self._log.append(f"device-enter:{self._label}")
+ # Return shape mirrors the real model cache: (cached_weights, model).
+ return (None, self._model)
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self._log.append(f"device-exit:{self._label}")
+ return False
+
+
+class _FakeCachedModel:
+ """Stand-in for ``CachedModelWithPartialLoad``: records full_unload_from_vram calls."""
+
+ def __init__(self, label: str, log: list[str]) -> None:
+ self._label = label
+ self._log = log
+ self.unload_calls = 0
+
+ def full_unload_from_vram(self) -> int:
+ self._log.append(f"full-unload:{self._label}")
+ self.unload_calls += 1
+ return 0
+
+
+class _FakeCacheRecord:
+ def __init__(self, cached_model: _FakeCachedModel) -> None:
+ self.cached_model = cached_model
+
+
+class _FakeInfo:
+ """Mirrors the runtime ``LoadedModel`` enough for the swapper to reach
+ ``info._cache_record.cached_model.full_unload_from_vram()`` on swap."""
+
+ def __init__(self, label: str, model: nn.Module, log: list[str]) -> None:
+ self._label = label
+ self._model = model
+ self._log = log
+ self._cache_record = _FakeCacheRecord(_FakeCachedModel(label, log))
+
+ def model_on_device(self):
+ return _FakeModelOnDevice(self._label, self._model, self._log)
+
+
+class _FakeContext:
+ """Mocks ``InvocationContext.models.load`` returning a fresh ``_FakeInfo``
+ for each call — mirrors the real behaviour where the swapper expects a
+ fresh handle per ``get()``."""
+
+ def __init__(self, infos_by_model_id: dict[str, _FakeInfo], log: list[str]) -> None:
+ self._infos = infos_by_model_id
+ self._log = log
+ # Track how many times each model id was loaded — the lazy-load fix
+ # depends on this count being 1 per swap, not 1 upfront.
+
+ class _Models:
+ def __init__(self, outer):
+ self._outer = outer
+ self.load_calls: list[str] = []
+
+ def load(self, model_id):
+ self.load_calls.append(model_id)
+ self._outer._log.append(f"models.load:{model_id}")
+ return self._outer._infos[model_id]
+
+ self.models = _Models(self)
+
+
+def _make_factory(log: list[str], label: str) -> "callable":
+ """Build a LoRAIteratorFactory that records each invocation in ``log``."""
+
+ def factory() -> Iterable[Tuple[ModelPatchRaw, float]]:
+ log.append(f"lora-factory-call:{label}")
+ return iter([])
+
+ return factory
+
+
+def _stub_lora_context_manager(log: list[str]):
+ """Patch ``LayerPatcher.apply_smart_model_patches`` to a stub that records
+ enter/exit in ``log`` and returns a no-op context manager.
+
+ The stub introspects its arguments so we can verify the swapper passes
+ the correct ``model``, ``patches`` factory output, and prefix.
+ """
+ calls: list[dict] = []
+
+ class _Stub:
+ def __init__(self, model, patches, prefix, dtype, cached_weights, force_sidecar_patching):
+ self.model = model
+ self.patches = patches
+ self.prefix = prefix
+ self.dtype = dtype
+ self.cached_weights = cached_weights
+ self.force_sidecar_patching = force_sidecar_patching
+ calls.append(
+ {
+ "model": model,
+ "prefix": prefix,
+ "dtype": dtype,
+ "force_sidecar_patching": force_sidecar_patching,
+ }
+ )
+
+ def __enter__(self):
+ log.append("lora-enter")
+ # Force the factory's iterator to evaluate so we can assert it was
+ # consumed (mirrors the real LayerPatcher behavior).
+ list(self.patches)
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ log.append("lora-exit")
+ return False
+
+ def factory(model, patches, prefix, dtype, cached_weights, force_sidecar_patching=False):
+ return _Stub(model, patches, prefix, dtype, cached_weights, force_sidecar_patching)
+
+ return factory, calls
+
+
+def test_lifecycle_high_only():
+ """Single-expert (TI2V-5B / A14B with only high loaded): enter HIGH, close."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ ctx = _FakeContext({"high": _FakeInfo("HIGH", high_nn, log)}, log)
+
+ stub, calls = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model=None,
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ low_lora_factory=None,
+ )
+ model = swapper.get(_ExpertSwapper.HIGH)
+ assert model is high_nn
+ swapper.close()
+
+ assert log == [
+ "models.load:high",
+ "device-enter:HIGH",
+ "lora-factory-call:HIGH",
+ "lora-enter",
+ "lora-exit",
+ "device-exit:HIGH",
+ ]
+ assert len(calls) == 1
+ assert calls[0]["model"] is high_nn
+ assert calls[0]["prefix"] == "lora_transformer-"
+
+
+def test_lifecycle_dual_expert_swap():
+ """A14B: HIGH first, then LOW. Each LoRA context opens/closes with its expert."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ low_nn = nn.Linear(1, 1)
+ ctx = _FakeContext(
+ {"high": _FakeInfo("HIGH", high_nn, log), "low": _FakeInfo("LOW", low_nn, log)},
+ log,
+ )
+
+ stub, calls = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model="low",
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ low_lora_factory=_make_factory(log, "LOW"),
+ )
+ first = swapper.get(_ExpertSwapper.HIGH)
+ assert first is high_nn
+
+ second = swapper.get(_ExpertSwapper.LOW)
+ assert second is low_nn
+
+ swapper.close()
+
+ expected = [
+ # enter HIGH (models.load first, then device, then lora)
+ "models.load:high",
+ "device-enter:HIGH",
+ "lora-factory-call:HIGH",
+ "lora-enter",
+ # swap to LOW: LoRA out -> device out -> force-unload HIGH -> models.load LOW
+ # -> device in -> LoRA in. The full-unload step shoves HIGH's weights off GPU
+ # before the cache decides how much room LOW gets.
+ "lora-exit",
+ "device-exit:HIGH",
+ "full-unload:HIGH",
+ "models.load:low",
+ "device-enter:LOW",
+ "lora-factory-call:LOW",
+ "lora-enter",
+ # close
+ "lora-exit",
+ "device-exit:LOW",
+ ]
+ assert log == expected
+ # Two patcher invocations, each bound to the expected model.
+ assert len(calls) == 2
+ assert calls[0]["model"] is high_nn
+ assert calls[1]["model"] is low_nn
+
+
+def test_quantized_flag_forwards_to_sidecar():
+ """GGUF (quantized) experts must request sidecar patching."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ ctx = _FakeContext({"high": _FakeInfo("HIGH", high_nn, log)}, log)
+
+ stub, calls = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model=None,
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ high_is_quantized=True,
+ )
+ swapper.get(_ExpertSwapper.HIGH)
+ swapper.close()
+
+ assert calls[0]["force_sidecar_patching"] is True
+
+
+def test_no_lora_factory_skips_lora_context():
+ """When no LoRAs are wired, the swapper doesn't enter the LoRA context."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ ctx = _FakeContext({"high": _FakeInfo("HIGH", high_nn, log)}, log)
+
+ stub, calls = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model=None,
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=None, # no LoRAs
+ low_lora_factory=None,
+ )
+ swapper.get(_ExpertSwapper.HIGH)
+ swapper.close()
+
+ # No "lora-enter" / "lora-exit" entries — LayerPatcher was never invoked.
+ assert "lora-enter" not in log
+ assert "lora-exit" not in log
+ assert len(calls) == 0
+
+
+def test_repeat_get_same_label_is_a_no_op():
+ """Calling get(HIGH) twice in a row must not re-enter the contexts.
+
+ Critically, ``models.load`` must only be called once per actual swap —
+ not on every ``get()``. Caching the loaded model on first entry, and
+ short-circuiting re-entry, prevents per-step cache thrash."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ ctx = _FakeContext({"high": _FakeInfo("HIGH", high_nn, log)}, log)
+
+ stub, calls = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model=None,
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ )
+ swapper.get(_ExpertSwapper.HIGH)
+ swapper.get(_ExpertSwapper.HIGH) # should be a no-op
+ swapper.close()
+
+ # device-enter + lora-enter happen exactly once, and crucially
+ # models.load is called only once — repeat get() must short-circuit
+ # so the cache isn't re-touched every step of the denoise loop.
+ assert log.count("models.load:high") == 1
+ assert log.count("device-enter:HIGH") == 1
+ assert log.count("lora-enter") == 1
+ assert log.count("lora-exit") == 1
+ assert log.count("device-exit:HIGH") == 1
+
+
+def test_lazy_load_per_swap_not_upfront():
+ """Regression for the cache-eviction warning that triggered this fix.
+
+ ``models.load`` must NOT be called at swapper construction. It is called
+ only on the first ``get()`` for each expert. This keeps the per-handle
+ cache window small enough that the LRU policy doesn't drop one expert
+ while the other is being used."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ low_nn = nn.Linear(1, 1)
+ ctx = _FakeContext(
+ {"high": _FakeInfo("HIGH", high_nn, log), "low": _FakeInfo("LOW", low_nn, log)},
+ log,
+ )
+
+ stub, _ = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ # Construction alone must not trigger any models.load call.
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model="low",
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ low_lora_factory=_make_factory(log, "LOW"),
+ )
+ assert ctx.models.load_calls == [], (
+ "Swapper must not call models.load until get() is invoked — see issue #7513 for cache-eviction rationale."
+ )
+
+ # First get(HIGH): loads HIGH only.
+ swapper.get(_ExpertSwapper.HIGH)
+ assert ctx.models.load_calls == ["high"]
+
+ # Swap to LOW: loads LOW only. HIGH is NOT re-loaded — its handle
+ # was used and released, the next call to it (if any) will re-load.
+ swapper.get(_ExpertSwapper.LOW)
+ assert ctx.models.load_calls == ["high", "low"]
+
+ # Back to HIGH: a fresh load (the previous handle is gone). This is
+ # the right behaviour — each swap gets a guaranteed-fresh handle
+ # rather than a stale reference into the cache.
+ swapper.get(_ExpertSwapper.HIGH)
+ assert ctx.models.load_calls == ["high", "low", "high"]
+
+ swapper.close()
+
+
+def test_empty_cache_called_on_swap():
+ """Regression: each expert swap must trigger ``TorchDevice.empty_cache()`` so
+ the next ``partial_load_to_vram`` sees an un-fragmented allocator.
+
+ A14B users reported the low-noise expert ending up far more CPU-resident than
+ the high-noise one — the previous expert's freed blocks stayed pinned in the
+ PyTorch caching allocator across the swap, and partial_load decided there
+ wasn't room for as much of the incoming expert as there actually was."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ low_nn = nn.Linear(1, 1)
+ ctx = _FakeContext(
+ {"high": _FakeInfo("HIGH", high_nn, log), "low": _FakeInfo("LOW", low_nn, log)},
+ log,
+ )
+
+ stub, _ = _stub_lora_context_manager(log)
+ with (
+ patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ),
+ patch("invokeai.app.invocations.wan_denoise.TorchDevice.empty_cache") as empty_cache_mock,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model="low",
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ low_lora_factory=_make_factory(log, "LOW"),
+ )
+ swapper.get(_ExpertSwapper.HIGH)
+ first_call_count = empty_cache_mock.call_count
+ assert first_call_count >= 1, "empty_cache should run on the initial expert load too"
+
+ swapper.get(_ExpertSwapper.LOW)
+ assert empty_cache_mock.call_count > first_call_count, (
+ "empty_cache must be called on each HIGH→LOW (or LOW→HIGH) swap"
+ )
+
+ # Same-label re-get is a no-op; empty_cache must NOT be re-invoked.
+ before_no_op = empty_cache_mock.call_count
+ swapper.get(_ExpertSwapper.LOW)
+ assert empty_cache_mock.call_count == before_no_op, (
+ "Re-getting the active expert must short-circuit before empty_cache."
+ )
+
+ swapper.close()
+
+
+def test_outgoing_expert_force_unloaded_from_vram():
+ """Regression: on swap, the previous expert's weights must be explicitly forced
+ off VRAM via ``cached_model.full_unload_from_vram()``.
+
+ A14B users observed the high-noise transformer continuing to occupy ~9 GB of
+ VRAM during the low-noise step, because the cache's automatic offload heuristic
+ underestimated how much room the new expert needed when workspace memory from
+ the previous denoise step was still allocated. The swapper sidesteps that by
+ invoking full_unload_from_vram on the outgoing expert directly."""
+ log: list[str] = []
+ high_info = _FakeInfo("HIGH", nn.Linear(1, 1), log)
+ low_info = _FakeInfo("LOW", nn.Linear(1, 1), log)
+ ctx = _FakeContext({"high": high_info, "low": low_info}, log)
+
+ stub, _ = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model="low",
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ low_lora_factory=_make_factory(log, "LOW"),
+ )
+ # Initial load: nothing to unload yet.
+ swapper.get(_ExpertSwapper.HIGH)
+ assert high_info._cache_record.cached_model.unload_calls == 0
+ assert low_info._cache_record.cached_model.unload_calls == 0
+
+ # Swap to LOW: HIGH must be force-unloaded; LOW is the incoming expert and
+ # must not be unloaded.
+ swapper.get(_ExpertSwapper.LOW)
+ assert high_info._cache_record.cached_model.unload_calls == 1
+ assert low_info._cache_record.cached_model.unload_calls == 0
+
+ # Swap back to HIGH: LOW must now be force-unloaded.
+ swapper.get(_ExpertSwapper.HIGH)
+ assert low_info._cache_record.cached_model.unload_calls == 1
+
+ swapper.close()
+
+
+def test_device_context_released_when_lora_enter_raises():
+ """Regression: if the LoRA patcher's ``__enter__`` raises, the device context
+ must still be released on the next swap or close.
+
+ Earlier shape stashed ``self._active_device_ctx`` only after the LoRA enter
+ succeeded, so an exception there left the device context entered but
+ unreachable — ``_release`` saw ``None`` and walked away, leaving 8–9 GB of
+ GGUF expert weights pinned to GPU until the model cache LRU evicted them."""
+ log: list[str] = []
+ high_nn = nn.Linear(1, 1)
+ ctx = _FakeContext({"high": _FakeInfo("HIGH", high_nn, log)}, log)
+
+ class _RaisingLoraStub:
+ def __init__(self, *args, **kwargs) -> None:
+ pass
+
+ def __enter__(self):
+ raise RuntimeError("LoRA patcher blew up")
+
+ def __exit__(self, *_args):
+ return False
+
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=lambda **_kwargs: _RaisingLoraStub(),
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model=None,
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ low_lora_factory=None,
+ )
+ with pytest.raises(RuntimeError, match="LoRA patcher blew up"):
+ swapper.get(_ExpertSwapper.HIGH)
+ # close() must succeed and must call the device context's __exit__ so
+ # the model leaves GPU. If the device context were unreachable,
+ # device-exit:HIGH would be missing from the log.
+ swapper.close()
+
+ assert "device-exit:HIGH" in log, "device context must be exited even if LoRA enter raised"
+
+
+def test_force_unload_failure_does_not_break_swap():
+ """If full_unload_from_vram raises (e.g. cache evicted the entry between unlock
+ and now), the swap must still succeed. Reaching into a private attribute is the
+ pragmatic choice today; this test pins the defensive try/except so a future
+ refactor of LoadedModel doesn't break swap reliability."""
+ log: list[str] = []
+
+ class _RaisingCachedModel:
+ def full_unload_from_vram(self):
+ raise RuntimeError("cache evicted me between unlock and unload")
+
+ raising_high = _FakeInfo("HIGH", nn.Linear(1, 1), log)
+ raising_high._cache_record = _FakeCacheRecord(_RaisingCachedModel())
+ low_info = _FakeInfo("LOW", nn.Linear(1, 1), log)
+ ctx = _FakeContext({"high": raising_high, "low": low_info}, log)
+
+ stub, _ = _stub_lora_context_manager(log)
+ with patch(
+ "invokeai.app.invocations.wan_denoise.LayerPatcher.apply_smart_model_patches",
+ side_effect=stub,
+ ):
+ swapper = _ExpertSwapper(
+ context=ctx,
+ high_model="high",
+ low_model="low",
+ inference_dtype=torch.bfloat16,
+ high_lora_factory=_make_factory(log, "HIGH"),
+ low_lora_factory=_make_factory(log, "LOW"),
+ )
+ swapper.get(_ExpertSwapper.HIGH)
+ # Should not raise even though the outgoing expert's full_unload throws.
+ model = swapper.get(_ExpertSwapper.LOW)
+ assert model is low_info._model
+ swapper.close()
diff --git a/tests/app/invocations/test_wan_ideal_dimensions.py b/tests/app/invocations/test_wan_ideal_dimensions.py
new file mode 100644
index 00000000000..e2944043848
--- /dev/null
+++ b/tests/app/invocations/test_wan_ideal_dimensions.py
@@ -0,0 +1,144 @@
+"""Unit tests for WanI2VIdealDimensionsInvocation.
+
+The node is a pure math transform — no context dependencies — so we can call
+``invoke`` with ``None`` directly.
+"""
+
+import pytest
+
+from invokeai.app.invocations.wan_ideal_dimensions import (
+ WAN_TARGET_RESOLUTION_PX,
+ WanI2VIdealDimensionsInvocation,
+)
+
+
+def _resolve(w: int, h: int, target: str = "720p", rounding: str = "nearest") -> tuple[int, int]:
+ inv = WanI2VIdealDimensionsInvocation(
+ width=w,
+ height=h,
+ target_resolution=target, # type: ignore[arg-type]
+ rounding=rounding, # type: ignore[arg-type]
+ )
+ out = inv.invoke(None) # type: ignore[arg-type]
+ return out.width, out.height
+
+
+class TestCommonResolutions:
+ """The output table from the docs."""
+
+ @pytest.mark.parametrize(
+ "w, h, target, expected",
+ [
+ (1920, 1080, "720p", (1280, 720)),
+ (1080, 1920, "720p", (720, 1280)),
+ (832, 480, "720p", (1248, 720)),
+ (4032, 3024, "720p", (960, 720)),
+ (3840, 2160, "720p", (1280, 720)),
+ (1024, 1024, "720p", (720, 720)),
+ (1920, 1080, "480p", (848, 480)),
+ (1920, 1080, "1080p", (1920, 1088)), # 1080 → snaps to 1088 (next multiple of 16)
+ ],
+ )
+ def test_nearest(self, w: int, h: int, target: str, expected: tuple[int, int]) -> None:
+ assert _resolve(w, h, target=target) == expected
+
+
+class TestRoundingModes:
+ """Floor / ceiling produce the expected over- or under-shoot vs. nearest."""
+
+ def test_floor_never_exceeds_raw(self) -> None:
+ # 1920x1080 → 480p has raw_w = 853.33; floor → 848, ceil → 864
+ assert _resolve(1920, 1080, target="480p", rounding="floor") == (848, 480)
+ assert _resolve(1920, 1080, target="480p", rounding="ceiling") == (864, 480)
+
+ def test_floor_and_ceiling_diverge_for_non_grid_aspect(self) -> None:
+ # 21:9-ish: 2048x858, raw_w = 1718.27 → floor 1712, ceil 1728
+ assert _resolve(2048, 858, target="720p", rounding="floor") == (1712, 720)
+ assert _resolve(2048, 858, target="720p", rounding="ceiling") == (1728, 720)
+
+
+class TestPostconditions:
+ """Output invariants that must always hold."""
+
+ @pytest.mark.parametrize(
+ "w, h, target",
+ [
+ (1920, 1080, "480p"),
+ (1920, 1080, "720p"),
+ (1080, 1920, "720p"),
+ (832, 480, "720p"),
+ (2048, 858, "720p"),
+ (4032, 3024, "480p"),
+ (17, 17, "720p"), # tiny input
+ ],
+ )
+ @pytest.mark.parametrize("rounding", ["nearest", "floor", "ceiling"])
+ def test_output_dims_are_multiples_of_16(self, w: int, h: int, target: str, rounding: str) -> None:
+ ow, oh = _resolve(w, h, target=target, rounding=rounding)
+ assert ow % 16 == 0
+ assert oh % 16 == 0
+
+ @pytest.mark.parametrize(
+ "w, h, target",
+ [
+ (1920, 1080, "720p"),
+ (1080, 1920, "720p"),
+ (832, 480, "720p"),
+ ],
+ )
+ def test_output_aspect_ratio_within_1_percent(self, w: int, h: int, target: str) -> None:
+ ow, oh = _resolve(w, h, target=target)
+ input_aspect = w / h
+ output_aspect = ow / oh
+ # 16-grid snap can shift aspect by at most half a 16-step on the long axis,
+ # which is ~1.1% at 720 short.
+ assert abs(output_aspect - input_aspect) / input_aspect < 0.012
+
+ def test_smallest_valid_input_still_snaps_to_16_grid(self) -> None:
+ # 16×16 is the minimum input the guard accepts. The downstream clamp ensures
+ # the output is at least 16×16 even when the floor rounding would zero it.
+ ow, oh = _resolve(16, 16, target="480p", rounding="floor")
+ assert ow >= 16
+ assert oh >= 16
+
+
+class TestResolutionPresetTable:
+ """The dropdown values must map to the documented short-side pixel counts."""
+
+ def test_presets_cover_canonical_video_sizes(self) -> None:
+ assert WAN_TARGET_RESOLUTION_PX == {"480p": 480, "720p": 720, "1080p": 1080}
+
+
+class TestInputValidation:
+ """Reject obviously bad inputs at the schema layer."""
+
+ def test_zero_width_rejected(self) -> None:
+ from pydantic import ValidationError
+
+ with pytest.raises(ValidationError):
+ WanI2VIdealDimensionsInvocation(width=0, height=720)
+
+ def test_negative_height_rejected(self) -> None:
+ from pydantic import ValidationError
+
+ with pytest.raises(ValidationError):
+ WanI2VIdealDimensionsInvocation(width=720, height=-1)
+
+ def test_input_smaller_than_pixel_grid_rejected(self) -> None:
+ # If the longer side is below the 16-px Wan grid, the floor-rounding output
+ # would silently disconnect from the requested aspect ratio (clamped to
+ # 16×16 regardless of the source's actual shape). Fail fast instead.
+ with pytest.raises(ValueError, match="smaller than the Wan pixel grid"):
+ _resolve(8, 8, target="480p", rounding="floor")
+ with pytest.raises(ValueError, match="smaller than the Wan pixel grid"):
+ _resolve(15, 15, target="720p", rounding="nearest")
+
+ def test_unknown_resolution_rejected(self) -> None:
+ from pydantic import ValidationError
+
+ with pytest.raises(ValidationError):
+ WanI2VIdealDimensionsInvocation(
+ width=1920,
+ height=1080,
+ target_resolution="2160p", # type: ignore[arg-type]
+ )
diff --git a/tests/app/invocations/test_wan_latents_to_video_encoding.py b/tests/app/invocations/test_wan_latents_to_video_encoding.py
new file mode 100644
index 00000000000..0ee27acf1e3
--- /dev/null
+++ b/tests/app/invocations/test_wan_latents_to_video_encoding.py
@@ -0,0 +1,18 @@
+from unittest.mock import MagicMock
+
+import numpy as np
+import pytest
+
+from invokeai.app.invocations.wan_latents_to_video import _write_video_frames
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+
+
+def test_video_encoding_stops_when_canceled() -> None:
+ writer = MagicMock()
+ frames = np.zeros((5, 4, 4, 3), dtype=np.uint8)
+ checks = iter([False, False, True])
+
+ with pytest.raises(CanceledException):
+ _write_video_frames(writer, frames, lambda: next(checks))
+
+ assert writer.append_data.call_count == 2
diff --git a/tests/app/invocations/test_wan_lora_loader.py b/tests/app/invocations/test_wan_lora_loader.py
new file mode 100644
index 00000000000..ce250eff86d
--- /dev/null
+++ b/tests/app/invocations/test_wan_lora_loader.py
@@ -0,0 +1,225 @@
+"""Tests for ``WanLoRALoaderInvocation`` target resolution and routing."""
+
+from unittest.mock import MagicMock
+
+import pytest
+
+from invokeai.app.invocations.model import LoRAField, ModelIdentifierField, WanTransformerField
+from invokeai.app.invocations.wan_lora_loader import (
+ WanLoRACollectionLoader,
+ WanLoRALoaderInvocation,
+ _resolve_target,
+)
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType
+
+# --------------------------------------------------------------------------
+# _resolve_target — pure function, no mocks needed.
+# --------------------------------------------------------------------------
+
+
+class TestResolveTarget:
+ @pytest.mark.parametrize(
+ "target, expert, expected",
+ [
+ ("auto", None, (True, True)),
+ ("auto", "high", (True, False)),
+ ("auto", "low", (False, True)),
+ ("both", None, (True, True)),
+ ("both", "high", (True, True)),
+ ("both", "low", (True, True)),
+ ("high", None, (True, False)),
+ ("high", "low", (True, False)), # explicit target overrides config
+ ("low", None, (False, True)),
+ ("low", "high", (False, True)),
+ ],
+ )
+ def test_target_resolution(self, target, expert, expected):
+ assert _resolve_target(target, expert) == expected
+
+
+# --------------------------------------------------------------------------
+# WanLoRALoaderInvocation — routing into primary vs low-noise lists.
+# --------------------------------------------------------------------------
+
+
+def _make_lora_field(key: str = "lora-1") -> ModelIdentifierField:
+ return ModelIdentifierField(
+ key=key,
+ hash=f"hash-{key}",
+ name=f"name-{key}",
+ base=BaseModelType.Wan,
+ type=ModelType.LoRA,
+ )
+
+
+def _make_transformer_field() -> WanTransformerField:
+ transformer_id = ModelIdentifierField(
+ key="wan-main",
+ hash="wan-main-hash",
+ name="wan-main",
+ base=BaseModelType.Wan,
+ type=ModelType.Main,
+ )
+ return WanTransformerField(transformer=transformer_id)
+
+
+def _make_context(lora_expert: str | None) -> MagicMock:
+ """Mock context where context.models.get_config(lora).expert == lora_expert
+ and context.models.exists returns True for any lora key."""
+ ctx = MagicMock()
+ ctx.models.exists.return_value = True
+ config = MagicMock()
+ config.expert = lora_expert
+ ctx.models.get_config.return_value = config
+ return ctx
+
+
+class TestSingleLoaderRouting:
+ def test_auto_untagged_goes_to_both(self):
+ inv = WanLoRALoaderInvocation(id="inv-1", lora=_make_lora_field(), transformer=_make_transformer_field())
+ out = inv.invoke(_make_context(lora_expert=None))
+ assert out.transformer is not None
+ assert len(out.transformer.loras) == 1
+ assert len(out.transformer.loras_low_noise) == 1
+
+ def test_auto_high_tag_goes_to_primary_only(self):
+ inv = WanLoRALoaderInvocation(id="inv-1", lora=_make_lora_field(), transformer=_make_transformer_field())
+ out = inv.invoke(_make_context(lora_expert="high"))
+ assert out.transformer is not None
+ assert len(out.transformer.loras) == 1
+ assert len(out.transformer.loras_low_noise) == 0
+
+ def test_auto_low_tag_goes_to_low_only(self):
+ inv = WanLoRALoaderInvocation(id="inv-1", lora=_make_lora_field(), transformer=_make_transformer_field())
+ out = inv.invoke(_make_context(lora_expert="low"))
+ assert out.transformer is not None
+ assert len(out.transformer.loras) == 0
+ assert len(out.transformer.loras_low_noise) == 1
+
+ def test_explicit_target_overrides_tag(self):
+ inv = WanLoRALoaderInvocation(
+ id="inv-1",
+ lora=_make_lora_field(),
+ target="high",
+ transformer=_make_transformer_field(),
+ )
+ out = inv.invoke(_make_context(lora_expert="low"))
+ assert out.transformer is not None
+ assert len(out.transformer.loras) == 1
+ assert len(out.transformer.loras_low_noise) == 0
+
+ def test_weight_propagates(self):
+ inv = WanLoRALoaderInvocation(
+ id="inv-1",
+ lora=_make_lora_field(),
+ weight=0.42,
+ transformer=_make_transformer_field(),
+ )
+ out = inv.invoke(_make_context(lora_expert=None))
+ assert out.transformer is not None
+ assert out.transformer.loras[0].weight == pytest.approx(0.42)
+
+ def test_unknown_lora_raises(self):
+ ctx = _make_context(lora_expert=None)
+ ctx.models.exists.return_value = False
+ inv = WanLoRALoaderInvocation(id="inv-1", lora=_make_lora_field(), transformer=_make_transformer_field())
+ with pytest.raises(ValueError, match="Unknown lora"):
+ inv.invoke(ctx)
+
+ def test_duplicate_on_primary_raises(self):
+ existing = LoRAField(lora=_make_lora_field(key="dup"), weight=0.5)
+ transformer = _make_transformer_field()
+ transformer.loras.append(existing)
+
+ inv = WanLoRALoaderInvocation(id="inv-1", lora=_make_lora_field(key="dup"), transformer=transformer)
+ with pytest.raises(ValueError, match="already applied to primary"):
+ inv.invoke(_make_context(lora_expert="high"))
+
+ def test_duplicate_on_low_noise_raises(self):
+ existing = LoRAField(lora=_make_lora_field(key="dup"), weight=0.5)
+ transformer = _make_transformer_field()
+ transformer.loras_low_noise.append(existing)
+
+ inv = WanLoRALoaderInvocation(id="inv-1", lora=_make_lora_field(key="dup"), transformer=transformer)
+ with pytest.raises(ValueError, match="already applied to low-noise"):
+ inv.invoke(_make_context(lora_expert="low"))
+
+ def test_no_transformer_returns_empty_output(self):
+ inv = WanLoRALoaderInvocation(id="inv-1", lora=_make_lora_field(), transformer=None)
+ out = inv.invoke(_make_context(lora_expert=None))
+ assert out.transformer is None
+
+
+# --------------------------------------------------------------------------
+# Collection loader — list routing + base validation.
+# --------------------------------------------------------------------------
+
+
+class TestCollectionLoaderRouting:
+ def test_routes_mixed_tagged_loras(self):
+ """A collection of three LoRAs (high, low, untagged) routes correctly."""
+ high_lora = LoRAField(lora=_make_lora_field(key="lora-high"), weight=0.5)
+ low_lora = LoRAField(lora=_make_lora_field(key="lora-low"), weight=0.6)
+ untagged_lora = LoRAField(lora=_make_lora_field(key="lora-any"), weight=0.7)
+
+ inv = WanLoRACollectionLoader(
+ id="inv-1",
+ loras=[high_lora, low_lora, untagged_lora],
+ transformer=_make_transformer_field(),
+ )
+
+ # The collection loader queries each LoRA's config separately. Mock
+ # get_config to return different expert tags by lora key.
+ expert_by_key = {"lora-high": "high", "lora-low": "low", "lora-any": None}
+ ctx = MagicMock()
+ ctx.models.exists.return_value = True
+
+ def get_config(field: ModelIdentifierField) -> MagicMock:
+ config = MagicMock()
+ config.expert = expert_by_key[field.key]
+ return config
+
+ ctx.models.get_config.side_effect = get_config
+ out = inv.invoke(ctx)
+ assert out.transformer is not None
+
+ primary_keys = {item.lora.key for item in out.transformer.loras}
+ low_keys = {item.lora.key for item in out.transformer.loras_low_noise}
+ # high -> primary only; low -> low only; untagged -> both
+ assert primary_keys == {"lora-high", "lora-any"}
+ assert low_keys == {"lora-low", "lora-any"}
+
+ def test_rejects_non_wan_base(self):
+ wrong_base_lora = LoRAField(
+ lora=ModelIdentifierField(key="not-wan", hash="h", name="n", base=BaseModelType.Flux, type=ModelType.LoRA),
+ weight=0.5,
+ )
+ inv = WanLoRACollectionLoader(id="inv-1", loras=[wrong_base_lora], transformer=_make_transformer_field())
+ ctx = MagicMock()
+ ctx.models.exists.return_value = True
+ with pytest.raises(ValueError, match="not Wan 2.2"):
+ inv.invoke(ctx)
+
+ def test_skips_duplicates(self):
+ dup_lora = LoRAField(lora=_make_lora_field(key="dup"), weight=0.5)
+ inv = WanLoRACollectionLoader(
+ id="inv-1",
+ loras=[dup_lora, dup_lora],
+ transformer=_make_transformer_field(),
+ )
+ ctx = MagicMock()
+ ctx.models.exists.return_value = True
+ config = MagicMock()
+ config.expert = None
+ ctx.models.get_config.return_value = config
+
+ out = inv.invoke(ctx)
+ assert out.transformer is not None
+ assert len(out.transformer.loras) == 1
+
+ def test_no_loras_returns_clean_copy(self):
+ inv = WanLoRACollectionLoader(id="inv-1", loras=None, transformer=_make_transformer_field())
+ out = inv.invoke(MagicMock())
+ assert out.transformer is not None
+ assert len(out.transformer.loras) == 0
+ assert len(out.transformer.loras_low_noise) == 0
diff --git a/tests/app/invocations/test_wan_ti2v_ideal_dimensions.py b/tests/app/invocations/test_wan_ti2v_ideal_dimensions.py
new file mode 100644
index 00000000000..80a91b3e967
--- /dev/null
+++ b/tests/app/invocations/test_wan_ti2v_ideal_dimensions.py
@@ -0,0 +1,154 @@
+"""Unit tests for WanTI2VIdealDimensionsInvocation.
+
+Mirrors ``test_wan_ideal_dimensions.py`` but for the TI2V-5B variant, which
+snaps to a multiple of 32 (16x Wan 2.2-VAE × 2x transformer patch) instead of
+16. The node is a pure math transform — no context dependencies — so we can
+call ``invoke`` with ``None`` directly.
+"""
+
+import pytest
+
+from invokeai.app.invocations.wan_ideal_dimensions import (
+ WAN_TARGET_RESOLUTION_PX,
+ WAN_TI2V_PIXEL_MULTIPLE,
+ WanTI2VIdealDimensionsInvocation,
+)
+
+
+def _resolve(w: int, h: int, target: str = "720p", rounding: str = "nearest") -> tuple[int, int]:
+ inv = WanTI2VIdealDimensionsInvocation(
+ width=w,
+ height=h,
+ target_resolution=target, # type: ignore[arg-type]
+ rounding=rounding, # type: ignore[arg-type]
+ )
+ out = inv.invoke(None) # type: ignore[arg-type]
+ return out.width, out.height
+
+
+class TestCommonResolutions:
+ """Verified output table for the 32-px grid."""
+
+ @pytest.mark.parametrize(
+ "w, h, target, expected",
+ [
+ (1920, 1080, "720p", (1280, 704)),
+ (1080, 1920, "720p", (704, 1280)),
+ (832, 480, "720p", (1248, 704)),
+ (4032, 3024, "720p", (960, 704)),
+ (3840, 2160, "720p", (1280, 704)),
+ (1024, 1024, "720p", (704, 704)),
+ (1920, 1080, "480p", (864, 480)),
+ (1920, 1080, "1080p", (1920, 1088)), # 1080 → 1088 (next multiple of 32)
+ # The reported failing case: 720x480 is not a multiple of 32 (720 % 32 != 0);
+ # the node snaps it to valid dims at both presets.
+ (720, 480, "480p", (704, 480)),
+ (720, 480, "720p", (1088, 704)),
+ ],
+ )
+ def test_nearest(self, w: int, h: int, target: str, expected: tuple[int, int]) -> None:
+ assert _resolve(w, h, target=target) == expected
+
+
+class TestRoundingModes:
+ """Floor / ceiling produce the expected over- or under-shoot vs. nearest."""
+
+ def test_floor_never_exceeds_raw(self) -> None:
+ # 1920x1080 → 480p has raw_w = 853.33; floor → 832, ceil → 864
+ assert _resolve(1920, 1080, target="480p", rounding="floor") == (832, 480)
+ assert _resolve(1920, 1080, target="480p", rounding="ceiling") == (864, 480)
+
+ def test_floor_and_ceiling_diverge_for_non_grid_aspect(self) -> None:
+ # 2048x858, raw_w = 1718.6 → floor 1696, ceil 1728
+ assert _resolve(2048, 858, target="720p", rounding="floor") == (1696, 704)
+ assert _resolve(2048, 858, target="720p", rounding="ceiling") == (1728, 736)
+
+
+class TestPostconditions:
+ """Output invariants that must always hold."""
+
+ @pytest.mark.parametrize(
+ "w, h, target",
+ [
+ (1920, 1080, "480p"),
+ (1920, 1080, "720p"),
+ (1080, 1920, "720p"),
+ (832, 480, "720p"),
+ (2048, 858, "720p"),
+ (4032, 3024, "480p"),
+ (720, 480, "720p"),
+ (33, 33, "720p"), # tiny input
+ ],
+ )
+ @pytest.mark.parametrize("rounding", ["nearest", "floor", "ceiling"])
+ def test_output_dims_are_multiples_of_32(self, w: int, h: int, target: str, rounding: str) -> None:
+ ow, oh = _resolve(w, h, target=target, rounding=rounding)
+ assert ow % 32 == 0
+ assert oh % 32 == 0
+
+ @pytest.mark.parametrize(
+ "w, h, target",
+ [
+ (1920, 1080, "720p"),
+ (1080, 1920, "720p"),
+ (832, 480, "720p"),
+ ],
+ )
+ def test_output_aspect_ratio_within_2_percent(self, w: int, h: int, target: str) -> None:
+ ow, oh = _resolve(w, h, target=target)
+ input_aspect = w / h
+ output_aspect = ow / oh
+ # 32-grid snap can shift aspect by at most half a 32-step on the long axis,
+ # which is ~2.2% at 704 short — looser than the I2V node's 16-grid tolerance.
+ assert abs(output_aspect - input_aspect) / input_aspect < 0.023
+
+ def test_smallest_valid_input_still_snaps_to_32_grid(self) -> None:
+ # 32×32 is the minimum input the guard accepts. The downstream clamp ensures
+ # the output is at least 32×32 even when floor rounding would zero it.
+ ow, oh = _resolve(32, 32, target="480p", rounding="floor")
+ assert ow >= 32
+ assert oh >= 32
+
+
+class TestResolutionPresetTable:
+ """The dropdown values must map to the documented short-side pixel counts."""
+
+ def test_presets_cover_canonical_video_sizes(self) -> None:
+ assert WAN_TARGET_RESOLUTION_PX == {"480p": 480, "720p": 720, "1080p": 1080}
+
+ def test_pixel_multiple_is_32(self) -> None:
+ assert WAN_TI2V_PIXEL_MULTIPLE == 32
+
+
+class TestInputValidation:
+ """Reject obviously bad inputs at the schema layer."""
+
+ def test_zero_width_rejected(self) -> None:
+ from pydantic import ValidationError
+
+ with pytest.raises(ValidationError):
+ WanTI2VIdealDimensionsInvocation(width=0, height=720)
+
+ def test_negative_height_rejected(self) -> None:
+ from pydantic import ValidationError
+
+ with pytest.raises(ValidationError):
+ WanTI2VIdealDimensionsInvocation(width=720, height=-1)
+
+ def test_input_smaller_than_pixel_grid_rejected(self) -> None:
+ # If the longer side is below the 32-px TI2V grid, the floor-rounding output
+ # would silently disconnect from the requested aspect ratio. Fail fast instead.
+ with pytest.raises(ValueError, match="smaller than the Wan pixel grid"):
+ _resolve(16, 16, target="480p", rounding="floor")
+ with pytest.raises(ValueError, match="smaller than the Wan pixel grid"):
+ _resolve(31, 31, target="720p", rounding="nearest")
+
+ def test_unknown_resolution_rejected(self) -> None:
+ from pydantic import ValidationError
+
+ with pytest.raises(ValidationError):
+ WanTI2VIdealDimensionsInvocation(
+ width=1920,
+ height=1080,
+ target_resolution="2160p", # type: ignore[arg-type]
+ )
diff --git a/tests/app/routers/test_auth.py b/tests/app/routers/test_auth.py
index 5362bd775ff..46ea5862db2 100644
--- a/tests/app/routers/test_auth.py
+++ b/tests/app/routers/test_auth.py
@@ -97,6 +97,9 @@ def test_login_success(monkeypatch: Any, mock_invoker: Invoker, client: TestClie
assert "expires_in" in json_response
assert json_response["user"]["email"] == "test@example.com"
assert json_response["user"]["is_admin"] is False
+ assert response.cookies.get("invokeai_media_token") == json_response["token"]
+ assert "HttpOnly" in response.headers["set-cookie"]
+ assert "Path=/api/v1/videos" in response.headers["set-cookie"]
def test_login_with_remember_me(monkeypatch: Any, mock_invoker: Invoker, client: TestClient) -> None:
diff --git a/tests/app/routers/test_boards_multiuser.py b/tests/app/routers/test_boards_multiuser.py
index ab64ac8a9b4..a43216a2a4c 100644
--- a/tests/app/routers/test_boards_multiuser.py
+++ b/tests/app/routers/test_boards_multiuser.py
@@ -75,6 +75,26 @@ def enable_multiuser_for_tests(monkeypatch: Any, mock_invoker: Invoker):
mock_board_images.get_all_board_image_names_for_board.return_value = []
mock_invoker.services.board_images = mock_board_images
+ # delete_board cascade-deletes videos on the board too — stub the video services
+ # so the route doesn't hit AttributeError on the None placeholders in mock_services.
+ mock_board_video_records = MagicMock()
+ mock_board_video_records.get_all_board_video_names_for_board.return_value = []
+ mock_board_video_records.get_video_count_for_board.return_value = 0
+ mock_invoker.services.board_video_records = mock_board_video_records
+ # The board service also consults video_records for cover-image selection (most recent video).
+ mock_video_records = MagicMock()
+ mock_video_records.get_most_recent_video_for_board.return_value = None
+ mock_invoker.services.video_records = mock_video_records
+ mock_invoker.services.videos = MagicMock()
+ # delete_videos_on_board now returns the authoritative ``deleted_videos`` list (only the
+ # videos whose file deletion actually succeeded). Default to an empty list so pydantic
+ # validation on ``DeleteBoardResult`` doesn't reject the MagicMock auto-return.
+ mock_invoker.services.videos.delete_videos_on_board.return_value = []
+ # The images service is a real ImageService instance in mock_services; the delete-board
+ # cascade calls ``delete_images_on_board`` on it, which fails without an initialized
+ # invoker. Stub it so the multiuser router tests can assert the cascade args.
+ mock_invoker.services.images = MagicMock()
+
mock_deps = MockApiDependencies(mock_invoker)
monkeypatch.setattr("invokeai.app.api.routers.auth.ApiDependencies", mock_deps)
monkeypatch.setattr("invokeai.app.api.auth_dependencies.ApiDependencies", mock_deps)
@@ -675,3 +695,164 @@ def test_admin_can_change_any_board_visibility(client: TestClient, admin_token:
)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["board_visibility"] == "public"
+
+
+# ---------------------------------------------------------------------------
+# Video cascade on board deletion (PR #9163 review fix)
+# ---------------------------------------------------------------------------
+
+
+def test_delete_board_with_include_images_cascades_videos(client: TestClient, mock_invoker: Invoker, user1_token: str):
+ """include_images=true must also call delete_videos_on_board (not image-only)."""
+ create = client.post(
+ "/api/v1/boards/?board_name=Cascade+Test+Board",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert create.status_code == status.HTTP_201_CREATED
+ board_id = create.json()["board_id"]
+
+ # The cascade returns the names of videos it actually deleted; the router must surface
+ # *that* list (not the pre-delete enumeration) so the response can't claim a video was
+ # destroyed when its DB record was preserved due to a file-delete failure.
+ mock_invoker.services.videos.delete_videos_on_board.return_value = ["video_a.mp4", "video_b.mp4"]
+
+ response = client.delete(
+ f"/api/v1/boards/{board_id}?include_images=true",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ body = response.json()
+ assert body["board_id"] == board_id
+ assert set(body["deleted_videos"]) == {"video_a.mp4", "video_b.mp4"}
+ mock_invoker.services.videos.delete_videos_on_board.assert_called_once()
+ call = mock_invoker.services.videos.delete_videos_on_board.call_args
+ assert call.kwargs["board_id"] == board_id
+
+
+def test_delete_board_with_partial_video_file_delete_failure_reports_only_actual_deletes(
+ client: TestClient, mock_invoker: Invoker, user1_token: str
+):
+ """JPPhoto PR #9163 May-22 follow-up: ``delete_board`` previously reported every video
+ that *would have been* deleted, but ``delete_videos_on_board`` now intentionally preserves
+ records whose backing files failed to delete (so the file isn't orphaned on disk). The
+ response must therefore report only the videos that were actually destroyed; preserved
+ records cascade to "uncategorized" via the board_videos FK and the frontend must learn
+ they still exist so the cache stays consistent.
+ """
+ create = client.post(
+ "/api/v1/boards/?board_name=Partial+Failure+Board",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert create.status_code == status.HTTP_201_CREATED
+ board_id = create.json()["board_id"]
+
+ # The service deleted "good.mp4" successfully but preserved "stuck.mp4" because its
+ # file delete failed. The router must NOT claim "stuck.mp4" was deleted.
+ mock_invoker.services.videos.delete_videos_on_board.return_value = ["good.mp4"]
+
+ response = client.delete(
+ f"/api/v1/boards/{board_id}?include_images=true",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ body = response.json()
+ assert body["deleted_videos"] == ["good.mp4"]
+ assert "stuck.mp4" not in body["deleted_videos"]
+
+
+def test_delete_board_with_include_images_filters_cascade_by_user(
+ client: TestClient, mock_invoker: Invoker, user1_token: str
+):
+ """A non-admin board owner deleting a public/shared board with include_images=True must
+ only destroy videos and images they themselves uploaded — other users' contributions are
+ preserved (they fall through to ``uncategorized`` via the FK cascade on board_videos /
+ board_images).
+
+ This guards against a privilege-escalation gap: previously the cascade iterated *all*
+ board_videos rows for the board_id regardless of uploader, letting the board owner
+ delete other users' files just by deleting the board.
+ """
+ create = client.post(
+ "/api/v1/boards/?board_name=Public+Cascade+Board",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert create.status_code == status.HTTP_201_CREATED
+ board_id = create.json()["board_id"]
+
+ response = client.delete(
+ f"/api/v1/boards/{board_id}?include_images=true",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+
+ # The router must pass the current user's id through to the cascade so the SQL filter
+ # narrows the lookup + delete to that user's rows only. Admins skip the filter (pass
+ # ``user_id=None``); this test exercises the non-admin path.
+ images_call = mock_invoker.services.board_images.get_all_board_image_names_for_board.call_args
+ assert images_call.kwargs.get("user_id") is not None
+
+ delete_images_call = mock_invoker.services.images.delete_images_on_board.call_args
+ delete_videos_call = mock_invoker.services.videos.delete_videos_on_board.call_args
+ assert delete_images_call.kwargs.get("user_id") is not None
+ assert delete_videos_call.kwargs.get("user_id") is not None
+ # Sanity: the three cascade calls must all share the same user_id (the requester).
+ requester_id = images_call.kwargs["user_id"]
+ assert delete_images_call.kwargs["user_id"] == requester_id
+ assert delete_videos_call.kwargs["user_id"] == requester_id
+
+
+def test_delete_board_with_include_images_admin_skips_user_filter(
+ client: TestClient, mock_invoker: Invoker, admin_token: str
+):
+ """Admins keep the legacy behavior: cascade deletes everything on the board regardless of
+ uploader. The filter is bypassed by passing ``user_id=None`` so the SQL ``WHERE`` clause
+ has no per-user restriction.
+ """
+ create = client.post(
+ "/api/v1/boards/?board_name=Admin+Cascade+Board",
+ headers={"Authorization": f"Bearer {admin_token}"},
+ )
+ assert create.status_code == status.HTTP_201_CREATED
+ board_id = create.json()["board_id"]
+
+ response = client.delete(
+ f"/api/v1/boards/{board_id}?include_images=true",
+ headers={"Authorization": f"Bearer {admin_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+
+ images_call = mock_invoker.services.board_images.get_all_board_image_names_for_board.call_args
+ assert images_call.kwargs.get("user_id") is None
+
+ delete_images_call = mock_invoker.services.images.delete_images_on_board.call_args
+ delete_videos_call = mock_invoker.services.videos.delete_videos_on_board.call_args
+ assert delete_images_call.kwargs.get("user_id") is None
+ assert delete_videos_call.kwargs.get("user_id") is None
+
+
+def test_delete_board_without_include_images_lists_uncategorized_videos(
+ client: TestClient, mock_invoker: Invoker, user1_token: str
+):
+ """include_images=false: videos cascade out of board_videos and become uncategorized.
+
+ The response now reports those names in deleted_board_videos so the frontend can
+ invalidate the right caches.
+ """
+ create = client.post(
+ "/api/v1/boards/?board_name=Soft+Delete+Board",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert create.status_code == status.HTTP_201_CREATED
+ board_id = create.json()["board_id"]
+
+ mock_invoker.services.board_video_records.get_all_board_video_names_for_board.return_value = ["v1.mp4"]
+
+ response = client.delete(
+ f"/api/v1/boards/{board_id}",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ body = response.json()
+ assert body["deleted_board_videos"] == ["v1.mp4"]
+ # delete_videos_on_board MUST NOT be invoked when include_images is false.
+ mock_invoker.services.videos.delete_videos_on_board.assert_not_called()
diff --git a/tests/app/routers/test_image_moves.py b/tests/app/routers/test_image_moves.py
index 697447269c3..1dd6066b17a 100644
--- a/tests/app/routers/test_image_moves.py
+++ b/tests/app/routers/test_image_moves.py
@@ -10,6 +10,7 @@
from invokeai.app.services.auth.token_service import set_jwt_secret
from invokeai.app.services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
from invokeai.app.services.board_records.board_records_sqlite import SqliteBoardRecordStorage
+from invokeai.app.services.board_video_records.board_video_records_sqlite import SqliteBoardVideoRecordStorage
from invokeai.app.services.boards.boards_default import BoardService
from invokeai.app.services.bulk_download.bulk_download_default import BulkDownloadService
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import ClientStatePersistenceSqlite
@@ -23,6 +24,7 @@
from invokeai.app.services.invoker import Invoker
from invokeai.app.services.users.users_common import UserCreateRequest
from invokeai.app.services.users.users_default import UserService
+from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
from invokeai.app.services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
from invokeai.backend.util.logging import InvokeAILogger
from tests.fixtures.sqlite_database import create_mock_sqlite_database
@@ -79,6 +81,11 @@ def mock_services() -> InvocationServices:
model_relationships=None, # type: ignore
client_state_persistence=ClientStatePersistenceSqlite(db=db),
users=UserService(db),
+ videos=None, # type: ignore
+ video_files=None, # type: ignore
+ video_records=SqliteVideoRecordStorage(db=db),
+ board_video_records=SqliteBoardVideoRecordStorage(db=db),
+ gallery=None, # type: ignore
image_moves=image_moves,
)
diff --git a/tests/app/routers/test_multiuser_authorization.py b/tests/app/routers/test_multiuser_authorization.py
index 5a7e673b18f..59a2fb4deb3 100644
--- a/tests/app/routers/test_multiuser_authorization.py
+++ b/tests/app/routers/test_multiuser_authorization.py
@@ -68,6 +68,7 @@ def client():
def mock_services() -> InvocationServices:
from invokeai.app.services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
from invokeai.app.services.board_records.board_records_sqlite import SqliteBoardRecordStorage
+ from invokeai.app.services.board_video_records.board_video_records_sqlite import SqliteBoardVideoRecordStorage
from invokeai.app.services.boards.boards_default import BoardService
from invokeai.app.services.bulk_download.bulk_download_default import BulkDownloadService
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import (
@@ -78,6 +79,7 @@ def mock_services() -> InvocationServices:
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
from invokeai.app.services.invocation_stats.invocation_stats_default import InvocationStatsService
from invokeai.app.services.users.users_default import UserService
+ from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
from tests.test_nodes import TestEventService
configuration = InvokeAIAppConfig(use_memory_db=True, node_cache_size=0)
@@ -116,6 +118,11 @@ def mock_services() -> InvocationServices:
client_state_persistence=ClientStatePersistenceSqlite(db=db),
users=UserService(db),
external_generation=None, # type: ignore
+ videos=None, # type: ignore
+ video_files=None, # type: ignore
+ video_records=SqliteVideoRecordStorage(db=db),
+ board_video_records=SqliteBoardVideoRecordStorage(db=db),
+ gallery=None, # type: ignore
)
@@ -689,6 +696,11 @@ def test_non_owner_cannot_star_image(
def test_non_owner_cannot_batch_delete_image(
self, client: TestClient, mock_invoker: Invoker, user1_token: str, user2_token: str
):
+ """Batch delete tolerates foreign items by skipping them in-loop and returning the
+ per-item delete list to the client. Previously the route re-raised the first 403,
+ dropping any partial successes — see PR #9163 review (finding 3). The non-owner
+ must still not destroy the image; only the response shape changes.
+ """
user1 = mock_invoker.services.users.get_by_email("user1@test.com")
assert user1 is not None
_save_image(mock_invoker, "user1-batch-del", user1.user_id)
@@ -698,7 +710,12 @@ def test_non_owner_cannot_batch_delete_image(
json={"image_names": ["user1-batch-del"]},
headers=_auth(user2_token),
)
- assert r.status_code == status.HTTP_403_FORBIDDEN
+ assert r.status_code == status.HTTP_200_OK
+ body = r.json()
+ # Auth failure must not advertise the foreign image as deleted, and the underlying
+ # record must still exist.
+ assert body["deleted_images"] == []
+ mock_invoker.services.image_records.get("user1-batch-del")
def test_non_owner_can_delete_image_from_public_board(
self, client: TestClient, mock_invoker: Invoker, user1_token: str, user2_token: str
diff --git a/tests/app/routers/test_session_queue_workflow_call.py b/tests/app/routers/test_session_queue_workflow_call.py
index d8fd465d8d3..69be937125f 100644
--- a/tests/app/routers/test_session_queue_workflow_call.py
+++ b/tests/app/routers/test_session_queue_workflow_call.py
@@ -45,6 +45,7 @@ def client():
def mock_services() -> InvocationServices:
from invokeai.app.services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
from invokeai.app.services.board_records.board_records_sqlite import SqliteBoardRecordStorage
+ from invokeai.app.services.board_video_records.board_video_records_sqlite import SqliteBoardVideoRecordStorage
from invokeai.app.services.boards.boards_default import BoardService
from invokeai.app.services.bulk_download.bulk_download_default import BulkDownloadService
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import (
@@ -55,6 +56,7 @@ def mock_services() -> InvocationServices:
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
from invokeai.app.services.invocation_stats.invocation_stats_default import InvocationStatsService
from invokeai.app.services.users.users_default import UserService
+ from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
from tests.test_nodes import TestEventService
configuration = InvokeAIAppConfig(use_memory_db=True, node_cache_size=0)
@@ -93,6 +95,11 @@ def mock_services() -> InvocationServices:
client_state_persistence=ClientStatePersistenceSqlite(db=db),
users=UserService(db),
external_generation=None, # type: ignore
+ videos=None, # type: ignore
+ video_files=None, # type: ignore
+ video_records=SqliteVideoRecordStorage(db=db),
+ board_video_records=SqliteBoardVideoRecordStorage(db=db),
+ gallery=None, # type: ignore
)
diff --git a/tests/app/routers/test_videos_multiuser.py b/tests/app/routers/test_videos_multiuser.py
new file mode 100644
index 00000000000..bb07c1adf5a
--- /dev/null
+++ b/tests/app/routers/test_videos_multiuser.py
@@ -0,0 +1,610 @@
+"""Multiuser regression tests for the /v1/videos/ routes.
+
+Covers JPPhoto's code-review finding (PR #9163): the list endpoints accepted
+an explicit ``board_id`` with no read-access check, so a non-admin user could
+enumerate videos on someone else's private board if they happened to know its
+id. The fix added ``_assert_board_read_access`` to both ``list_video_dtos``
+and ``get_video_names``.
+
+These tests exercise the HTTP layer end-to-end (auth + route guards) using the
+same fixture pattern as test_boards_multiuser. The storage-level user_id
+filter is covered separately in tests/app/services/video_records.
+"""
+
+from pathlib import Path
+from typing import Any
+from unittest.mock import MagicMock, patch
+
+import pytest
+from fastapi import status
+from fastapi.testclient import TestClient
+
+from invokeai.app.api.dependencies import ApiDependencies
+from invokeai.app.api.routers.videos import _is_mp4_file
+from invokeai.app.api_app import app
+from invokeai.app.services.invoker import Invoker
+from invokeai.app.services.users.users_common import UserCreateRequest
+
+
+class MockApiDependencies(ApiDependencies):
+ invoker: Invoker
+
+ def __init__(self, invoker: Invoker) -> None:
+ self.invoker = invoker
+
+
+@pytest.fixture
+def setup_jwt_secret():
+ from invokeai.app.services.auth.token_service import set_jwt_secret
+
+ set_jwt_secret("test-secret-key-for-unit-tests-only-do-not-use-in-production")
+
+
+@pytest.fixture
+def client():
+ return TestClient(app)
+
+
+def setup_test_user(
+ mock_invoker: Invoker,
+ email: str,
+ display_name: str,
+ password: str = "TestPass123",
+ is_admin: bool = False,
+) -> str:
+ user_service = mock_invoker.services.users
+ user = user_service.create(
+ UserCreateRequest(email=email, display_name=display_name, password=password, is_admin=is_admin)
+ )
+ return user.user_id
+
+
+def get_user_token(client: TestClient, email: str, password: str = "TestPass123") -> str:
+ response = client.post(
+ "/api/v1/auth/login",
+ json={"email": email, "password": password, "remember_me": False},
+ )
+ assert response.status_code == 200
+ return response.json()["token"]
+
+
+@pytest.fixture
+def enable_multiuser_for_videos(monkeypatch: Any, mock_invoker: Invoker):
+ """Enable multiuser and stub services the video routes touch."""
+ mock_invoker.services.configuration.multiuser = True
+
+ # The list routes call services.videos.get_many / get_video_names. We don't care about
+ # the payloads here — only whether the route runs the board-access guard *before* the
+ # service call. A return value of "any non-error response" is enough.
+ mock_videos = MagicMock()
+ mock_videos.get_many.return_value = {"items": [], "offset": 0, "limit": 10, "total": 0}
+ mock_videos.get_video_names.return_value = {"video_names": [], "starred_count": 0, "total_count": 0}
+ mock_invoker.services.videos = mock_videos
+
+ # board_video_records is touched by remove_video_from_board; not exercised by the
+ # list tests but stub it defensively so unrelated routes don't blow up.
+ mock_invoker.services.board_video_records = MagicMock()
+ # The board service computes video_count + cover_video_name on every get_dto/update;
+ # an unconfigured MagicMock returns nested MagicMocks that fail Pydantic validation and
+ # the boards route swallows the exception as a 404. Pin sane defaults.
+ mock_invoker.services.board_video_records.get_video_count_for_board.return_value = 0
+ mock_invoker.services.video_records = MagicMock()
+ mock_invoker.services.video_records.get_most_recent_video_for_board.return_value = None
+ mock_invoker.services.board_images = MagicMock()
+ mock_invoker.services.board_images.get_all_board_image_names_for_board.return_value = []
+
+ mock_deps = MockApiDependencies(mock_invoker)
+ monkeypatch.setattr("invokeai.app.api.routers.auth.ApiDependencies", mock_deps)
+ monkeypatch.setattr("invokeai.app.api.auth_dependencies.ApiDependencies", mock_deps)
+ monkeypatch.setattr("invokeai.app.api.routers.boards.ApiDependencies", mock_deps)
+ monkeypatch.setattr("invokeai.app.api.routers.videos.ApiDependencies", mock_deps)
+ monkeypatch.setattr("invokeai.app.api.routers.images.ApiDependencies", mock_deps)
+ # _access.assert_board_read_access is called from list_video_dtos and get_video_names
+ # via the videos router; it uses ApiDependencies from its own module scope.
+ monkeypatch.setattr("invokeai.app.api.routers._access.ApiDependencies", mock_deps)
+ yield
+
+
+@pytest.fixture
+def admin_token(setup_jwt_secret: None, enable_multiuser_for_videos: Any, mock_invoker: Invoker, client: TestClient):
+ setup_test_user(mock_invoker, "admin@test.com", "Test Admin", is_admin=True)
+ return get_user_token(client, "admin@test.com")
+
+
+@pytest.fixture
+def user1_token(enable_multiuser_for_videos: Any, mock_invoker: Invoker, client: TestClient, admin_token: str):
+ setup_test_user(mock_invoker, "user1@test.com", "User One", is_admin=False)
+ return get_user_token(client, "user1@test.com")
+
+
+@pytest.fixture
+def user2_token(enable_multiuser_for_videos: Any, mock_invoker: Invoker, client: TestClient, admin_token: str):
+ setup_test_user(mock_invoker, "user2@test.com", "User Two", is_admin=False)
+ return get_user_token(client, "user2@test.com")
+
+
+@pytest.fixture
+def user1_private_board(client: TestClient, user1_token: str) -> str:
+ response = client.post(
+ "/api/v1/boards/?board_name=User1+Private+Board",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_201_CREATED
+ return response.json()["board_id"]
+
+
+# ---------------------------------------------------------------------------
+# Auth requirement
+# ---------------------------------------------------------------------------
+
+
+def test_list_video_dtos_requires_auth(enable_multiuser_for_videos: Any, client: TestClient):
+ response = client.get("/api/v1/videos/")
+ assert response.status_code == status.HTTP_401_UNAUTHORIZED
+
+
+def test_get_video_names_requires_auth(enable_multiuser_for_videos: Any, client: TestClient):
+ response = client.get("/api/v1/videos/names")
+ assert response.status_code == status.HTTP_401_UNAUTHORIZED
+
+
+# ---------------------------------------------------------------------------
+# Explicit board_id with no read access (the JPPhoto finding)
+# ---------------------------------------------------------------------------
+
+
+def test_list_video_dtos_forbidden_for_other_users_private_board(
+ client: TestClient, user1_private_board: str, user2_token: str
+):
+ """user2 cannot list videos on user1's private board even if they know the board_id."""
+ response = client.get(
+ f"/api/v1/videos/?board_id={user1_private_board}",
+ headers={"Authorization": f"Bearer {user2_token}"},
+ )
+ assert response.status_code == status.HTTP_403_FORBIDDEN
+
+
+def test_get_video_names_forbidden_for_other_users_private_board(
+ client: TestClient, user1_private_board: str, user2_token: str
+):
+ response = client.get(
+ f"/api/v1/videos/names?board_id={user1_private_board}",
+ headers={"Authorization": f"Bearer {user2_token}"},
+ )
+ assert response.status_code == status.HTTP_403_FORBIDDEN
+
+
+def test_owner_can_list_videos_on_their_private_board(client: TestClient, user1_private_board: str, user1_token: str):
+ response = client.get(
+ f"/api/v1/videos/?board_id={user1_private_board}",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+
+
+def test_admin_can_list_videos_on_any_private_board(client: TestClient, user1_private_board: str, admin_token: str):
+ response = client.get(
+ f"/api/v1/videos/?board_id={user1_private_board}",
+ headers={"Authorization": f"Bearer {admin_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+
+
+# ---------------------------------------------------------------------------
+# Omitted board_id: route should not blow up; isolation enforced at SQL layer
+# ---------------------------------------------------------------------------
+
+
+def test_list_video_dtos_no_board_id_succeeds_for_any_authed_user(client: TestClient, user2_token: str):
+ """The route allows omitted board_id (the SQL layer filters by user_id) — no 403 here."""
+ response = client.get(
+ "/api/v1/videos/",
+ headers={"Authorization": f"Bearer {user2_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+
+
+def test_list_video_dtos_none_board_succeeds_for_any_authed_user(client: TestClient, user2_token: str):
+ response = client.get(
+ "/api/v1/videos/?board_id=none",
+ headers={"Authorization": f"Bearer {user2_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+
+
+# ---------------------------------------------------------------------------
+# POST /videos/delete must not re-raise mid-loop (PR #9163 review fix)
+# ---------------------------------------------------------------------------
+
+
+def test_delete_videos_from_list_skips_foreign_items_and_returns_owned(
+ client: TestClient, mock_invoker: Invoker, user1_token: str
+):
+ """A non-admin batch delete that includes a video owned by another user must keep going
+ and return 200 with the owned items in ``deleted_videos``. Previously the route raised
+ 403 mid-loop, throwing away the response payload so the frontend cache never learned
+ about already-deleted records and the UI showed stale entries until the next refresh.
+ """
+ # Resolve user1's id from the token claim so we can wire up the ownership stub
+ # without depending on test-internal user state.
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+ user1_id = user1.user_id
+
+ def fake_get_user_id(video_name: str):
+ # Names beginning with 'mine_' belong to user1, anything else to a stranger.
+ return user1_id if video_name.startswith("mine_") else "other-user-id"
+
+ mock_invoker.services.video_records.get_user_id.side_effect = fake_get_user_id
+ # When _assert_video_owner falls back to the board check, return no board so the public
+ # fallback path doesn't relax permissions for the foreign video.
+ mock_invoker.services.board_video_records.get_board_for_video.return_value = None
+
+ fake_dto = MagicMock()
+ fake_dto.board_id = None
+ mock_invoker.services.videos.get_dto.return_value = fake_dto
+
+ response = client.post(
+ "/api/v1/videos/delete",
+ json={"video_names": ["mine_a.mp4", "foreign.mp4", "mine_b.mp4"]},
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ body = response.json()
+ # Both owned items must appear; the foreign one must be skipped silently.
+ assert set(body["deleted_videos"]) == {"mine_a.mp4", "mine_b.mp4"}
+ # The service must have been told to delete the owned names but not the foreign one.
+ delete_calls = {call.args[0] for call in mock_invoker.services.videos.delete.call_args_list}
+ assert delete_calls == {"mine_a.mp4", "mine_b.mp4"}
+
+
+# ---------------------------------------------------------------------------
+# POST /videos/star and /videos/unstar must not re-raise mid-loop either
+# (PR #9163 review fix — same partial-mutation-then-403 pattern as bulk delete)
+# ---------------------------------------------------------------------------
+
+
+def _setup_mixed_ownership_batch(mock_invoker: Invoker, user1_id: str) -> None:
+ """Names beginning with 'mine_' belong to user1, anything else to a stranger."""
+
+ def fake_get_user_id(video_name: str):
+ return user1_id if video_name.startswith("mine_") else "other-user-id"
+
+ mock_invoker.services.video_records.get_user_id.side_effect = fake_get_user_id
+ # When _assert_video_owner falls back to the board check, return no board so the public
+ # fallback path doesn't relax permissions for the foreign video.
+ mock_invoker.services.board_video_records.get_board_for_video.return_value = None
+
+ # The route reads ``updated.board_id`` to build ``affected_boards``; a bare MagicMock
+ # there would fail the response model's Pydantic validation.
+ fake_updated = MagicMock()
+ fake_updated.board_id = None
+ mock_invoker.services.videos.update.return_value = fake_updated
+
+
+def test_star_videos_from_list_skips_foreign_items_and_returns_owned(
+ client: TestClient, mock_invoker: Invoker, user1_token: str
+):
+ """A batch star that includes a video owned by another user must keep going and return
+ 200 with the owned items in ``starred_videos``. Previously the route raised 403
+ mid-loop: earlier videos were already mutated, but the error-shaped response carried no
+ payload, so the client never invalidated caches for the successful updates.
+ """
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+ _setup_mixed_ownership_batch(mock_invoker, user1.user_id)
+
+ response = client.post(
+ "/api/v1/videos/star",
+ json={"video_names": ["mine_a.mp4", "foreign.mp4", "mine_b.mp4"]},
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ body = response.json()
+ assert set(body["starred_videos"]) == {"mine_a.mp4", "mine_b.mp4"}
+ # The service must have been asked to update the owned names but not the foreign one.
+ update_calls = {call.args[0] for call in mock_invoker.services.videos.update.call_args_list}
+ assert update_calls == {"mine_a.mp4", "mine_b.mp4"}
+
+
+def test_unstar_videos_from_list_skips_foreign_items_and_returns_owned(
+ client: TestClient, mock_invoker: Invoker, user1_token: str
+):
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+ _setup_mixed_ownership_batch(mock_invoker, user1.user_id)
+
+ response = client.post(
+ "/api/v1/videos/unstar",
+ json={"video_names": ["mine_a.mp4", "foreign.mp4", "mine_b.mp4"]},
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ body = response.json()
+ assert set(body["unstarred_videos"]) == {"mine_a.mp4", "mine_b.mp4"}
+ update_calls = {call.args[0] for call in mock_invoker.services.videos.update.call_args_list}
+ assert update_calls == {"mine_a.mp4", "mine_b.mp4"}
+
+
+# ---------------------------------------------------------------------------
+# POST /videos/upload must reject malformed MP4 payloads with 415 (residual
+# verification flagged in JPPhoto's PR #9163 review)
+# ---------------------------------------------------------------------------
+
+
+def test_upload_video_malformed_mp4_returns_415_and_cleans_up_tmp(
+ client: TestClient, mock_invoker: Invoker, user1_token: str, tmp_path: Path
+):
+ """An upload that looks like an MP4 on the surface (``.mp4`` extension or video MIME
+ type) but contains bytes ``probe_video`` can't decode must:
+
+ 1. Reach ``probe_video`` (the extension/MIME gate is intentionally permissive — the
+ real validation is the decode probe).
+ 2. Surface a 415 to the caller.
+ 3. Unlink the streamed-to-disk temp file so the server doesn't leak storage on every
+ garbage upload.
+ """
+ # Capture the tmp path the route created so we can prove it was unlinked after the
+ # 415 response. ``tempfile.NamedTemporaryFile(..., delete=False)`` is invoked inside
+ # the route, so we wrap the real call and stash the resulting path.
+ captured_paths: list[Path] = []
+
+ import tempfile as _tempfile
+
+ real_named_tmp = _tempfile.NamedTemporaryFile
+
+ def spying_named_tmp(*args: Any, **kwargs: Any):
+ handle = real_named_tmp(*args, **kwargs)
+ captured_paths.append(Path(handle.name))
+ return handle
+
+ # The fixture's videos mock would no-op the service call; we explicitly do NOT want
+ # that path to fire because we're asserting probe_video runs and rejects.
+ mock_invoker.services.videos.create.side_effect = AssertionError(
+ "videos.create should not be called when probe_video rejects the upload"
+ )
+
+ with (
+ patch("invokeai.app.api.routers.videos.tempfile.NamedTemporaryFile", side_effect=spying_named_tmp),
+ patch(
+ "invokeai.app.api.routers.videos.probe_video",
+ side_effect=RuntimeError("not a decodable mp4"),
+ ),
+ ):
+ response = client.post(
+ "/api/v1/videos/upload",
+ params={"video_category": "general", "is_intermediate": False},
+ files={"file": ("renamed_text.mp4", b"this is not an mp4 payload at all", "video/mp4")},
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+
+ assert response.status_code == status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
+ # The route should have allocated exactly one tmp file and then unlinked it.
+ assert len(captured_paths) == 1, f"expected one tmp file, got {captured_paths}"
+ tmp_file = captured_paths[0]
+ assert not tmp_file.exists(), f"tmp file leaked after 415: {tmp_file}"
+
+
+def test_upload_video_rejects_non_mp4_container_with_spoofed_mime(
+ client: TestClient, mock_invoker: Invoker, user1_token: str
+):
+ mock_invoker.services.videos.create.side_effect = AssertionError("non-MP4 payload reached video creation")
+ with patch("invokeai.app.api.routers.videos.probe_video", return_value=(64, 64, 1.0, 8.0)):
+ response = client.post(
+ "/api/v1/videos/upload",
+ params={"video_category": "general", "is_intermediate": False},
+ files={"file": ("spoofed.mp4", b"\x1aE\xdf\xa3webm payload", "video/mp4")},
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+
+ assert response.status_code == status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
+ mock_invoker.services.videos.create.assert_not_called()
+
+
+def test_mp4_validation_allows_boxes_before_file_type(tmp_path: Path) -> None:
+ path = tmp_path / "valid.mp4"
+ path.write_bytes(b"\x00\x00\x00\x08free" + b"\x00\x00\x00\x18ftypmp42" + b"\x00" * 12)
+
+ assert _is_mp4_file(path)
+
+
+def test_mp4_validation_rejects_quicktime_brand(tmp_path: Path) -> None:
+ path = tmp_path / "quicktime.mp4"
+ path.write_bytes(b"\x00\x00\x00\x18ftypqt " + b"\x00" * 12)
+
+ assert not _is_mp4_file(path)
+
+
+@pytest.mark.parametrize("suffix,thumbnail", [("full", False), ("thumbnail", True)])
+def test_video_media_requires_auth_in_multiuser_mode(
+ enable_multiuser_for_videos: Any,
+ client: TestClient,
+ mock_invoker: Invoker,
+ tmp_path: Path,
+ suffix: str,
+ thumbnail: bool,
+):
+ client.cookies.clear()
+ media_path = tmp_path / ("video.webp" if thumbnail else "video.mp4")
+ media_path.write_bytes(b"media")
+ mock_invoker.services.videos.get_path.return_value = str(media_path)
+
+ response = client.get(f"/api/v1/videos/i/private.mp4/{suffix}")
+
+ assert response.status_code == status.HTTP_401_UNAUTHORIZED
+ mock_invoker.services.videos.get_path.assert_not_called()
+
+
+@pytest.mark.parametrize("suffix,thumbnail", [("full", False), ("thumbnail", True)])
+def test_video_owner_can_load_media_with_login_cookie(
+ client: TestClient,
+ mock_invoker: Invoker,
+ user1_token: str,
+ tmp_path: Path,
+ suffix: str,
+ thumbnail: bool,
+):
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+ mock_invoker.services.video_records.get_user_id.return_value = user1.user_id
+ media_path = tmp_path / ("video.webp" if thumbnail else "video.mp4")
+ media_path.write_bytes(b"media")
+ mock_invoker.services.videos.get_path.return_value = str(media_path)
+ client.cookies.clear()
+ login = client.post(
+ "/api/v1/auth/login",
+ json={"email": "user1@test.com", "password": "TestPass123", "remember_me": False},
+ )
+ assert login.status_code == status.HTTP_200_OK
+
+ response = client.get(f"/api/v1/videos/i/private.mp4/{suffix}")
+
+ assert response.status_code == status.HTTP_200_OK
+ assert response.headers["cache-control"] == "private, no-store"
+ mock_invoker.services.videos.get_path.assert_called_once_with("private.mp4", thumbnail=thumbnail)
+
+
+def test_foreign_user_cannot_load_private_video_media(
+ client: TestClient, mock_invoker: Invoker, user1_token: str, user2_token: str
+):
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+ mock_invoker.services.video_records.get_user_id.return_value = user1.user_id
+ mock_invoker.services.board_video_records.get_board_for_video.return_value = None
+
+ response = client.get(
+ "/api/v1/videos/i/private.mp4/full",
+ headers={"Authorization": f"Bearer {user2_token}"},
+ )
+
+ assert response.status_code == status.HTTP_403_FORBIDDEN
+ mock_invoker.services.videos.get_path.assert_not_called()
+
+
+# ---------------------------------------------------------------------------
+# GET /videos/i/{video_name}/thumbnail must return 404 when the thumbnail file
+# is missing on disk (JPPhoto PR #9163 follow-up). Video saves are allowed
+# without a thumbnail in video_files_disk.save, so this is reachable.
+# ---------------------------------------------------------------------------
+
+
+def test_get_video_thumbnail_missing_file_returns_404(
+ client: TestClient,
+ mock_invoker: Invoker,
+ user1_token: str,
+ tmp_path: Path,
+):
+ """If videos.get_path resolves successfully but the file doesn't exist, the route must
+ return 404 up front. Previously it returned FileResponse and the missing-file error was
+ raised by Starlette *after* the route's try/except, so callers saw a 500-class failure
+ instead of the documented 404.
+ """
+ missing_path = tmp_path / "does_not_exist.webp"
+ assert not missing_path.exists()
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+ mock_invoker.services.video_records.get_user_id.return_value = user1.user_id
+ mock_invoker.services.videos.get_path.return_value = str(missing_path)
+
+ response = client.get(
+ "/api/v1/videos/i/some_video.mp4/thumbnail",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_404_NOT_FOUND
+ mock_invoker.services.videos.get_path.assert_called_once_with("some_video.mp4", thumbnail=True)
+
+
+# ---------------------------------------------------------------------------
+# DELETE /videos/board: stranded-contributor recovery (JPPhoto PR #9163 May-22 follow-up)
+#
+# Scenario: user2 uploads to user1's Public board, user1 later flips the board to
+# Shared/Private. Without a fallback path, neither the uploader nor the board owner
+# can detach the video — _assert_video_direct_owner rejects user1, and
+# _assert_board_write_access rejects user2 because the board is no longer Public.
+# The route must accept removal from either the video owner OR a user with write
+# access to the destination board (mirrors remove_image_from_board).
+# ---------------------------------------------------------------------------
+
+
+def test_remove_video_from_board_succeeds_for_video_owner_on_foreign_private_board(
+ client: TestClient, mock_invoker: Invoker, user1_token: str, user2_token: str
+):
+ """user2 owns the video; the video sits on user1's now-private board. user2 must still
+ be able to detach it via its direct ownership."""
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ user2 = mock_invoker.services.users.get_by_email("user2@test.com")
+ assert user1 is not None and user2 is not None
+
+ # user2 owns the video.
+ mock_invoker.services.video_records.get_user_id.return_value = user2.user_id
+ # The video lives on user1's now-private board.
+ mock_invoker.services.board_video_records.get_board_for_video.return_value = "user1-private-board"
+
+ response = client.request(
+ "DELETE",
+ "/api/v1/videos/board",
+ json={"video_name": "uploaded.mp4"},
+ headers={"Authorization": f"Bearer {user2_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ mock_invoker.services.board_video_records.remove_video_from_board.assert_called_once_with(video_name="uploaded.mp4")
+
+
+def test_remove_video_from_board_succeeds_for_board_owner_of_non_owned_video(
+ client: TestClient, mock_invoker: Invoker, user1_token: str, user2_token: str
+):
+ """user1 owns the board; user2 owns the video sitting on it. user1 must be able to
+ detach the foreign video from their board even though they are not the video owner."""
+ from invokeai.app.services.board_records.board_records_common import BoardVisibility
+
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ user2 = mock_invoker.services.users.get_by_email("user2@test.com")
+ assert user1 is not None and user2 is not None
+
+ mock_invoker.services.video_records.get_user_id.return_value = user2.user_id
+ mock_invoker.services.board_video_records.get_board_for_video.return_value = "user1-board"
+
+ # _assert_board_write_access reads the board DTO to check ownership/visibility.
+ fake_board = MagicMock()
+ fake_board.user_id = user1.user_id
+ fake_board.board_visibility = BoardVisibility.Private
+ with patch.object(mock_invoker.services.boards, "get_dto", return_value=fake_board):
+ response = client.request(
+ "DELETE",
+ "/api/v1/videos/board",
+ json={"video_name": "stranded.mp4"},
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert response.status_code == status.HTTP_200_OK
+ mock_invoker.services.board_video_records.remove_video_from_board.assert_called_once_with(video_name="stranded.mp4")
+
+
+def test_remove_video_from_board_rejects_third_party(
+ client: TestClient, mock_invoker: Invoker, user1_token: str, user2_token: str
+):
+ """A user who is neither the video owner nor a board write-access holder must be
+ rejected — the relaxed path is a stranded-contributor escape hatch, not an open door."""
+ from invokeai.app.services.board_records.board_records_common import BoardVisibility
+
+ admin = mock_invoker.services.users.get_by_email("admin@test.com")
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert admin is not None and user1 is not None
+
+ # Video is owned by admin; board is owned by user1 and is Private.
+ mock_invoker.services.video_records.get_user_id.return_value = admin.user_id
+ mock_invoker.services.board_video_records.get_board_for_video.return_value = "user1-board"
+
+ fake_board = MagicMock()
+ fake_board.user_id = user1.user_id
+ fake_board.board_visibility = BoardVisibility.Private
+
+ # user2 has no claim to either resource.
+ with patch.object(mock_invoker.services.boards, "get_dto", return_value=fake_board):
+ response = client.request(
+ "DELETE",
+ "/api/v1/videos/board",
+ json={"video_name": "not_mine.mp4"},
+ headers={"Authorization": f"Bearer {user2_token}"},
+ )
+ assert response.status_code == status.HTTP_403_FORBIDDEN
+ mock_invoker.services.board_video_records.remove_video_from_board.assert_not_called()
diff --git a/tests/app/routers/test_virtual_boards.py b/tests/app/routers/test_virtual_boards.py
index 42ae0be778b..f5468dcc802 100644
--- a/tests/app/routers/test_virtual_boards.py
+++ b/tests/app/routers/test_virtual_boards.py
@@ -25,6 +25,21 @@ def _save_image(mock_invoker: Invoker, image_name: str, user_id: str) -> None:
)
+def _save_video(mock_invoker: Invoker, video_name: str, user_id: str) -> None:
+ mock_invoker.services.video_records.save(
+ video_name=video_name,
+ video_origin=ResourceOrigin.INTERNAL,
+ video_category=ImageCategory.GENERAL,
+ width=10,
+ height=10,
+ duration=1.0,
+ fps=8.0,
+ has_workflow=False,
+ is_intermediate=False,
+ user_id=user_id,
+ )
+
+
def test_list_by_date_requires_auth(enable_multiuser: Any, client: TestClient):
r = client.get("/api/v1/virtual_boards/by_date")
assert r.status_code == status.HTTP_401_UNAUTHORIZED
@@ -79,3 +94,54 @@ def test_admin_sees_all_dates(
assert r.status_code == status.HTTP_200_OK
total = sum(b.get("image_count", 0) for b in r.json())
assert total >= 2 # admin sees images from both users
+
+
+def test_item_names_by_date_requires_auth(enable_multiuser: Any, client: TestClient):
+ r = client.get("/api/v1/virtual_boards/by_date/2026-05-18/item_names")
+ assert r.status_code == status.HTTP_401_UNAUTHORIZED
+
+
+def test_video_only_date_appears_as_virtual_board(client: TestClient, user1_token: str, mock_invoker: Invoker):
+ """A date containing only videos must still surface as a virtual board (video_count > 0)."""
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+
+ _save_video(mock_invoker, "u1-vid-only.mp4", user1.user_id)
+
+ r = client.get(
+ "/api/v1/virtual_boards/by_date",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert r.status_code == status.HTTP_200_OK
+ boards = r.json()
+ assert len(boards) == 1
+ assert boards[0]["image_count"] == 0
+ assert boards[0]["video_count"] == 1
+ assert boards[0]["cover_video_name"] == "u1-vid-only.mp4"
+ assert boards[0]["cover_image_name"] is None
+
+
+def test_item_names_by_date_returns_video_refs(client: TestClient, user1_token: str, mock_invoker: Invoker):
+ """Selecting a virtual date must return polymorphic refs — videos included, per-user filtered."""
+ user1 = mock_invoker.services.users.get_by_email("user1@test.com")
+ assert user1 is not None
+
+ _save_image(mock_invoker, "u1-mixed.png", user1.user_id)
+ _save_video(mock_invoker, "u1-mixed.mp4", user1.user_id)
+
+ r = client.get(
+ "/api/v1/virtual_boards/by_date",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert r.status_code == status.HTTP_200_OK
+ date = r.json()[0]["date"]
+
+ r = client.get(
+ f"/api/v1/virtual_boards/by_date/{date}/item_names",
+ headers={"Authorization": f"Bearer {user1_token}"},
+ )
+ assert r.status_code == status.HTTP_200_OK
+ body = r.json()
+ refs = {(item["kind"], item["name"]) for item in body["items"]}
+ assert refs == {("image", "u1-mixed.png"), ("video", "u1-mixed.mp4")}
+ assert body["total_count"] == 2
diff --git a/tests/app/routers/test_workflows_multiuser.py b/tests/app/routers/test_workflows_multiuser.py
index 10c9fde17e7..c6492c70dcb 100644
--- a/tests/app/routers/test_workflows_multiuser.py
+++ b/tests/app/routers/test_workflows_multiuser.py
@@ -59,6 +59,7 @@ def client():
def mock_services() -> InvocationServices:
from invokeai.app.services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
from invokeai.app.services.board_records.board_records_sqlite import SqliteBoardRecordStorage
+ from invokeai.app.services.board_video_records.board_video_records_sqlite import SqliteBoardVideoRecordStorage
from invokeai.app.services.boards.boards_default import BoardService
from invokeai.app.services.bulk_download.bulk_download_default import BulkDownloadService
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import (
@@ -69,6 +70,7 @@ def mock_services() -> InvocationServices:
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
from invokeai.app.services.invocation_stats.invocation_stats_default import InvocationStatsService
from invokeai.app.services.users.users_default import UserService
+ from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
from tests.test_nodes import TestEventService
configuration = InvokeAIAppConfig(use_memory_db=True, node_cache_size=0)
@@ -107,6 +109,11 @@ def mock_services() -> InvocationServices:
client_state_persistence=ClientStatePersistenceSqlite(db=db),
users=UserService(db),
external_generation=None, # type: ignore
+ videos=None, # type: ignore
+ video_files=None, # type: ignore
+ video_records=SqliteVideoRecordStorage(db=db),
+ board_video_records=SqliteBoardVideoRecordStorage(db=db),
+ gallery=None, # type: ignore
)
diff --git a/tests/app/services/gallery/test_gallery_default.py b/tests/app/services/gallery/test_gallery_default.py
new file mode 100644
index 00000000000..d58731ab005
--- /dev/null
+++ b/tests/app/services/gallery/test_gallery_default.py
@@ -0,0 +1,209 @@
+"""Regression tests for SqliteGalleryService multiuser isolation and date-based
+virtual boards.
+
+Covers JPPhoto's code-review findings (PR #9163):
+
+1. The gallery /items/ and /items/names endpoints returned every user's items
+ when ``board_id`` was omitted, because ``_build_half`` only applied a user
+ filter for the explicit "none" sentinel. The fix added an ``elif user_id is
+ not None and not is_admin`` branch; these tests pin the behaviour for both
+ halves of the polymorphic union.
+
+2. Date-based virtual boards were image-only: video-only dates did not appear
+ at all, and mixed dates omitted videos from counts/contents/covers. The
+ gallery service now owns ``get_dates`` and a ``created_date`` filter on
+ ``list_item_names`` so virtual boards cover both kinds.
+"""
+
+import pytest
+
+from invokeai.app.services.config.config_default import InvokeAIAppConfig
+from invokeai.app.services.gallery.gallery_common import GalleryItemKind
+from invokeai.app.services.gallery.gallery_default import SqliteGalleryService
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
+from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
+from invokeai.backend.util.logging import InvokeAILogger
+from tests.fixtures.sqlite_database import create_mock_sqlite_database
+
+
+@pytest.fixture
+def services():
+ config = InvokeAIAppConfig(use_memory_db=True)
+ logger = InvokeAILogger.get_logger(config=config)
+ db = create_mock_sqlite_database(config, logger)
+ return {
+ "gallery": SqliteGalleryService(db=db),
+ "images": SqliteImageRecordStorage(db=db),
+ "videos": SqliteVideoRecordStorage(db=db),
+ }
+
+
+def _save_image(store: SqliteImageRecordStorage, name: str, user_id: str) -> None:
+ store.save(
+ image_name=name,
+ image_origin=ResourceOrigin.INTERNAL,
+ image_category=ImageCategory.GENERAL,
+ width=64,
+ height=64,
+ has_workflow=False,
+ is_intermediate=False,
+ user_id=user_id,
+ )
+
+
+def _save_video(store: SqliteVideoRecordStorage, name: str, user_id: str) -> None:
+ store.save(
+ video_name=name,
+ video_origin=ResourceOrigin.INTERNAL,
+ video_category=ImageCategory.GENERAL,
+ width=64,
+ height=64,
+ duration=1.0,
+ fps=8.0,
+ has_workflow=False,
+ is_intermediate=False,
+ user_id=user_id,
+ )
+
+
+@pytest.fixture
+def seeded(services):
+ # Mixed-kind items for two users, no board association — which is the path that
+ # previously bypassed user filtering entirely.
+ _save_image(services["images"], "alice.png", user_id="alice")
+ _save_video(services["videos"], "alice.mp4", user_id="alice")
+ _save_image(services["images"], "bob.png", user_id="bob")
+ _save_video(services["videos"], "bob.mp4", user_id="bob")
+ return services
+
+
+class TestListItemNamesOmittedBoardIdMultiuser:
+ def test_non_admin_only_sees_own_items(self, seeded) -> None:
+ result = seeded["gallery"].list_item_names(user_id="alice", is_admin=False)
+ names = {(item.kind, item.name) for item in result.items}
+ assert names == {
+ (GalleryItemKind.IMAGE, "alice.png"),
+ (GalleryItemKind.VIDEO, "alice.mp4"),
+ }
+ assert result.total_count == 2
+
+ def test_admin_sees_all_items(self, seeded) -> None:
+ result = seeded["gallery"].list_item_names(user_id="alice", is_admin=True)
+ names = {(item.kind, item.name) for item in result.items}
+ assert names == {
+ (GalleryItemKind.IMAGE, "alice.png"),
+ (GalleryItemKind.IMAGE, "bob.png"),
+ (GalleryItemKind.VIDEO, "alice.mp4"),
+ (GalleryItemKind.VIDEO, "bob.mp4"),
+ }
+ assert result.total_count == 4
+
+ def test_explicit_none_board_still_isolates(self, seeded) -> None:
+ # Before the fix this branch was correct; included here as a guard against
+ # accidental regression in the still-functioning code path.
+ result = seeded["gallery"].list_item_names(board_id="none", user_id="alice", is_admin=False)
+ names = {(item.kind, item.name) for item in result.items}
+ assert names == {
+ (GalleryItemKind.IMAGE, "alice.png"),
+ (GalleryItemKind.VIDEO, "alice.mp4"),
+ }
+
+
+def _backdate(services, table: str, name_col: str, name: str, created_at: str) -> None:
+ """Rewrites created_at so tests can build multi-date galleries (save() always stamps now)."""
+ db = services["images"]._db
+ with db.transaction() as cursor:
+ cursor.execute(f"UPDATE {table} SET created_at = ? WHERE {name_col} = ?", (created_at, name))
+
+
+class TestGetDatesPolymorphic:
+ def test_video_only_date_appears(self, services) -> None:
+ # A date with videos and no images must still produce a virtual board — with the
+ # video as its cover, since there is no image to fall back to.
+ _save_video(services["videos"], "only.mp4", user_id="alice")
+ _backdate(services, "videos", "video_name", "only.mp4", "2026-01-02 10:00:00")
+
+ boards = services["gallery"].get_dates(user_id="alice", is_admin=False)
+
+ assert len(boards) == 1
+ board = boards[0]
+ assert board.date == "2026-01-02"
+ assert board.image_count == 0
+ assert board.asset_count == 0
+ assert board.video_count == 1
+ assert board.cover_image_name is None
+ assert board.cover_video_name == "only.mp4"
+
+ def test_mixed_date_counts_both_kinds(self, services) -> None:
+ _save_image(services["images"], "day1.png", user_id="alice")
+ _save_video(services["videos"], "day1.mp4", user_id="alice")
+ _save_video(services["videos"], "day1b.mp4", user_id="alice")
+ _backdate(services, "images", "image_name", "day1.png", "2026-01-03 09:00:00")
+ _backdate(services, "videos", "video_name", "day1.mp4", "2026-01-03 10:00:00")
+ _backdate(services, "videos", "video_name", "day1b.mp4", "2026-01-03 11:00:00")
+
+ boards = services["gallery"].get_dates(user_id="alice", is_admin=False)
+
+ assert len(boards) == 1
+ board = boards[0]
+ assert board.date == "2026-01-03"
+ assert board.image_count == 1
+ assert board.video_count == 2
+ # The newest item of the date is a video, so the cover is the video.
+ assert board.cover_video_name == "day1b.mp4"
+ assert board.cover_image_name is None
+
+ def test_newest_image_wins_cover(self, services) -> None:
+ _save_video(services["videos"], "old.mp4", user_id="alice")
+ _save_image(services["images"], "new.png", user_id="alice")
+ _backdate(services, "videos", "video_name", "old.mp4", "2026-01-04 09:00:00")
+ _backdate(services, "images", "image_name", "new.png", "2026-01-04 10:00:00")
+
+ boards = services["gallery"].get_dates(user_id="alice", is_admin=False)
+
+ assert len(boards) == 1
+ assert boards[0].cover_image_name == "new.png"
+ assert boards[0].cover_video_name is None
+
+ def test_dates_are_user_isolated(self, seeded) -> None:
+ boards = seeded["gallery"].get_dates(user_id="alice", is_admin=False)
+ # alice has one image + one video, both created today.
+ assert len(boards) == 1
+ assert boards[0].image_count == 1
+ assert boards[0].video_count == 1
+
+ def test_admin_sees_all_dates(self, seeded) -> None:
+ boards = seeded["gallery"].get_dates(user_id="alice", is_admin=True)
+ assert len(boards) == 1
+ assert boards[0].image_count == 2
+ assert boards[0].video_count == 2
+
+
+class TestListItemNamesByCreatedDate:
+ def test_returns_only_items_of_date_including_videos(self, services) -> None:
+ _save_image(services["images"], "target.png", user_id="alice")
+ _save_video(services["videos"], "target.mp4", user_id="alice")
+ _save_image(services["images"], "other.png", user_id="alice")
+ _backdate(services, "images", "image_name", "target.png", "2026-01-05 09:00:00")
+ _backdate(services, "videos", "video_name", "target.mp4", "2026-01-05 10:00:00")
+ _backdate(services, "images", "image_name", "other.png", "2026-01-06 09:00:00")
+
+ result = services["gallery"].list_item_names(user_id="alice", is_admin=False, created_date="2026-01-05")
+
+ names = {(item.kind, item.name) for item in result.items}
+ assert names == {
+ (GalleryItemKind.IMAGE, "target.png"),
+ (GalleryItemKind.VIDEO, "target.mp4"),
+ }
+ assert result.total_count == 2
+
+ def test_created_date_is_user_isolated(self, services) -> None:
+ _save_video(services["videos"], "alice-day.mp4", user_id="alice")
+ _save_video(services["videos"], "bob-day.mp4", user_id="bob")
+ _backdate(services, "videos", "video_name", "alice-day.mp4", "2026-01-07 09:00:00")
+ _backdate(services, "videos", "video_name", "bob-day.mp4", "2026-01-07 10:00:00")
+
+ result = services["gallery"].list_item_names(user_id="alice", is_admin=False, created_date="2026-01-07")
+
+ assert [(item.kind, item.name) for item in result.items] == [(GalleryItemKind.VIDEO, "alice-day.mp4")]
diff --git a/tests/app/services/shared/sqlite_migrator/migrations/test_migration_2026_07_01_add_videos_tables.py b/tests/app/services/shared/sqlite_migrator/migrations/test_migration_2026_07_01_add_videos_tables.py
new file mode 100644
index 00000000000..062d7ff836a
--- /dev/null
+++ b/tests/app/services/shared/sqlite_migrator/migrations/test_migration_2026_07_01_add_videos_tables.py
@@ -0,0 +1,90 @@
+"""Tests for migration 2026_07_01_add_videos_tables: add videos and board_videos tables."""
+
+import sqlite3
+
+import pytest
+
+from invokeai.app.services.shared.sqlite_migrator.migrations.migration_2026_07_01_add_videos_tables import (
+ AddVideosTablesCallback,
+ build_migration,
+)
+
+
+def _create_boards_table(conn: sqlite3.Connection) -> None:
+ conn.execute(
+ """
+ CREATE TABLE boards (
+ board_id TEXT NOT NULL PRIMARY KEY,
+ board_name TEXT NOT NULL
+ );
+ """
+ )
+
+
+def _get_table_names(conn: sqlite3.Connection) -> set[str]:
+ return {row[0] for row in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()}
+
+
+@pytest.fixture
+def db() -> sqlite3.Connection:
+ conn = sqlite3.connect(":memory:")
+ conn.execute("PRAGMA foreign_keys = ON;")
+ _create_boards_table(conn)
+ return conn
+
+
+class TestAddVideosTables:
+ def test_creates_videos_and_board_videos_tables(self, db: sqlite3.Connection):
+ AddVideosTablesCallback()(db.cursor())
+ db.commit()
+
+ assert {"videos", "board_videos"} <= _get_table_names(db)
+
+ def test_videos_updated_at_trigger(self, db: sqlite3.Connection):
+ AddVideosTablesCallback()(db.cursor())
+ db.commit()
+
+ db.execute(
+ "INSERT INTO videos (video_name, video_origin, video_category, width, height)"
+ " VALUES ('v1', 'internal', 'general', 640, 480)"
+ )
+ before = db.execute("SELECT updated_at FROM videos WHERE video_name='v1'").fetchone()[0]
+ db.execute("UPDATE videos SET starred = TRUE WHERE video_name='v1'")
+ after = db.execute("SELECT updated_at FROM videos WHERE video_name='v1'").fetchone()[0]
+ assert after >= before
+
+ def test_board_delete_cascades_to_board_videos(self, db: sqlite3.Connection):
+ AddVideosTablesCallback()(db.cursor())
+ db.commit()
+
+ db.execute("INSERT INTO boards (board_id, board_name) VALUES ('b1', 'Board 1')")
+ db.execute(
+ "INSERT INTO videos (video_name, video_origin, video_category, width, height)"
+ " VALUES ('v1', 'internal', 'general', 640, 480)"
+ )
+ db.execute("INSERT INTO board_videos (board_id, video_name) VALUES ('b1', 'v1')")
+ db.execute("DELETE FROM boards WHERE board_id='b1'")
+
+ assert db.execute("SELECT COUNT(*) FROM board_videos").fetchone()[0] == 0
+ # The video itself is not deleted, only its board association.
+ assert db.execute("SELECT COUNT(*) FROM videos").fetchone()[0] == 1
+
+ def test_idempotent_when_tables_exist(self, db: sqlite3.Connection):
+ cursor = db.cursor()
+ AddVideosTablesCallback()(cursor)
+ db.execute(
+ "INSERT INTO videos (video_name, video_origin, video_category, width, height)"
+ " VALUES ('v1', 'internal', 'general', 640, 480)"
+ )
+ AddVideosTablesCallback()(cursor)
+ db.commit()
+
+ # Re-running must not drop or recreate existing tables/data.
+ assert db.execute("SELECT COUNT(*) FROM videos").fetchone()[0] == 1
+
+ def test_builder_metadata(self):
+ migration = build_migration()
+ assert migration.id == "2026_07_01_add_videos_tables"
+ assert migration.depends_on == "migration_27"
+ assert migration.from_version is None
+ assert migration.to_version is None
diff --git a/tests/app/services/shared/test_invocation_context_videos.py b/tests/app/services/shared/test_invocation_context_videos.py
new file mode 100644
index 00000000000..1a3e0cca1b2
--- /dev/null
+++ b/tests/app/services/shared/test_invocation_context_videos.py
@@ -0,0 +1,67 @@
+from pathlib import Path
+from unittest.mock import MagicMock
+
+import pytest
+
+from invokeai.app.services.board_records.board_records_common import BoardVisibility
+from invokeai.app.services.shared.invocation_context import VideosInterface
+
+
+def _make_interface(visibility: BoardVisibility, owner_id: str = "owner") -> tuple[VideosInterface, MagicMock]:
+ services = MagicMock()
+ services.users.get.return_value = MagicMock(is_admin=False)
+ services.boards.get_dto.return_value = MagicMock(user_id=owner_id, board_visibility=visibility)
+ data = MagicMock()
+ data.queue_item.user_id = "queue-user"
+ data.queue_item.workflow = None
+ data.queue_item.session.graph = None
+ data.queue_item.session_id = "session"
+ data.invocation.is_intermediate = False
+ return VideosInterface(services, data, MagicMock()), services
+
+
+def test_video_save_rejects_foreign_private_board() -> None:
+ videos, services = _make_interface(BoardVisibility.Private)
+
+ with pytest.raises(PermissionError, match="not authorized"):
+ videos.save(Path("output.mp4"), width=64, height=64, duration=1.0, board_id="foreign-board")
+
+ services.videos.create.assert_not_called()
+
+
+@pytest.mark.parametrize("visibility", [BoardVisibility.Public])
+def test_video_save_allows_writable_foreign_board(visibility: BoardVisibility) -> None:
+ videos, services = _make_interface(visibility)
+
+ videos.save(Path("output.mp4"), width=64, height=64, duration=1.0, board_id="public-board")
+
+ services.videos.create.assert_called_once()
+
+
+def test_video_save_allows_board_owner() -> None:
+ videos, services = _make_interface(BoardVisibility.Private, owner_id="queue-user")
+
+ videos.save(Path("output.mp4"), width=64, height=64, duration=1.0, board_id="owned-board")
+
+ services.videos.create.assert_called_once()
+
+
+def test_video_path_rejects_foreign_private_video() -> None:
+ videos, services = _make_interface(BoardVisibility.Private)
+ services.video_records.get_user_id.return_value = "owner"
+ services.board_video_records.get_board_for_video.return_value = "foreign-board"
+
+ with pytest.raises(PermissionError, match="not authorized"):
+ videos.get_path("private.mp4")
+
+ services.videos.get_path.assert_not_called()
+
+
+def test_video_dto_allows_foreign_shared_video() -> None:
+ videos, services = _make_interface(BoardVisibility.Shared)
+ services.video_records.get_user_id.return_value = "owner"
+ services.board_video_records.get_board_for_video.return_value = "shared-board"
+
+ videos.get_dto("shared.mp4")
+
+ services.videos.get_dto.assert_called_once_with("shared.mp4")
diff --git a/tests/app/services/test_image_move_startup_safety.py b/tests/app/services/test_image_move_startup_safety.py
index f7f673322ea..e6b1380e5e8 100644
--- a/tests/app/services/test_image_move_startup_safety.py
+++ b/tests/app/services/test_image_move_startup_safety.py
@@ -38,6 +38,11 @@ def _services(**overrides):
"workflow_thumbnails": object(),
"client_state_persistence": object(),
"users": object(),
+ "videos": object(),
+ "video_files": object(),
+ "video_records": object(),
+ "board_video_records": object(),
+ "gallery": object(),
"image_moves": None,
}
services.update(overrides)
diff --git a/tests/app/services/video_files/test_video_files_disk.py b/tests/app/services/video_files/test_video_files_disk.py
new file mode 100644
index 00000000000..1b82c2b8f0a
--- /dev/null
+++ b/tests/app/services/video_files/test_video_files_disk.py
@@ -0,0 +1,159 @@
+"""Tests for DiskVideoFileStorage (video_files_disk.py).
+
+Covers the save-failure cleanup contract (JPPhoto PR #9163 follow-up): ``save()`` moves the
+source MP4 into permanent storage *before* writing the thumbnail and sidecar, so a failure in
+either of those later steps used to leave the moved MP4 (and any partial artifacts) on disk
+with no DB record through which they could be managed — the caller rolls the record back on
+``VideoFileSaveException`` but nothing removed the files.
+"""
+
+from pathlib import Path
+from unittest.mock import MagicMock
+
+import pytest
+
+from invokeai.app.services.video_files.video_files_common import VideoFileSaveException
+from invokeai.app.services.video_files.video_files_disk import DiskVideoFileStorage
+
+VIDEO_NAME = "abc123.mp4"
+
+
+@pytest.fixture
+def storage(tmp_path: Path) -> DiskVideoFileStorage:
+ return DiskVideoFileStorage(tmp_path / "videos")
+
+
+def _make_source(tmp_path: Path) -> Path:
+ # Not a decodable MP4 — thumbnail extraction fails gracefully (best-effort), which lets
+ # these tests drive the sidecar path without a real video file.
+ source = tmp_path / "source.mp4"
+ source.write_bytes(b"\x00\x00\x00\x18ftypmp42 not a real mp4")
+ return source
+
+
+def _all_files(root: Path) -> list[Path]:
+ return [p for p in root.rglob("*") if p.is_file()]
+
+
+def test_save_writes_video_and_sidecar(storage: DiskVideoFileStorage, tmp_path: Path):
+ source = _make_source(tmp_path)
+
+ storage.save(source_path=source, video_name=VIDEO_NAME, metadata='{"seed": 1}')
+
+ assert storage.get_path(VIDEO_NAME).exists()
+ assert not source.exists()
+ assert storage.get_workflow(VIDEO_NAME) is None # sidecar readable, workflow not set
+
+
+def test_save_failure_after_move_removes_all_destination_files(
+ storage: DiskVideoFileStorage, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+):
+ source = _make_source(tmp_path)
+
+ def broken_dump(*args, **kwargs):
+ raise OSError("disk full")
+
+ # Force the sidecar write (the last step of save) to fail after the MP4 has been moved.
+ monkeypatch.setattr("invokeai.app.services.video_files.video_files_disk.json.dump", broken_dump)
+
+ with pytest.raises(VideoFileSaveException):
+ storage.save(source_path=source, video_name=VIDEO_NAME, metadata='{"seed": 1}')
+
+ assert _all_files(tmp_path / "videos") == []
+
+
+def test_save_failure_in_thumbnail_write_removes_moved_video(
+ storage: DiskVideoFileStorage, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+):
+ source = _make_source(tmp_path)
+
+ # Frame extraction itself is best-effort, but a failure while *writing* the extracted
+ # thumbnail propagates. Simulate that: extraction succeeds, the write blows up.
+ monkeypatch.setattr(
+ "invokeai.app.services.video_files.video_files_disk.extract_video_frame",
+ lambda *args, **kwargs: MagicMock(),
+ )
+ broken_thumbnail = MagicMock()
+ broken_thumbnail.save.side_effect = OSError("read-only filesystem")
+ monkeypatch.setattr(
+ "invokeai.app.services.video_files.video_files_disk.make_thumbnail",
+ lambda *args, **kwargs: broken_thumbnail,
+ )
+
+ with pytest.raises(VideoFileSaveException):
+ storage.save(source_path=source, video_name=VIDEO_NAME)
+
+ assert _all_files(tmp_path / "videos") == []
+
+
+def test_save_failure_cleanup_covers_subfolders(
+ storage: DiskVideoFileStorage, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+):
+ source = _make_source(tmp_path)
+
+ def broken_dump(*args, **kwargs):
+ raise OSError("disk full")
+
+ monkeypatch.setattr("invokeai.app.services.video_files.video_files_disk.json.dump", broken_dump)
+
+ with pytest.raises(VideoFileSaveException):
+ storage.save(
+ source_path=source,
+ video_name=VIDEO_NAME,
+ video_subfolder="2026/07",
+ metadata='{"seed": 1}',
+ )
+
+ assert _all_files(tmp_path / "videos") == []
+
+
+def test_staged_delete_can_be_rolled_back(storage: DiskVideoFileStorage, tmp_path: Path):
+ source = _make_source(tmp_path)
+ storage.save(source_path=source, video_name=VIDEO_NAME, metadata='{"seed": 1}')
+ video_path = storage.get_path(VIDEO_NAME)
+
+ token = storage.stage_delete(VIDEO_NAME)
+ assert not video_path.exists()
+
+ storage.rollback_delete(token)
+ assert video_path.exists()
+ assert storage.get_workflow(VIDEO_NAME) is None
+
+
+def test_staged_delete_can_be_committed(storage: DiskVideoFileStorage, tmp_path: Path):
+ source = _make_source(tmp_path)
+ storage.save(source_path=source, video_name=VIDEO_NAME, metadata='{"seed": 1}')
+
+ token = storage.stage_delete(VIDEO_NAME)
+ storage.commit_delete(token)
+
+ assert _all_files(tmp_path / "videos") == []
+
+
+def test_start_restores_staged_delete_when_record_still_exists(storage: DiskVideoFileStorage, tmp_path: Path):
+ source = _make_source(tmp_path)
+ storage.save(source_path=source, video_name=VIDEO_NAME, metadata='{"seed": 1}')
+ storage.stage_delete(VIDEO_NAME)
+ assert not storage.get_path(VIDEO_NAME).exists()
+ invoker = MagicMock()
+ invoker.services.video_records.get.return_value = MagicMock()
+
+ DiskVideoFileStorage(tmp_path / "videos").start(invoker)
+
+ assert storage.get_path(VIDEO_NAME).exists()
+ assert not list((tmp_path / "videos").glob(".delete_*"))
+
+
+def test_start_purges_staged_delete_when_record_is_gone(storage: DiskVideoFileStorage, tmp_path: Path):
+ from invokeai.app.services.video_records.video_records_common import VideoRecordNotFoundException
+
+ source = _make_source(tmp_path)
+ storage.save(source_path=source, video_name=VIDEO_NAME, metadata='{"seed": 1}')
+ storage.stage_delete(VIDEO_NAME)
+ invoker = MagicMock()
+ invoker.services.video_records.get.side_effect = VideoRecordNotFoundException
+
+ DiskVideoFileStorage(tmp_path / "videos").start(invoker)
+
+ assert _all_files(tmp_path / "videos") == []
+ assert not list((tmp_path / "videos").glob(".delete_*"))
diff --git a/tests/app/services/video_records/test_video_records_sqlite.py b/tests/app/services/video_records/test_video_records_sqlite.py
new file mode 100644
index 00000000000..867ad902e26
--- /dev/null
+++ b/tests/app/services/video_records/test_video_records_sqlite.py
@@ -0,0 +1,139 @@
+"""Regression tests for SqliteVideoRecordStorage multiuser isolation.
+
+Covers JPPhoto's code-review finding (PR #9163): when ``board_id`` was omitted
+from /v1/videos/ and /v1/videos/names, the SQL builder applied no user filter
+and a non-admin caller saw every user's videos. The fix added an
+``elif user_id is not None and not is_admin`` branch; these tests pin the
+behaviour so the regression cannot reappear.
+"""
+
+import pytest
+
+from invokeai.app.services.config.config_default import InvokeAIAppConfig
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
+from invokeai.app.services.users.users_common import UserCreateRequest
+from invokeai.app.services.users.users_default import UserService
+from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
+from invokeai.backend.util.logging import InvokeAILogger
+from tests.fixtures.sqlite_database import create_mock_sqlite_database
+
+
+@pytest.fixture
+def store() -> SqliteVideoRecordStorage:
+ config = InvokeAIAppConfig(use_memory_db=True)
+ logger = InvokeAILogger.get_logger(config=config)
+ db = create_mock_sqlite_database(config, logger)
+ return SqliteVideoRecordStorage(db=db)
+
+
+def _save(store: SqliteVideoRecordStorage, name: str, user_id: str) -> None:
+ store.save(
+ video_name=name,
+ video_origin=ResourceOrigin.INTERNAL,
+ video_category=ImageCategory.GENERAL,
+ width=64,
+ height=64,
+ duration=1.0,
+ fps=8.0,
+ has_workflow=False,
+ is_intermediate=False,
+ user_id=user_id,
+ )
+
+
+@pytest.fixture
+def seeded_store(store: SqliteVideoRecordStorage) -> SqliteVideoRecordStorage:
+ # Two videos per user; all without board association (the bug occurred when board_id
+ # was omitted from the query).
+ _save(store, "alice_1.mp4", user_id="alice")
+ _save(store, "alice_2.mp4", user_id="alice")
+ _save(store, "bob_1.mp4", user_id="bob")
+ _save(store, "bob_2.mp4", user_id="bob")
+ return store
+
+
+class TestGetManyOmittedBoardIdMultiuser:
+ """get_many() with board_id=None must filter by user_id for non-admin callers."""
+
+ def test_non_admin_only_sees_own_videos(self, seeded_store: SqliteVideoRecordStorage) -> None:
+ result = seeded_store.get_many(user_id="alice", is_admin=False)
+ names = {v.video_name for v in result.items}
+ assert names == {"alice_1.mp4", "alice_2.mp4"}
+ assert result.total == 2
+
+ def test_admin_sees_every_users_videos(self, seeded_store: SqliteVideoRecordStorage) -> None:
+ result = seeded_store.get_many(user_id="alice", is_admin=True)
+ names = {v.video_name for v in result.items}
+ assert names == {"alice_1.mp4", "alice_2.mp4", "bob_1.mp4", "bob_2.mp4"}
+
+ def test_no_user_id_returns_all(self, seeded_store: SqliteVideoRecordStorage) -> None:
+ # No user_id means the caller is bypassing user filtering entirely (e.g. internal calls).
+ result = seeded_store.get_many(user_id=None, is_admin=False)
+ names = {v.video_name for v in result.items}
+ assert names == {"alice_1.mp4", "alice_2.mp4", "bob_1.mp4", "bob_2.mp4"}
+
+
+class TestGetVideoNamesOmittedBoardIdMultiuser:
+ """get_video_names() with board_id=None must filter by user_id for non-admin callers."""
+
+ def test_non_admin_only_sees_own_videos(self, seeded_store: SqliteVideoRecordStorage) -> None:
+ result = seeded_store.get_video_names(user_id="alice", is_admin=False)
+ assert set(result.video_names) == {"alice_1.mp4", "alice_2.mp4"}
+ assert result.total_count == 2
+
+ def test_admin_sees_every_users_videos(self, seeded_store: SqliteVideoRecordStorage) -> None:
+ result = seeded_store.get_video_names(user_id="alice", is_admin=True)
+ assert set(result.video_names) == {"alice_1.mp4", "alice_2.mp4", "bob_1.mp4", "bob_2.mp4"}
+
+ def test_explicit_none_board_still_isolates(self, seeded_store: SqliteVideoRecordStorage) -> None:
+ # The "none" sentinel (uncategorized) must also apply the user filter — this was the
+ # only path that was correct *before* the fix; the test guards against accidental
+ # regression there too.
+ result = seeded_store.get_video_names(board_id="none", user_id="alice", is_admin=False)
+ assert set(result.video_names) == {"alice_1.mp4", "alice_2.mp4"}
+
+
+class TestUserDeletionLifecycle:
+ """Documents the intended videos↔users lifecycle (JPPhoto PR #9163 July-10 follow-up).
+
+ ``videos.user_id`` deliberately has no FK to ``users`` — exactly like ``images``,
+ ``boards`` and ``workflows``, whose user_id columns (migration_27) are index-only.
+ Deleting a user therefore leaves their videos in place instead of cascading a row
+ delete that would strand the files on disk; the orphaned records stay visible to
+ administrators (and only to administrators), who can clean them up or reassign them.
+ These tests pin that platform-wide behavior for videos so any future change to the
+ user-deletion story is a deliberate decision rather than an accident.
+ """
+
+ @pytest.fixture
+ def migrated_db(self) -> SqliteDatabase:
+ config = InvokeAIAppConfig(use_memory_db=True)
+ logger = InvokeAILogger.get_logger(config=config)
+ return create_mock_sqlite_database(config, logger)
+
+ def test_videos_survive_owner_deletion_and_remain_admin_only(self, migrated_db: SqliteDatabase) -> None:
+ users = UserService(migrated_db)
+ store = SqliteVideoRecordStorage(db=migrated_db)
+
+ owner = users.create(
+ UserCreateRequest(
+ email="doomed@example.com",
+ display_name="Doomed User",
+ password="TestPassword123",
+ is_admin=False,
+ )
+ )
+ _save(store, "doomed.mp4", user_id=owner.user_id)
+
+ users.delete(owner.user_id)
+ assert users.get(owner.user_id) is None
+
+ # The record survives, still attributed to the deleted owner...
+ assert store.get_user_id("doomed.mp4") == owner.user_id
+ # ...is visible to admins for cleanup...
+ admin_view = store.get_many(user_id="some-admin", is_admin=True)
+ assert "doomed.mp4" in {v.video_name for v in admin_view.items}
+ # ...and no regular user inherits it.
+ other_view = store.get_many(user_id="bystander", is_admin=False)
+ assert "doomed.mp4" not in {v.video_name for v in other_view.items}
diff --git a/tests/app/services/videos/__init__.py b/tests/app/services/videos/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/app/services/videos/test_videos_default.py b/tests/app/services/videos/test_videos_default.py
new file mode 100644
index 00000000000..772bb6477a9
--- /dev/null
+++ b/tests/app/services/videos/test_videos_default.py
@@ -0,0 +1,265 @@
+"""Tests for VideoService (videos_default.py).
+
+Covers the board-cascade delete contract (JPPhoto PR #9163 follow-up). The old
+implementation silently swallowed per-file delete errors and then deleted every
+record anyway, which orphaned the file on disk while reporting success.
+"""
+
+from unittest.mock import MagicMock
+
+import pytest
+
+from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
+from invokeai.app.services.video_records.video_records_common import VideoRecord
+from invokeai.app.services.videos.videos_default import VideoService
+from invokeai.app.util.misc import get_iso_timestamp
+
+
+def _make_record(video_name: str = "abc.mp4", video_subfolder: str = "") -> VideoRecord:
+ now = get_iso_timestamp()
+ return VideoRecord(
+ video_name=video_name,
+ video_origin=ResourceOrigin.INTERNAL,
+ video_category=ImageCategory.GENERAL,
+ width=64,
+ height=64,
+ duration=1.0,
+ fps=24.0,
+ created_at=now,
+ updated_at=now,
+ is_intermediate=False,
+ starred=False,
+ has_workflow=False,
+ video_subfolder=video_subfolder,
+ )
+
+
+@pytest.fixture
+def video_service() -> VideoService:
+ svc = VideoService()
+ invoker = MagicMock()
+ svc.start(invoker)
+ return svc
+
+
+class TestDeleteVideosOnBoardContract:
+ """Per JPPhoto's PR review: a file-delete failure must NOT result in the DB record being
+ deleted. Otherwise the API reports success while the file lingers on disk with no record
+ pointing at it, and the user has no way to discover or clean up the leak.
+ """
+
+ def test_record_preserved_when_file_delete_fails(self, video_service: VideoService):
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ invoker.services.board_video_records.get_all_board_video_names_for_board.return_value = [
+ "good.mp4",
+ "bad.mp4",
+ ]
+ invoker.services.video_records.get.side_effect = [
+ _make_record(video_name="good.mp4", video_subfolder="general"),
+ _make_record(video_name="bad.mp4", video_subfolder="general"),
+ ]
+ invoker.services.video_files.stage_delete.side_effect = [object(), Exception("disk error")]
+
+ deleted = video_service.delete_videos_on_board("board-1")
+
+ # Only the video whose file we successfully removed should have its record deleted.
+ invoker.services.video_records.delete_many.assert_called_once_with(["good.mp4"])
+ # The method must also surface the truthful set to the caller so the API response can
+ # avoid claiming the preserved record was deleted (JPPhoto PR #9163 May-22 follow-up).
+ assert deleted == ["good.mp4"]
+
+ def test_file_cleanup_failure_does_not_raise(self, video_service: VideoService):
+ """A single file-delete failure must not surface as a 500 to the user — the rest of
+ the board deletion has to keep going so other videos and the board itself can still
+ be cleaned up."""
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ invoker.services.board_video_records.get_all_board_video_names_for_board.return_value = ["v.mp4"]
+ invoker.services.video_records.get.return_value = _make_record(video_name="v.mp4")
+ invoker.services.video_files.stage_delete.side_effect = Exception("permission denied")
+
+ # Should not raise
+ deleted = video_service.delete_videos_on_board("board-1")
+
+ # And the failing video's record must be preserved.
+ invoker.services.video_records.delete_many.assert_called_once_with([])
+ assert deleted == []
+
+ def test_all_records_deleted_on_full_success(self, video_service: VideoService):
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ invoker.services.board_video_records.get_all_board_video_names_for_board.return_value = [
+ "a.mp4",
+ "b.mp4",
+ ]
+ invoker.services.video_records.get.side_effect = [
+ _make_record(video_name="a.mp4"),
+ _make_record(video_name="b.mp4"),
+ ]
+ invoker.services.video_files.stage_delete.side_effect = [object(), object()]
+
+ deleted = video_service.delete_videos_on_board("board-1")
+
+ invoker.services.video_records.delete_many.assert_called_once_with(["a.mp4", "b.mp4"])
+ assert deleted == ["a.mp4", "b.mp4"]
+
+ def test_staging_cleanup_failure_is_deferred_after_records_are_deleted(self, video_service: VideoService):
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ invoker.services.board_video_records.get_all_board_video_names_for_board.return_value = ["v.mp4"]
+ invoker.services.video_records.get.return_value = _make_record(video_name="v.mp4")
+ invoker.services.video_files.stage_delete.return_value = object()
+ invoker.services.video_files.commit_delete.side_effect = OSError("staging directory busy")
+
+ deleted = video_service.delete_videos_on_board("board-1")
+
+ assert deleted == ["v.mp4"]
+ invoker.services.video_records.delete_many.assert_called_once_with(["v.mp4"])
+ invoker.services.logger.error.assert_called()
+
+
+class TestDeleteAtomicity:
+ def test_single_delete_rolls_files_back_when_record_delete_fails(self, video_service: VideoService):
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ invoker.services.video_records.get.return_value = _make_record()
+ invoker.services.video_records.delete.side_effect = RuntimeError("database unavailable")
+
+ with pytest.raises(RuntimeError, match="database unavailable"):
+ video_service.delete("abc.mp4")
+
+ invoker.services.video_files.stage_delete.assert_called_once_with("abc.mp4", video_subfolder="")
+ invoker.services.video_files.rollback_delete.assert_called_once()
+ invoker.services.video_files.commit_delete.assert_not_called()
+
+
+class TestCreateRollback:
+ """Per JPPhoto's PR review (May 22 follow-up): if the video file save fails after the DB
+ record has been written, the create path must roll back the record (and any board
+ attachment) so the gallery never contains a DB-only ghost whose file endpoints 404.
+ """
+
+ def _wire_minimal_create_dependencies(self, invoker: MagicMock, video_name: str = "v.mp4") -> None:
+ invoker.services.names.create_video_name.return_value = video_name
+ invoker.services.configuration.image_subfolder_strategy = "flat"
+ invoker.services.logger = MagicMock()
+
+ def test_record_and_board_relation_rolled_back_when_file_save_fails(self, video_service: VideoService, tmp_path):
+ from invokeai.app.services.video_files.video_files_common import VideoFileSaveException
+
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ self._wire_minimal_create_dependencies(invoker, video_name="ghost.mp4")
+
+ # The DB record save succeeds; the board attach succeeds; the file save explodes.
+ invoker.services.video_records.save.return_value = None
+ invoker.services.board_video_records.add_video_to_board.return_value = None
+ invoker.services.video_files.save.side_effect = VideoFileSaveException("disk full")
+
+ with pytest.raises(VideoFileSaveException):
+ video_service.create(
+ source_path=tmp_path / "src.mp4",
+ width=64,
+ height=64,
+ duration=1.0,
+ fps=24.0,
+ video_origin=ResourceOrigin.EXTERNAL,
+ video_category=ImageCategory.GENERAL,
+ board_id="some-board",
+ )
+
+ # Both the DB record AND the board association must be unwound — otherwise the
+ # gallery would show a ghost video whose file endpoints 404.
+ invoker.services.board_video_records.remove_video_from_board.assert_called_once_with(video_name="ghost.mp4")
+ invoker.services.video_records.delete.assert_called_once_with("ghost.mp4")
+
+ def test_record_rolled_back_when_no_board_and_file_save_fails(self, video_service: VideoService, tmp_path):
+ from invokeai.app.services.video_files.video_files_common import VideoFileSaveException
+
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ self._wire_minimal_create_dependencies(invoker, video_name="solo.mp4")
+ invoker.services.video_records.save.return_value = None
+ invoker.services.video_files.save.side_effect = VideoFileSaveException("disk full")
+
+ with pytest.raises(VideoFileSaveException):
+ video_service.create(
+ source_path=tmp_path / "src.mp4",
+ width=64,
+ height=64,
+ duration=1.0,
+ fps=24.0,
+ video_origin=ResourceOrigin.EXTERNAL,
+ video_category=ImageCategory.GENERAL,
+ board_id=None,
+ )
+
+ # No board attachment was attempted, so no detach call should be made — but the
+ # record must still be rolled back.
+ invoker.services.board_video_records.remove_video_from_board.assert_not_called()
+ invoker.services.video_records.delete.assert_called_once_with("solo.mp4")
+
+ def test_files_rolled_back_when_failure_occurs_after_file_save(self, video_service: VideoService, tmp_path):
+ """The disk layer cleans up after its own save failures, but a failure *after* a
+ successful file save (e.g. building the DTO) must also unwind the files — otherwise
+ they'd sit on disk with no record pointing at them (JPPhoto PR #9163 July-10
+ follow-up)."""
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ self._wire_minimal_create_dependencies(invoker, video_name="late.mp4")
+ invoker.services.video_records.save.return_value = None
+ invoker.services.video_files.save.return_value = None
+ # get_dto reads the record back — make that step explode.
+ invoker.services.video_records.get.side_effect = RuntimeError("db went away")
+
+ with pytest.raises(RuntimeError):
+ video_service.create(
+ source_path=tmp_path / "src.mp4",
+ width=64,
+ height=64,
+ duration=1.0,
+ fps=24.0,
+ video_origin=ResourceOrigin.EXTERNAL,
+ video_category=ImageCategory.GENERAL,
+ board_id=None,
+ )
+
+ invoker.services.video_records.delete.assert_called_once_with("late.mp4")
+ invoker.services.video_files.delete.assert_called_once()
+ assert invoker.services.video_files.delete.call_args.args[0] == "late.mp4"
+
+
+class TestCreateBoardAttachFallback:
+ """Board attachment during create is best-effort, mirroring ImageService.create: a board
+ deleted between the caller's access check and the insert must not destroy a just-generated
+ video. The fallback must be explicit, not silent — the returned DTO reports the actual
+ (missing) board association and a warning is logged (JPPhoto PR #9163 July-10 follow-up).
+ """
+
+ def test_create_succeeds_with_explicit_fallback_when_board_attach_fails(
+ self, video_service: VideoService, tmp_path
+ ):
+ invoker = video_service._VideoService__invoker # type: ignore[attr-defined]
+ invoker.services.names.create_video_name.return_value = "orphan.mp4"
+ invoker.services.configuration.image_subfolder_strategy = "flat"
+ invoker.services.logger = MagicMock()
+
+ invoker.services.video_records.save.return_value = None
+ invoker.services.board_video_records.add_video_to_board.side_effect = Exception("board was deleted")
+ invoker.services.video_files.save.return_value = None
+ # get_dto reads the record and the *actual* board association back.
+ invoker.services.video_records.get.return_value = _make_record(video_name="orphan.mp4")
+ invoker.services.board_video_records.get_board_for_video.return_value = None
+ invoker.services.urls.get_video_url.return_value = "http://localhost/videos/orphan.mp4"
+
+ video_dto = video_service.create(
+ source_path=tmp_path / "src.mp4",
+ width=64,
+ height=64,
+ duration=1.0,
+ fps=24.0,
+ video_origin=ResourceOrigin.EXTERNAL,
+ video_category=ImageCategory.GENERAL,
+ board_id="deleted-board",
+ )
+
+ # The video survives, but the DTO must not claim the requested board attachment
+ # succeeded, and the fallback must be logged.
+ assert video_dto.board_id is None
+ invoker.services.logger.warning.assert_called_once()
+ assert "deleted-board" in invoker.services.logger.warning.call_args.args[0]
+ # Nothing was attached, so nothing should be unwound.
+ invoker.services.board_video_records.remove_video_from_board.assert_not_called()
diff --git a/tests/app/util/test_video_thumbnails.py b/tests/app/util/test_video_thumbnails.py
new file mode 100644
index 00000000000..7869cd67de9
--- /dev/null
+++ b/tests/app/util/test_video_thumbnails.py
@@ -0,0 +1,160 @@
+"""Tests for the subprocess-bounded video decode helpers (PR #9163 review).
+
+The bug: ``probe_video`` / ``extract_video_frame`` decoded untrusted uploads in-process
+with no timeout, despite the module itself noting that cv2 has historically hung on some
+containers. A crafted MP4 that makes the imageio probe fail and then blocks inside
+``cv2.VideoCapture()`` would pin the FastAPI request worker that called it forever;
+repeated uploads could exhaust the worker pool. Decoding now runs in a killable child
+process with a hard timeout.
+
+The hang tests substitute a worker command that never returns and assert the helpers
+fail within a bounded interval; the happy-path tests run the real worker against a real
+synthetic MP4 so the subprocess plumbing is actually validated end to end.
+"""
+
+import sys
+import time
+from pathlib import Path
+from threading import Event
+
+import imageio.v3 as iio
+import numpy as np
+import pytest
+
+from invokeai.app.services.session_processor.session_processor_common import CanceledException
+from invokeai.app.util import video_thumbnails
+from invokeai.app.util.video_thumbnails import decoder_frame_count, extract_video_frame, iter_video_frames, probe_video
+
+FRAMES = 12
+FPS = 8.0
+
+
+@pytest.fixture
+def synthetic_mp4(tmp_path: Path) -> Path:
+ path = tmp_path / "synth.mp4"
+ frames = [np.full((32, 48, 3), 32 + i * 16, dtype=np.uint8) for i in range(FRAMES)]
+ iio.imwrite(path, frames, plugin="FFMPEG", codec="libx264", fps=FPS, macro_block_size=1)
+ return path
+
+
+@pytest.fixture
+def hanging_worker(monkeypatch: pytest.MonkeyPatch):
+ """Replaces the decode worker with a child process that sleeps forever."""
+
+ def _hang_command(*args: str) -> list[str]:
+ return [sys.executable, "-c", "import time; time.sleep(600)"]
+
+ monkeypatch.setattr(video_thumbnails, "_worker_command", _hang_command)
+
+
+class TestHappyPathThroughSubprocess:
+ def test_probe_returns_metadata(self, synthetic_mp4: Path) -> None:
+ width, height, duration, fps = probe_video(synthetic_mp4)
+ assert (width, height) == (48, 32)
+ assert fps == pytest.approx(FPS)
+ assert duration == pytest.approx(FRAMES / FPS, abs=0.5)
+
+ def test_extract_frame_returns_image(self, synthetic_mp4: Path) -> None:
+ frame = extract_video_frame(synthetic_mp4, frame_index=0)
+ assert frame is not None
+ assert frame.size == (48, 32)
+
+ def test_frame_count_matches(self, synthetic_mp4: Path) -> None:
+ assert decoder_frame_count(synthetic_mp4) == FRAMES
+
+
+class TestUnreadableInput:
+ def test_probe_rejects_garbage_bytes(self, tmp_path: Path) -> None:
+ bogus = tmp_path / "junk.mp4"
+ bogus.write_bytes(b"not actually an mp4")
+ with pytest.raises(FileNotFoundError):
+ probe_video(bogus)
+
+ def test_extract_frame_returns_none_for_garbage_bytes(self, tmp_path: Path) -> None:
+ bogus = tmp_path / "junk.mp4"
+ bogus.write_bytes(b"not actually an mp4")
+ assert extract_video_frame(bogus) is None
+
+
+class TestHungDecoderIsBounded:
+ """A decode backend that never returns must fail within the timeout, not hang the caller."""
+
+ TIMEOUT = 2.0
+ # Generous ceiling for CI scheduling jitter; the point is "seconds, not forever".
+ MAX_ELAPSED = 15.0
+
+ def test_probe_fails_within_bounded_interval(self, hanging_worker, tmp_path: Path) -> None:
+ target = tmp_path / "malicious.mp4"
+ target.write_bytes(b"pretend this hangs the decoder")
+ started = time.monotonic()
+ with pytest.raises(FileNotFoundError):
+ probe_video(target, timeout=self.TIMEOUT)
+ assert time.monotonic() - started < self.MAX_ELAPSED
+
+ def test_extract_frame_fails_within_bounded_interval(self, hanging_worker, tmp_path: Path) -> None:
+ target = tmp_path / "malicious.mp4"
+ target.write_bytes(b"pretend this hangs the decoder")
+ started = time.monotonic()
+ assert extract_video_frame(target, timeout=self.TIMEOUT) is None
+ assert time.monotonic() - started < self.MAX_ELAPSED
+
+ def test_frame_count_fails_within_bounded_interval(self, hanging_worker, tmp_path: Path) -> None:
+ target = tmp_path / "malicious.mp4"
+ target.write_bytes(b"pretend this hangs the decoder")
+ started = time.monotonic()
+ assert decoder_frame_count(target, timeout=self.TIMEOUT) is None
+ assert time.monotonic() - started < self.MAX_ELAPSED
+
+
+class TestStreamedDecoderIsBounded:
+ def test_streams_real_frames_through_worker(self, synthetic_mp4: Path) -> None:
+ frames = list(iter_video_frames(synthetic_mp4))
+ assert len(frames) == FRAMES
+ assert frames[0].shape == (32, 48, 3)
+
+ def test_consumer_time_does_not_count_as_decoder_inactivity(self, synthetic_mp4: Path) -> None:
+ frames = iter_video_frames(synthetic_mp4, timeout=2.0)
+ next(frames)
+ # Sleep longer than the inactivity timeout. This time belongs to the consumer and
+ # must not expire the decoder, while the two-second window avoids treating normal
+ # process/FFmpeg scheduling latency on macOS CI as a decoder hang.
+ time.sleep(2.2)
+ assert next(frames).shape == (32, 48, 3)
+
+ def test_times_out_when_worker_stops_producing_frames(self, hanging_worker, tmp_path: Path) -> None:
+ target = tmp_path / "malicious.mp4"
+ target.write_bytes(b"pretend this hangs the decoder")
+ started = time.monotonic()
+ with pytest.raises(TimeoutError, match="Timed out decoding"):
+ next(iter_video_frames(target, timeout=0.2))
+ assert time.monotonic() - started < 5
+
+ def test_cancellation_terminates_blocked_decoder(self, hanging_worker, tmp_path: Path) -> None:
+ target = tmp_path / "malicious.mp4"
+ target.write_bytes(b"pretend this hangs the decoder")
+ canceled = Event()
+ canceled.set()
+ with pytest.raises(CanceledException):
+ next(iter_video_frames(target, timeout=5, is_canceled=canceled.is_set))
+
+
+def test_timeout_kills_worker_descendants(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
+ child_pid_path = tmp_path / "child.pid"
+
+ def _descendant_command(*args: str) -> list[str]:
+ script = (
+ "import pathlib, subprocess, sys, time; "
+ "child = subprocess.Popen([sys.executable, '-c', 'import time; time.sleep(600)']); "
+ "pathlib.Path(sys.argv[1]).write_text(str(child.pid)); "
+ "time.sleep(600)"
+ )
+ return [sys.executable, "-c", script, str(child_pid_path)]
+
+ monkeypatch.setattr(video_thumbnails, "_worker_command", _descendant_command)
+ assert video_thumbnails._run_worker(["probe", "unused"], timeout=0.5) is None
+ child_pid = int(child_pid_path.read_text())
+
+ deadline = time.monotonic() + 5
+ while video_thumbnails._is_process_running(child_pid) and time.monotonic() < deadline:
+ time.sleep(0.05)
+ assert not video_thumbnails._is_process_running(child_pid)
diff --git a/tests/backend/model_manager/configs/test_wan_gguf_config.py b/tests/backend/model_manager/configs/test_wan_gguf_config.py
new file mode 100644
index 00000000000..46b8ce499fe
--- /dev/null
+++ b/tests/backend/model_manager/configs/test_wan_gguf_config.py
@@ -0,0 +1,260 @@
+"""Tests for the GGUF Wan probe (Main_GGUF_Wan_Config)."""
+
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
+
+import gguf
+import pytest
+import torch
+
+from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
+from invokeai.backend.model_manager.configs.main import (
+ Main_GGUF_Wan_Config,
+ _detect_wan_gguf_expert,
+ _detect_wan_gguf_variant,
+ _has_wan_keys,
+ _is_native_wan_layout,
+)
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, WanVariantType
+from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor
+
+
+def _ggml(shape: tuple[int, ...]) -> GGMLTensor:
+ return GGMLTensor(
+ data=torch.zeros((1,), dtype=torch.uint8),
+ ggml_quantization_type=gguf.GGMLQuantizationType.Q4_0,
+ tensor_shape=torch.Size(shape),
+ compute_dtype=torch.float32,
+ )
+
+
+def _wan_a14b_state_dict(prefix: str = "") -> dict:
+ """Synthetic Wan A14B GGUF state dict (16-channel patch embed)."""
+ return {
+ f"{prefix}patch_embedding.weight": _ggml((5120, 16, 1, 2, 2)),
+ f"{prefix}condition_embedder.text_embedder.linear_1.weight": _ggml((5120, 4096)),
+ f"{prefix}blocks.0.attn1.to_q.weight": _ggml((5120, 5120)),
+ f"{prefix}blocks.0.ffn.net.0.proj.weight": _ggml((13824, 5120)),
+ }
+
+
+def _wan_ti2v_state_dict() -> dict:
+ """Synthetic Wan TI2V-5B GGUF state dict (48-channel patch embed)."""
+ return {
+ "patch_embedding.weight": _ggml((3072, 48, 1, 2, 2)),
+ "condition_embedder.text_embedder.linear_1.weight": _ggml((3072, 4096)),
+ "blocks.0.attn1.to_q.weight": _ggml((3072, 3072)),
+ "blocks.0.ffn.net.0.proj.weight": _ggml((14336, 3072)),
+ }
+
+
+def _wan_i2v_a14b_state_dict() -> dict:
+ """Wan 2.2 I2V-A14B GGUF: same shape as T2V except patch_embedding has 36
+ input channels (16 noise + 16 ref-image latents + 4 first-frame mask)."""
+ return {
+ "patch_embedding.weight": _ggml((5120, 36, 1, 2, 2)),
+ "condition_embedder.text_embedder.linear_1.weight": _ggml((5120, 4096)),
+ "blocks.0.attn1.to_q.weight": _ggml((5120, 5120)),
+ "blocks.0.ffn.net.0.proj.weight": _ggml((13824, 5120)),
+ }
+
+
+def _wan_a14b_native_state_dict() -> dict:
+ """Synthetic Wan A14B GGUF state dict using the native upstream key layout
+ (text_embedding/self_attn/cross_attn/ffn.0 — what QuantStack and ComfyUI ship)."""
+ return {
+ "patch_embedding.weight": _ggml((5120, 16, 1, 2, 2)),
+ "text_embedding.0.weight": _ggml((5120, 4096)),
+ "text_embedding.2.weight": _ggml((5120, 5120)),
+ "blocks.0.self_attn.q.weight": _ggml((5120, 5120)),
+ "blocks.0.cross_attn.q.weight": _ggml((5120, 5120)),
+ "blocks.0.ffn.0.weight": _ggml((13824, 5120)),
+ "blocks.0.modulation": _ggml((1, 6, 5120)),
+ "head.head.weight": _ggml((64, 5120)),
+ "head.modulation": _ggml((1, 2, 5120)),
+ }
+
+
+def _build_overrides(model_path: Path, name: str) -> dict:
+ return {
+ "hash": "test-hash",
+ "path": str(model_path),
+ "file_size": 0,
+ "name": name,
+ "source": str(model_path),
+ "source_type": "path",
+ }
+
+
+def _make_mod(path: Path, sd: dict) -> MagicMock:
+ mod = MagicMock()
+ mod.path = path
+ mod.load_state_dict.return_value = sd
+ return mod
+
+
+class TestKeyFingerprint:
+ def test_recognises_bare_keys(self):
+ assert _has_wan_keys(_wan_ti2v_state_dict()) is True
+
+ def test_recognises_comfyui_prefix(self):
+ assert _has_wan_keys(_wan_a14b_state_dict(prefix="model.diffusion_model.")) is True
+
+ def test_recognises_diffusion_model_prefix(self):
+ assert _has_wan_keys(_wan_a14b_state_dict(prefix="diffusion_model.")) is True
+
+ def test_recognises_native_upstream_layout(self):
+ assert _has_wan_keys(_wan_a14b_native_state_dict()) is True
+
+ def test_rejects_qwen_image(self):
+ sd = {"txt_in.weight": _ggml((1, 1)), "img_in.weight": _ggml((1, 1))}
+ assert _has_wan_keys(sd) is False
+
+ def test_rejects_flux(self):
+ sd = {"double_blocks.0.img_attn.proj.weight": _ggml((1, 1))}
+ assert _has_wan_keys(sd) is False
+
+
+class TestNativeLayoutDetection:
+ def test_native_a14b(self):
+ assert _is_native_wan_layout(_wan_a14b_native_state_dict()) is True
+
+ def test_diffusers_a14b_is_not_native(self):
+ assert _is_native_wan_layout(_wan_a14b_state_dict()) is False
+
+ def test_diffusers_ti2v_is_not_native(self):
+ assert _is_native_wan_layout(_wan_ti2v_state_dict()) is False
+
+
+class TestVariantDetection:
+ def test_a14b_from_16ch(self):
+ sd = _wan_a14b_state_dict()
+ assert _detect_wan_gguf_variant(sd) == WanVariantType.T2V_A14B
+
+ def test_ti2v_from_48ch(self):
+ sd = _wan_ti2v_state_dict()
+ assert _detect_wan_gguf_variant(sd) == WanVariantType.TI2V_5B
+
+ def test_i2v_a14b_from_36ch(self):
+ """Wan 2.2 I2V has the same A14B architecture as T2V but with
+ in_channels=36 because the ref-image latents and first-frame mask are
+ concatenated to the noise along the channel dim before patch embedding."""
+ sd = _wan_i2v_a14b_state_dict()
+ assert _detect_wan_gguf_variant(sd) == WanVariantType.I2V_A14B
+
+ def test_unknown_channel_count_returns_none(self):
+ sd = {"patch_embedding.weight": _ggml((1, 32, 1, 2, 2))}
+ assert _detect_wan_gguf_variant(sd) is None
+
+ def test_missing_patch_embedding_returns_none(self):
+ sd = {"blocks.0.attn1.to_q.weight": _ggml((1, 1))}
+ assert _detect_wan_gguf_variant(sd) is None
+
+
+class TestExpertFilenameHeuristic:
+ @pytest.mark.parametrize(
+ "name, expected",
+ [
+ ("wan2.2-t2v-a14b-high_noise-Q4_K_M", "high"),
+ ("Wan2.2-T2V-A14B-High-Noise-Q4_K_M", "high"),
+ ("wan_a14b_highnoise_q4", "high"),
+ ("wan2.2-t2v-a14b-low_noise-Q4_K_M", "low"),
+ ("Wan2.2-A14B-LowNoise-Q4", "low"),
+ ("wan2.2-ti2v-5b-Q4_K_M", "none"),
+ ("wan-A14B-flagship", "none"),
+ ],
+ )
+ def test_filename_heuristic(self, name: str, expected: str):
+ assert _detect_wan_gguf_expert(name) == expected
+
+
+class TestProbe:
+ def test_a14b_high_noise_filename(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan2.2-t2v-a14b-high_noise-Q4_K_M.gguf"
+ f.touch()
+
+ cfg = Main_GGUF_Wan_Config.from_model_on_disk(
+ _make_mod(f, _wan_a14b_state_dict()),
+ _build_overrides(f, "Wan A14B (high)"),
+ )
+ assert cfg.base == BaseModelType.Wan
+ assert cfg.format == ModelFormat.GGUFQuantized
+ assert cfg.variant == WanVariantType.T2V_A14B
+ assert cfg.expert == "high"
+
+ def test_a14b_low_noise_filename(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan2.2-t2v-a14b-low_noise-Q4_K_M.gguf"
+ f.touch()
+
+ cfg = Main_GGUF_Wan_Config.from_model_on_disk(
+ _make_mod(f, _wan_a14b_state_dict()),
+ _build_overrides(f, "Wan A14B (low)"),
+ )
+ assert cfg.expert == "low"
+
+ def test_ti2v_5b_unambiguous(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan2.2-ti2v-5b-Q4_K_M.gguf"
+ f.touch()
+
+ cfg = Main_GGUF_Wan_Config.from_model_on_disk(
+ _make_mod(f, _wan_ti2v_state_dict()),
+ _build_overrides(f, "Wan TI2V-5B"),
+ )
+ assert cfg.variant == WanVariantType.TI2V_5B
+ assert cfg.expert == "none"
+
+ def test_rejects_non_gguf(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan-a14b.safetensors"
+ f.touch()
+ sd = {"patch_embedding.weight": torch.zeros(5120, 16, 1, 2, 2)} # NOT a GGMLTensor
+
+ with pytest.raises(NotAMatchError, match="GGUF"):
+ Main_GGUF_Wan_Config.from_model_on_disk(
+ _make_mod(f, sd),
+ _build_overrides(f, "non-gguf"),
+ )
+
+ def test_rejects_unrecognised_state_dict(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "junk.gguf"
+ f.touch()
+ sd = {"random.key": _ggml((1, 1))}
+
+ with pytest.raises(NotAMatchError, match="Wan transformer"):
+ Main_GGUF_Wan_Config.from_model_on_disk(
+ _make_mod(f, sd),
+ _build_overrides(f, "junk"),
+ )
+
+ def test_native_upstream_a14b_high_noise(self):
+ """QuantStack-style GGUF: native upstream keys + HighNoise filename."""
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "Wan2.2-T2V-A14B-HighNoise-Q4_K_M.gguf"
+ f.touch()
+
+ cfg = Main_GGUF_Wan_Config.from_model_on_disk(
+ _make_mod(f, _wan_a14b_native_state_dict()),
+ _build_overrides(f, "Wan A14B QuantStack (high)"),
+ )
+ assert cfg.base == BaseModelType.Wan
+ assert cfg.format == ModelFormat.GGUFQuantized
+ assert cfg.variant == WanVariantType.T2V_A14B
+ assert cfg.expert == "high"
+
+ def test_explicit_expert_override(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan-a14b-flagship.gguf"
+ f.touch()
+ overrides = _build_overrides(f, "user-tagged")
+ overrides["expert"] = "low"
+
+ cfg = Main_GGUF_Wan_Config.from_model_on_disk(
+ _make_mod(f, _wan_a14b_state_dict()),
+ overrides,
+ )
+ assert cfg.expert == "low"
diff --git a/tests/backend/model_manager/configs/test_wan_lora_config.py b/tests/backend/model_manager/configs/test_wan_lora_config.py
new file mode 100644
index 00000000000..43f55db06b2
--- /dev/null
+++ b/tests/backend/model_manager/configs/test_wan_lora_config.py
@@ -0,0 +1,372 @@
+"""Tests for the Wan LoRA probe (LoRA_LyCORIS_Wan_Config).
+
+These tests cover detection across the three formats Wan LoRAs ship in:
+
+- **Diffusers PEFT**, with or without a ``transformer.`` prefix
+- **Native upstream PEFT** with ``diffusion_model.`` prefix (ComfyUI-trained)
+- **Kohya** ``lora_unet_blocks_N_`` with both diffusers and native
+ attention naming
+
+And the anti-pattern guards that prevent false positives on:
+
+- Anima (Cosmos DiT — ``cross_attn_q_proj`` / ``mlp`` / ``adaln_modulation``)
+- QwenImage (``transformer_blocks.``)
+- Flux (``double_blocks`` / ``single_blocks`` / ``single_transformer_blocks``)
+- Z-Image (``diffusion_model.layers.``)
+"""
+
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
+
+import pytest
+import torch
+
+from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
+from invokeai.backend.model_manager.configs.lora import LoRA_LyCORIS_Wan_Config
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat
+from invokeai.backend.patches.lora_conversions.wan_lora_constants import (
+ has_non_wan_architecture_keys,
+ has_wan_kohya_keys,
+ has_wan_peft_keys,
+)
+
+
+def _make_mod(path: Path, sd: dict) -> MagicMock:
+ mod = MagicMock()
+ mod.path = path
+ mod.load_state_dict.return_value = sd
+ return mod
+
+
+def _overrides(model_path: Path, name: str) -> dict:
+ return {
+ "hash": "test-hash",
+ "path": str(model_path),
+ "file_size": 0,
+ "name": name,
+ "source": str(model_path),
+ "source_type": "path",
+ }
+
+
+def _t(shape: tuple[int, ...]) -> torch.Tensor:
+ return torch.zeros(shape)
+
+
+class TestDiffusersPEFTPositives:
+ def test_attn1_to_q(self):
+ keys = ["transformer.blocks.0.attn1.to_q.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_attn2_to_k(self):
+ keys = ["blocks.0.attn2.to_k.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_ffn_net(self):
+ keys = ["transformer.blocks.0.ffn.net.0.proj.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_base_model_peft_prefix(self):
+ keys = ["base_model.model.transformer.blocks.0.attn1.to_q.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+
+class TestNativePEFTPositives:
+ def test_self_attn_q(self):
+ keys = ["diffusion_model.blocks.0.self_attn.q.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_cross_attn_k(self):
+ keys = ["diffusion_model.blocks.0.cross_attn.k.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_cross_attn_o(self):
+ keys = ["transformer.blocks.0.cross_attn.o.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_ffn_native(self):
+ keys = ["diffusion_model.blocks.0.ffn.0.lora_A.weight"]
+ assert has_wan_peft_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+
+class TestKohyaPositives:
+ def test_kohya_diffusers_attn1_to_q(self):
+ keys = ["lora_unet_blocks_0_attn1_to_q.lora_down.weight"]
+ assert has_wan_kohya_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_kohya_diffusers_attn2_to_out(self):
+ keys = ["lora_unet_blocks_0_attn2_to_out_0.lora_down.weight"]
+ assert has_wan_kohya_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_kohya_native_self_attn_q(self):
+ keys = ["lora_unet_blocks_0_self_attn_q.lora_down.weight"]
+ assert has_wan_kohya_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_kohya_native_cross_attn_v(self):
+ keys = ["lora_unet_blocks_5_cross_attn_v.lora_down.weight"]
+ assert has_wan_kohya_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+ def test_kohya_native_ffn_0(self):
+ keys = ["lora_unet_blocks_0_ffn_0.lora_down.weight"]
+ assert has_wan_kohya_keys(keys) is True
+ assert has_non_wan_architecture_keys(keys) is False
+
+
+class TestArchitectureGuards:
+ """Anti-pattern checks: non-Wan architectures must be flagged so the
+ probe rejects them even if a wan-ish substring matches."""
+
+ @pytest.mark.parametrize(
+ "label, keys",
+ [
+ ("anima_kohya_q_proj", ["lora_unet_blocks_0_cross_attn_q_proj.lora_down.weight"]),
+ ("anima_peft_mlp", ["transformer.blocks.0.mlp.layer1.lora_A.weight"]),
+ ("anima_peft_adaln", ["transformer.blocks.0.adaln_modulation.linear.lora_A.weight"]),
+ ("anima_peft_self_attn_q_proj", ["transformer.blocks.0.self_attn.q_proj.lora_A.weight"]),
+ ("qwen_image", ["transformer_blocks.0.attn.to_q.lora_A.weight"]),
+ ("flux_kohya_double", ["lora_unet_double_blocks_0_img_attn_qkv.lora_down.weight"]),
+ ("flux_kohya_single", ["lora_unet_single_blocks_0_linear1.lora_down.weight"]),
+ ("flux_diffusers_single_transformer", ["transformer.single_transformer_blocks.0.attn.to_q.lora_A.weight"]),
+ ("z_image", ["diffusion_model.layers.0.attn.to_q.lora_A.weight"]),
+ ],
+ )
+ def test_non_wan_archs_are_flagged(self, label: str, keys: list[str]):
+ assert has_non_wan_architecture_keys(keys) is True
+
+
+class TestProbeAcceptance:
+ """End-to-end probe behavior — Wan LoRA must be accepted, non-Wan rejected."""
+
+ def _wan_diffusers_sd(self) -> dict:
+ return {
+ "transformer.blocks.0.attn1.to_q.lora_A.weight": _t((128, 5120)),
+ "transformer.blocks.0.attn1.to_q.lora_B.weight": _t((5120, 128)),
+ "transformer.blocks.0.ffn.net.0.proj.lora_A.weight": _t((128, 5120)),
+ "transformer.blocks.0.ffn.net.0.proj.lora_B.weight": _t((13824, 128)),
+ }
+
+ def _wan_native_sd(self) -> dict:
+ return {
+ "diffusion_model.blocks.0.self_attn.q.lora_A.weight": _t((128, 5120)),
+ "diffusion_model.blocks.0.self_attn.q.lora_B.weight": _t((5120, 128)),
+ }
+
+ def _wan_kohya_sd(self) -> dict:
+ return {
+ "lora_unet_blocks_0_attn1_to_q.lora_down.weight": _t((128, 5120)),
+ "lora_unet_blocks_0_attn1_to_q.lora_up.weight": _t((5120, 128)),
+ }
+
+ def _wan_ti2v5b_sd(self) -> dict:
+ """A TI2V-5B LoRA — inner_dim 3072, not 5120."""
+ return {
+ "transformer.blocks.0.attn1.to_q.lora_A.weight": _t((64, 3072)),
+ "transformer.blocks.0.attn1.to_q.lora_B.weight": _t((3072, 64)),
+ }
+
+ def test_accepts_diffusers_wan(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "my-wan-lora.safetensors"
+ f.touch()
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_diffusers_sd()),
+ _overrides(f, "wan-lora"),
+ )
+ assert cfg.base == BaseModelType.Wan
+ assert cfg.format == ModelFormat.LyCORIS
+ assert cfg.expert is None
+ assert cfg.variant == "a14b" # 5120-dim state dict
+
+ def test_accepts_native_wan(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan-style-lora.safetensors"
+ f.touch()
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_native_sd()),
+ _overrides(f, "wan-native"),
+ )
+ assert cfg.base == BaseModelType.Wan
+
+ def test_accepts_kohya_wan(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan-kohya.safetensors"
+ f.touch()
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_kohya_sd()),
+ _overrides(f, "wan-kohya"),
+ )
+ assert cfg.base == BaseModelType.Wan
+
+ def test_filename_marks_high_noise_expert(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "stylize-high_noise.safetensors"
+ f.touch()
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_diffusers_sd()),
+ _overrides(f, "high-noise lora"),
+ )
+ assert cfg.expert == "high"
+
+ def test_filename_marks_low_noise_expert(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "fine-detail-LowNoise.safetensors"
+ f.touch()
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_diffusers_sd()),
+ _overrides(f, "low-noise lora"),
+ )
+ assert cfg.expert == "low"
+
+ def test_explicit_expert_override_wins(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "ambiguous-name.safetensors"
+ f.touch()
+ overrides = _overrides(f, "override")
+ overrides["expert"] = "low"
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_diffusers_sd()),
+ overrides,
+ )
+ assert cfg.expert == "low"
+
+ def test_expert_none_for_untagged_filename(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "my-lora.safetensors"
+ f.touch()
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_diffusers_sd()),
+ _overrides(f, "untagged"),
+ )
+ assert cfg.expert is None
+
+ def test_variant_detected_as_5b_when_inner_dim_3072(self):
+ """TI2V-5B LoRAs have inner_dim 3072. Detector must classify them as
+ '5b' so the FE filter doesn't route them to an A14B main and crash."""
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "ti2v5b-lora.safetensors"
+ f.touch()
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_ti2v5b_sd()),
+ _overrides(f, "ti2v5b"),
+ )
+ assert cfg.base == BaseModelType.Wan
+ assert cfg.variant == "5b"
+
+ def test_variant_none_when_unrecognised_inner_dim(self):
+ """A future Wan family or a LoRA touching only ffn at non-attn dims
+ should map to variant=None rather than mis-classify."""
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "future-wan.safetensors"
+ f.touch()
+ # Only an ffn LoRA — no attn weight to read inner_dim from.
+ # Also a non-5120, non-3072 dim that would otherwise mis-classify.
+ sd = {
+ "transformer.blocks.0.ffn.net.0.proj.lora_A.weight": _t((128, 4096)),
+ "transformer.blocks.0.ffn.net.0.proj.lora_B.weight": _t((11008, 128)),
+ }
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(_make_mod(f, sd), _overrides(f, "future"))
+ assert cfg.variant is None
+
+ def test_explicit_variant_override_wins(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "manual.safetensors"
+ f.touch()
+ overrides = _overrides(f, "manual")
+ overrides["variant"] = "5b"
+ # State dict is 5120-dim (auto-detect would say "a14b") but the
+ # explicit override should stick.
+ cfg = LoRA_LyCORIS_Wan_Config.from_model_on_disk(
+ _make_mod(f, self._wan_diffusers_sd()),
+ overrides,
+ )
+ assert cfg.variant == "5b"
+
+ def test_rejects_anima_lora(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "anima.safetensors"
+ f.touch()
+ sd = {
+ "transformer.blocks.0.cross_attn.q_proj.lora_A.weight": _t((128, 4096)),
+ "transformer.blocks.0.mlp.layer1.lora_A.weight": _t((128, 4096)),
+ }
+ with pytest.raises(NotAMatchError, match="Wan LoRA"):
+ LoRA_LyCORIS_Wan_Config.from_model_on_disk(_make_mod(f, sd), _overrides(f, "anima"))
+
+ def test_rejects_qwen_image_lora(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "qwen.safetensors"
+ f.touch()
+ sd = {"transformer_blocks.0.attn.to_q.lora_A.weight": _t((128, 4096))}
+ with pytest.raises(NotAMatchError, match="Wan LoRA"):
+ LoRA_LyCORIS_Wan_Config.from_model_on_disk(_make_mod(f, sd), _overrides(f, "qwen"))
+
+ def test_rejects_flux_lora(self):
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "flux.safetensors"
+ f.touch()
+ sd = {"lora_unet_double_blocks_0_img_attn_qkv.lora_down.weight": _t((128, 3072))}
+ with pytest.raises(NotAMatchError, match="Wan LoRA"):
+ LoRA_LyCORIS_Wan_Config.from_model_on_disk(_make_mod(f, sd), _overrides(f, "flux"))
+
+
+class TestProbeMutualExclusivity:
+ """Regression: Anima's probe must REJECT Wan-native LoRA keys, so probing
+ is correct regardless of which config the factory iterates first.
+
+ ``Config_Base.CONFIG_CLASSES`` is a ``set``, so iteration order is
+ non-deterministic across Python process restarts. Probes therefore need
+ to be mutually exclusive at the per-config level — see also
+ ``test_wan_lora_probe_independence.py`` for the broader cross-architecture
+ coverage."""
+
+ def test_anima_rejects_wan_native_lora(self):
+ """Wan native LoRAs (``diffusion_model.blocks.X.self_attn.q.lora_*``)
+ used to false-positive on Anima's probe because Anima accepted any
+ ``cross_attn``/``self_attn`` substring. Anima now requires
+ Cosmos-DiT-exclusive markers (``mlp``, ``adaln_modulation``, or the
+ ``_proj`` attention suffix), so a Wan LoRA — which has none of those —
+ is correctly rejected."""
+ from invokeai.backend.model_manager.configs.lora import LoRA_LyCORIS_Anima_Config
+
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan_native_lora.safetensors"
+ f.touch()
+ # Realistic Wan native PEFT keys — what lightx2v's Lightning
+ # distillations and most ComfyUI-trained Wan LoRAs look like.
+ sd = {
+ "diffusion_model.blocks.0.self_attn.q.lora_A.weight": _t((128, 5120)),
+ "diffusion_model.blocks.0.self_attn.q.lora_B.weight": _t((5120, 128)),
+ "diffusion_model.blocks.0.cross_attn.k.lora_A.weight": _t((128, 5120)),
+ "diffusion_model.blocks.0.cross_attn.k.lora_B.weight": _t((5120, 128)),
+ }
+ with pytest.raises(NotAMatchError, match="Anima LoRA"):
+ LoRA_LyCORIS_Anima_Config.from_model_on_disk(_make_mod(f, sd), _overrides(f, "wan-native-lora"))
+
+ def test_wan_rejects_anima_lora(self):
+ """Mirror direction: a real Anima LoRA must not be matched by Wan.
+ Wan's anti-patterns already cover ``_proj`` suffix, ``mlp``, and
+ ``adaln_modulation``."""
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "anima_lora.safetensors"
+ f.touch()
+ sd = {
+ "transformer.blocks.0.self_attn.q_proj.lora_A.weight": _t((128, 4096)),
+ "transformer.blocks.0.self_attn.q_proj.lora_B.weight": _t((4096, 128)),
+ "transformer.blocks.0.mlp.layer1.lora_A.weight": _t((128, 4096)),
+ "transformer.blocks.0.mlp.layer1.lora_B.weight": _t((4096, 128)),
+ }
+ with pytest.raises(NotAMatchError, match="Wan LoRA"):
+ LoRA_LyCORIS_Wan_Config.from_model_on_disk(_make_mod(f, sd), _overrides(f, "anima-lora"))
diff --git a/tests/backend/model_manager/configs/test_wan_lora_probe_independence.py b/tests/backend/model_manager/configs/test_wan_lora_probe_independence.py
new file mode 100644
index 00000000000..93fdf054639
--- /dev/null
+++ b/tests/backend/model_manager/configs/test_wan_lora_probe_independence.py
@@ -0,0 +1,275 @@
+"""Regression tests for Wan vs Anima LoRA probe mutual exclusivity.
+
+InvokeAI's ``Config_Base.CONFIG_CLASSES`` is a ``set``, so iteration order is
+non-deterministic across Python process restarts. The probe MUST therefore be
+mutually exclusive at the per-config level — first-match-wins is not safe to
+rely on.
+
+The historic bug these tests guard against: Anima's probe accepted anything
+with the ``cross_attn`` or ``self_attn`` substring, which collides with Wan's
+native LoRA key layout (``diffusion_model.blocks.X.cross_attn.q.lora_down.weight``).
+A Wan native LoRA — including lightx2v's Lightning distillations — would
+randomly identify as ``BaseModelType.Anima`` depending on dict hash order.
+
+The fix tightened Anima's probe to require Cosmos-DiT-exclusive markers
+(``mlp``, ``adaln_modulation``, or attention with the ``_proj`` suffix).
+
+Each test below feeds a fixed state dict shape to BOTH the Wan and Anima
+probes individually and asserts at most one accepts — order-independent.
+"""
+
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
+
+import pytest
+import torch
+
+from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
+from invokeai.backend.model_manager.configs.lora import (
+ LoRA_LyCORIS_Anima_Config,
+ LoRA_LyCORIS_Wan_Config,
+)
+from invokeai.backend.model_manager.taxonomy import BaseModelType
+
+
+def _t(shape: tuple[int, ...]) -> torch.Tensor:
+ return torch.zeros(shape)
+
+
+def _make_mod(path: Path, sd: dict) -> MagicMock:
+ mod = MagicMock()
+ mod.path = path
+ mod.load_state_dict.return_value = sd
+ return mod
+
+
+def _overrides(p: Path, name: str) -> dict:
+ return {
+ "hash": "test-hash",
+ "path": str(p),
+ "file_size": 0,
+ "name": name,
+ "source": str(p),
+ "source_type": "path",
+ }
+
+
+def _probe(cls, path: Path, sd: dict, name: str):
+ """Try a probe; return (accepted: bool, instance_or_exc)."""
+ try:
+ return True, cls.from_model_on_disk(_make_mod(path, sd), _overrides(path, name))
+ except NotAMatchError as e:
+ return False, e
+
+
+def _i2v_lightning_v1_keys() -> dict:
+ """Realistic key shape from lightx2v's I2V-A14B Lightning V1 — the actual
+ LoRA that triggered the bug. Native upstream Wan naming with
+ ``diffusion_model.`` prefix, no ``_proj`` suffix on attention."""
+ sd: dict[str, torch.Tensor] = {}
+ for block in range(3):
+ for sub in ("self_attn", "cross_attn"):
+ for proj in ("q", "k", "v", "o"):
+ base = f"diffusion_model.blocks.{block}.{sub}.{proj}"
+ sd[f"{base}.lora_down.weight"] = _t((64, 5120))
+ sd[f"{base}.lora_up.weight"] = _t((5120, 64))
+ sd[f"{base}.alpha"] = torch.tensor(8.0)
+ for ffn_idx in (0, 2):
+ base = f"diffusion_model.blocks.{block}.ffn.{ffn_idx}"
+ sd[f"{base}.lora_down.weight"] = _t((64, 5120))
+ sd[f"{base}.lora_up.weight"] = _t((5120, 64))
+ sd[f"{base}.alpha"] = torch.tensor(8.0)
+ return sd
+
+
+def _t2v_lightning_v2_keys() -> dict:
+ """Same layout as I2V Lightning — both lightx2v releases use native Wan
+ keys with ``diffusion_model.`` prefix. The T2V version had been working
+ (after a manual factory reorder), but only by luck of dict-hash order."""
+ return _i2v_lightning_v1_keys() # structurally identical to I2V V1
+
+
+def _wan_kohya_keys() -> dict:
+ """Hypothetical Kohya-format Wan LoRA — same native naming, underscore
+ separators. Lightning hasn't shipped in this format, but other community
+ LoRAs do."""
+ sd: dict[str, torch.Tensor] = {}
+ for block in range(2):
+ for sub in ("self_attn", "cross_attn"):
+ for proj in ("q", "k", "v", "o"):
+ base = f"lora_unet_blocks_{block}_{sub}_{proj}"
+ sd[f"{base}.lora_down.weight"] = _t((64, 5120))
+ sd[f"{base}.lora_up.weight"] = _t((5120, 64))
+ return sd
+
+
+def _wan_diffusers_peft_keys() -> dict:
+ """Wan diffusers-style LoRA: ``transformer.blocks.X.attn1.to_q.lora_A.weight``
+ etc. Distinct enough from Anima that even the loose probes wouldn't collide,
+ but covered here for completeness."""
+ sd: dict[str, torch.Tensor] = {}
+ for block in range(2):
+ for attn in ("attn1", "attn2"):
+ for to in ("to_q", "to_k", "to_v"):
+ base = f"transformer.blocks.{block}.{attn}.{to}"
+ sd[f"{base}.lora_A.weight"] = _t((64, 5120))
+ sd[f"{base}.lora_B.weight"] = _t((5120, 64))
+ sd[f"transformer.blocks.{block}.ffn.net.0.proj.lora_A.weight"] = _t((64, 5120))
+ sd[f"transformer.blocks.{block}.ffn.net.0.proj.lora_B.weight"] = _t((13824, 64))
+ return sd
+
+
+def _anima_peft_keys() -> dict:
+ """Realistic Anima Cosmos-DiT LoRA: ``q_proj``/``k_proj`` attention naming
+ plus ``mlp`` and ``adaln_modulation`` modules. Wan has none of these."""
+ sd: dict[str, torch.Tensor] = {}
+ for block in range(2):
+ for sub in ("self_attn", "cross_attn"):
+ for proj in ("q_proj", "k_proj", "v_proj", "output_proj"):
+ base = f"transformer.blocks.{block}.{sub}.{proj}"
+ sd[f"{base}.lora_A.weight"] = _t((64, 4096))
+ sd[f"{base}.lora_B.weight"] = _t((4096, 64))
+ sd[f"transformer.blocks.{block}.mlp.layer1.lora_A.weight"] = _t((64, 4096))
+ sd[f"transformer.blocks.{block}.mlp.layer1.lora_B.weight"] = _t((4096, 64))
+ sd[f"transformer.blocks.{block}.adaln_modulation.linear.lora_A.weight"] = _t((64, 4096))
+ sd[f"transformer.blocks.{block}.adaln_modulation.linear.lora_B.weight"] = _t((4096, 64))
+ return sd
+
+
+def _anima_kohya_keys() -> dict:
+ """Same Anima content in Kohya format."""
+ sd: dict[str, torch.Tensor] = {}
+ for block in range(2):
+ for sub in ("self_attn", "cross_attn"):
+ for proj in ("q_proj", "k_proj", "v_proj", "output_proj"):
+ base = f"lora_unet_blocks_{block}_{sub}_{proj}"
+ sd[f"{base}.lora_down.weight"] = _t((64, 4096))
+ sd[f"{base}.lora_up.weight"] = _t((4096, 64))
+ sd[f"lora_unet_blocks_{block}_mlp_layer1.lora_down.weight"] = _t((64, 4096))
+ sd[f"lora_unet_blocks_{block}_mlp_layer1.lora_up.weight"] = _t((4096, 64))
+ return sd
+
+
+# ---------------------------------------------------------------------------
+# Mutual-exclusivity assertions
+# ---------------------------------------------------------------------------
+
+
+@pytest.mark.parametrize(
+ "label, sd_builder",
+ [
+ ("i2v_lightning_v1", _i2v_lightning_v1_keys),
+ ("t2v_lightning_v2", _t2v_lightning_v2_keys),
+ ("wan_kohya_native", _wan_kohya_keys),
+ ("wan_diffusers_peft", _wan_diffusers_peft_keys),
+ ],
+)
+def test_wan_loras_only_match_wan(label: str, sd_builder) -> None:
+ """Wan probe accepts; Anima probe rejects. Independent of factory order."""
+ sd = sd_builder()
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / f"{label}.safetensors"
+ f.touch()
+
+ wan_ok, wan_result = _probe(LoRA_LyCORIS_Wan_Config, f, sd, label)
+ anima_ok, anima_result = _probe(LoRA_LyCORIS_Anima_Config, f, sd, label)
+
+ assert wan_ok, f"Wan probe must accept {label}; got {wan_result}"
+ assert wan_result.base == BaseModelType.Wan
+ assert not anima_ok, (
+ f"Anima probe must reject {label} so probing is order-independent. Instead it accepted: {anima_result}"
+ )
+
+
+@pytest.mark.parametrize(
+ "label, sd_builder",
+ [
+ ("anima_peft", _anima_peft_keys),
+ ("anima_kohya", _anima_kohya_keys),
+ ],
+)
+def test_anima_loras_only_match_anima(label: str, sd_builder) -> None:
+ """Anima probe accepts; Wan probe rejects. Independent of factory order."""
+ sd = sd_builder()
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / f"{label}.safetensors"
+ f.touch()
+
+ wan_ok, wan_result = _probe(LoRA_LyCORIS_Wan_Config, f, sd, label)
+ anima_ok, anima_result = _probe(LoRA_LyCORIS_Anima_Config, f, sd, label)
+
+ assert anima_ok, f"Anima probe must accept {label}; got {anima_result}"
+ assert anima_result.base == BaseModelType.Anima
+ assert not wan_ok, (
+ f"Wan probe must reject {label} so probing is order-independent. Instead it accepted: {wan_result}"
+ )
+
+
+# ---------------------------------------------------------------------------
+# Belt-and-suspenders: confirm CONFIG_CLASSES doesn't ALSO produce a match for
+# any unrelated LoRA config. This is the test that would have caught the
+# original bug regardless of which LoRA configs are registered in the future.
+# ---------------------------------------------------------------------------
+
+
+def test_at_most_one_lora_config_matches_wan_lightning() -> None:
+ """Run every LoRA config in the factory against an I2V Lightning state
+ dict. Only one should accept. If a future LoRA config (a hypothetical
+ new model with cross_attn naming) starts matching too, this test fires
+ so we can tighten that probe rather than relying on factory ordering."""
+ from invokeai.backend.model_manager.configs.base import Config_Base
+ from invokeai.backend.model_manager.taxonomy import ModelType
+
+ sd = _i2v_lightning_v1_keys()
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "wan_lightning.safetensors"
+ f.touch()
+ mod = _make_mod(f, sd)
+ overrides = _overrides(f, "wan_lightning")
+
+ accepting: list[str] = []
+ for cls in Config_Base.CONFIG_CLASSES:
+ # Only LoRA configs are at risk of collision with each other; skip
+ # the rest. (Main models can also probe-accept-then-reject on type
+ # mismatch, but they're disambiguated by ``matches_sort_key``.)
+ if getattr(cls.model_fields.get("type", None), "default", None) != ModelType.LoRA:
+ continue
+ try:
+ cls.from_model_on_disk(mod, dict(overrides))
+ accepting.append(cls.__name__)
+ except (NotAMatchError, Exception):
+ continue
+
+ assert accepting == ["LoRA_LyCORIS_Wan_Config"], (
+ f"Exactly one LoRA config must accept a Wan Lightning LoRA; got {accepting}. "
+ "If a new LoRA config starts matching here, tighten its probe to be "
+ "mutually exclusive with Wan rather than relying on factory ordering."
+ )
+
+
+def test_at_most_one_lora_config_matches_anima_peft() -> None:
+ """Same exclusivity guarantee for the Anima side."""
+ from invokeai.backend.model_manager.configs.base import Config_Base
+ from invokeai.backend.model_manager.taxonomy import ModelType
+
+ sd = _anima_peft_keys()
+ with TemporaryDirectory() as tmp:
+ f = Path(tmp) / "anima_peft.safetensors"
+ f.touch()
+ mod = _make_mod(f, sd)
+ overrides = _overrides(f, "anima_peft")
+
+ accepting: list[str] = []
+ for cls in Config_Base.CONFIG_CLASSES:
+ if getattr(cls.model_fields.get("type", None), "default", None) != ModelType.LoRA:
+ continue
+ try:
+ cls.from_model_on_disk(mod, dict(overrides))
+ accepting.append(cls.__name__)
+ except (NotAMatchError, Exception):
+ continue
+
+ assert accepting == ["LoRA_LyCORIS_Anima_Config"], (
+ f"Exactly one LoRA config must accept an Anima LoRA; got {accepting}."
+ )
diff --git a/tests/backend/model_manager/configs/test_wan_main_config.py b/tests/backend/model_manager/configs/test_wan_main_config.py
new file mode 100644
index 00000000000..d3f4f00451e
--- /dev/null
+++ b/tests/backend/model_manager/configs/test_wan_main_config.py
@@ -0,0 +1,152 @@
+"""Tests for Wan 2.2 model identification (Main_Diffusers_Wan_Config)."""
+
+import json
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
+
+import pytest
+
+from invokeai.backend.model_manager.configs.main import Main_Diffusers_Wan_Config
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, WanVariantType
+
+
+def _write_json(path: Path, data: dict) -> None:
+ path.parent.mkdir(parents=True, exist_ok=True)
+ with path.open("w") as f:
+ json.dump(data, f)
+
+
+def _build_a14b_layout(root: Path) -> None:
+ """Synthetic on-disk layout for Wan-AI/Wan2.2-T2V-A14B: dual transformers, z_dim=16."""
+ _write_json(root / "model_index.json", {"_class_name": "WanPipeline"})
+ _write_json(root / "transformer" / "config.json", {"_class_name": "WanTransformer3DModel", "in_channels": 16})
+ _write_json(root / "transformer_2" / "config.json", {"_class_name": "WanTransformer3DModel", "in_channels": 16})
+ _write_json(root / "vae" / "config.json", {"_class_name": "AutoencoderKLWan", "z_dim": 16})
+
+
+def _build_ti2v_5b_layout(root: Path) -> None:
+ """Synthetic on-disk layout for Wan-AI/Wan2.2-TI2V-5B: single transformer, z_dim=48."""
+ _write_json(root / "model_index.json", {"_class_name": "WanImageToVideoPipeline"})
+ _write_json(root / "transformer" / "config.json", {"_class_name": "WanTransformer3DModel", "in_channels": 48})
+ _write_json(root / "vae" / "config.json", {"_class_name": "AutoencoderKLWan", "z_dim": 48})
+
+
+def _build_i2v_a14b_layout(root: Path) -> None:
+ """Wan-AI/Wan2.2-I2V-A14B: dual transformers, z_dim=16, transformer in_channels=36.
+
+ The Wan 2.2 I2V transformer concatenates noise latents (16) + ref-image
+ latents (16) + first-frame mask (4) along the channel dim, so its
+ ``in_channels`` is 36 vs 16 for T2V.
+ """
+ _write_json(root / "model_index.json", {"_class_name": "WanImageToVideoPipeline"})
+ _write_json(
+ root / "transformer" / "config.json",
+ {"_class_name": "WanTransformer3DModel", "in_channels": 36, "image_dim": None},
+ )
+ _write_json(
+ root / "transformer_2" / "config.json",
+ {"_class_name": "WanTransformer3DModel", "in_channels": 36, "image_dim": None},
+ )
+ _write_json(root / "vae" / "config.json", {"_class_name": "AutoencoderKLWan", "z_dim": 16})
+
+
+def _build_overrides(model_path: Path, name: str) -> dict:
+ return {
+ "hash": "test-hash",
+ "path": str(model_path),
+ "file_size": 0,
+ "name": name,
+ "source": str(model_path),
+ "source_type": "path",
+ }
+
+
+def _make_mod(model_path: Path) -> MagicMock:
+ mod = MagicMock()
+ mod.path = model_path
+ return mod
+
+
+class TestWanDiffusersIdentification:
+ """Wan diffusers probe: variant detection from transformer / VAE / dir layout."""
+
+ def test_a14b_detected_from_dual_transformer(self) -> None:
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "Wan2.2-T2V-A14B"
+ _build_a14b_layout(root)
+
+ cfg = Main_Diffusers_Wan_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "A14B"))
+
+ assert cfg.base == BaseModelType.Wan
+ assert cfg.format == ModelFormat.Diffusers
+ assert cfg.variant == WanVariantType.T2V_A14B
+ assert cfg.has_dual_expert is True
+
+ def test_i2v_a14b_detected_from_in_channels_36(self) -> None:
+ """I2V-A14B has the same dual-expert + z_dim=16 layout as T2V, but its
+ transformer's ``in_channels`` is 36 (16 noise + 16 ref-image latents +
+ 4 first-frame mask). That's the canonical Wan 2.2 differentiator."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "Wan2.2-I2V-A14B"
+ _build_i2v_a14b_layout(root)
+
+ cfg = Main_Diffusers_Wan_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "I2V"))
+
+ assert cfg.variant == WanVariantType.I2V_A14B
+ assert cfg.has_dual_expert is True
+
+ def test_t2v_a14b_kept_when_in_channels_is_16(self) -> None:
+ """A14B layout with ``in_channels=16`` resolves to T2V (not I2V)."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "Wan2.2-T2V-A14B"
+ _build_a14b_layout(root)
+
+ cfg = Main_Diffusers_Wan_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "T2V"))
+
+ assert cfg.variant == WanVariantType.T2V_A14B
+
+ def test_ti2v_5b_detected_from_z_dim(self) -> None:
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "Wan2.2-TI2V-5B"
+ _build_ti2v_5b_layout(root)
+
+ cfg = Main_Diffusers_Wan_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "TI2V-5B"))
+
+ assert cfg.variant == WanVariantType.TI2V_5B
+ assert cfg.has_dual_expert is False
+
+ def test_filename_heuristic_when_vae_config_missing(self) -> None:
+ """When ``vae/config.json`` is missing, fall back to the directory name."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "Wan2.2-TI2V-5B"
+ _write_json(root / "model_index.json", {"_class_name": "WanPipeline"})
+ _write_json(root / "transformer" / "config.json", {"_class_name": "WanTransformer3DModel"})
+ # No vae/config.json — single-transformer + dirname containing "5b" → TI2V-5B.
+
+ cfg = Main_Diffusers_Wan_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "TI2V-5B"))
+
+ assert cfg.variant == WanVariantType.TI2V_5B
+
+ def test_explicit_variant_override_takes_precedence(self) -> None:
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "wan-something"
+ _build_a14b_layout(root)
+ overrides = _build_overrides(root, "Custom A14B")
+ overrides["variant"] = WanVariantType.TI2V_5B # Explicit override.
+
+ cfg = Main_Diffusers_Wan_Config.from_model_on_disk(_make_mod(root), overrides)
+ assert cfg.variant == WanVariantType.TI2V_5B
+ # has_dual_expert is still detected from disk; the override only forces variant.
+ assert cfg.has_dual_expert is True
+
+ def test_rejects_non_wan_pipeline(self) -> None:
+ """A model_index.json that isn't a Wan class name must not match."""
+ from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
+
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "not-wan"
+ _write_json(root / "model_index.json", {"_class_name": "FluxPipeline"})
+
+ with pytest.raises(NotAMatchError):
+ Main_Diffusers_Wan_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "fake"))
diff --git a/tests/backend/model_manager/configs/test_wan_t5_encoder_config.py b/tests/backend/model_manager/configs/test_wan_t5_encoder_config.py
new file mode 100644
index 00000000000..4a5732bc10a
--- /dev/null
+++ b/tests/backend/model_manager/configs/test_wan_t5_encoder_config.py
@@ -0,0 +1,96 @@
+"""Tests for the WanT5Encoder config probe (UMT5-XXL diffusers folder)."""
+
+import json
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
+
+import pytest
+
+from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
+from invokeai.backend.model_manager.configs.wan_t5_encoder import WanT5Encoder_WanT5Encoder_Config
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType
+
+
+def _build_overrides(model_path: Path, name: str) -> dict:
+ return {
+ "hash": "test-hash",
+ "path": str(model_path),
+ "file_size": 0,
+ "name": name,
+ "source": str(model_path),
+ "source_type": "path",
+ }
+
+
+def _make_mod(model_path: Path) -> MagicMock:
+ mod = MagicMock()
+ mod.path = model_path
+ return mod
+
+
+def _write_encoder_config(target: Path, model_type: str) -> None:
+ target.parent.mkdir(parents=True, exist_ok=True)
+ with target.open("w") as f:
+ json.dump({"model_type": model_type, "architectures": ["UMT5EncoderModel"]}, f)
+
+
+class TestWanT5EncoderProbe:
+ def test_accepts_nested_text_encoder_layout(self):
+ """Standard layout: /text_encoder/config.json with model_type=umt5."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "wan-encoder-bundle"
+ root.mkdir()
+ _write_encoder_config(root / "text_encoder" / "config.json", "umt5")
+
+ cfg = WanT5Encoder_WanT5Encoder_Config.from_model_on_disk(
+ _make_mod(root), _build_overrides(root, "wan-encoder")
+ )
+
+ assert cfg.base == BaseModelType.Any
+ assert cfg.type == ModelType.WanT5Encoder
+ assert cfg.format == ModelFormat.WanT5Encoder
+
+ def test_accepts_flat_encoder_layout(self):
+ """Flat layout: /config.json directly (just the encoder folder)."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "umt5-xxl"
+ root.mkdir()
+ _write_encoder_config(root / "config.json", "umt5")
+
+ cfg = WanT5Encoder_WanT5Encoder_Config.from_model_on_disk(
+ _make_mod(root), _build_overrides(root, "umt5-xxl")
+ )
+ assert cfg.format == ModelFormat.WanT5Encoder
+
+ def test_rejects_t5(self):
+ """A regular T5-XXL encoder must not match (different vocabulary)."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "t5-xxl"
+ root.mkdir()
+ _write_encoder_config(root / "config.json", "t5")
+
+ with pytest.raises(NotAMatchError, match="not 'umt5'"):
+ WanT5Encoder_WanT5Encoder_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "t5-xxl"))
+
+ def test_rejects_full_pipeline(self):
+ """A folder with model_index.json or transformer/ is a full pipeline, not an encoder."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "full-pipeline"
+ root.mkdir()
+ _write_encoder_config(root / "text_encoder" / "config.json", "umt5")
+ (root / "model_index.json").touch()
+
+ with pytest.raises(NotAMatchError, match="full Wan pipeline"):
+ WanT5Encoder_WanT5Encoder_Config.from_model_on_disk(
+ _make_mod(root), _build_overrides(root, "full-pipeline")
+ )
+
+ def test_rejects_missing_config(self):
+ """Empty directory has no encoder config to read."""
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "empty"
+ root.mkdir()
+
+ with pytest.raises(NotAMatchError, match="no encoder config"):
+ WanT5Encoder_WanT5Encoder_Config.from_model_on_disk(_make_mod(root), _build_overrides(root, "empty"))
diff --git a/tests/backend/model_manager/configs/test_wan_vae_config.py b/tests/backend/model_manager/configs/test_wan_vae_config.py
new file mode 100644
index 00000000000..21c3f42a7b8
--- /dev/null
+++ b/tests/backend/model_manager/configs/test_wan_vae_config.py
@@ -0,0 +1,173 @@
+"""Tests for Wan 2.2 VAE config probes (checkpoint + diffusers)."""
+
+import json
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
+
+import pytest
+import torch
+
+from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
+from invokeai.backend.model_manager.configs.vae import (
+ VAE_Checkpoint_QwenImage_Config,
+ VAE_Checkpoint_Wan_Config,
+ VAE_Diffusers_Wan_Config,
+ _wan_vae_z_dim,
+)
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat
+
+
+def _build_overrides(model_path: Path, name: str) -> dict:
+ return {
+ "hash": "test-hash",
+ "path": str(model_path),
+ "file_size": 0,
+ "name": name,
+ "source": str(model_path),
+ "source_type": "path",
+ }
+
+
+def _make_mod(model_path: Path, state_dict: dict | None = None) -> MagicMock:
+ mod = MagicMock()
+ mod.path = model_path
+ if state_dict is not None:
+ mod.load_state_dict.return_value = state_dict
+ return mod
+
+
+def _wan_vae_state_dict(z_dim: int) -> dict:
+ """Synthetic 5D Wan-style VAE state dict."""
+ return {
+ "decoder.conv_in.weight": torch.zeros(96, z_dim, 1, 3, 3),
+ "encoder.conv_in.weight": torch.zeros(z_dim, 3, 1, 3, 3),
+ }
+
+
+class TestZDimDetection:
+ def test_detects_16_channel(self):
+ assert _wan_vae_z_dim(_wan_vae_state_dict(16)) == 16
+
+ def test_detects_48_channel(self):
+ assert _wan_vae_z_dim(_wan_vae_state_dict(48)) == 48
+
+ def test_rejects_unknown_z_dim(self):
+ # Some other 5D conv weight (not Wan).
+ sd = {"decoder.conv_in.weight": torch.zeros(96, 32, 1, 3, 3)}
+ assert _wan_vae_z_dim(sd) is None
+
+ def test_rejects_4d_conv(self):
+ # Standard SD/SDXL 4D conv — not Wan.
+ sd = {"decoder.conv_in.weight": torch.zeros(96, 16, 3, 3)}
+ assert _wan_vae_z_dim(sd) is None
+
+
+class TestVAECheckpointWanConfig:
+ """Probe + filename-heuristic disambiguation from Qwen Image VAE."""
+
+ def test_48_channel_unambiguous_wan(self):
+ with TemporaryDirectory() as tmp:
+ vae_path = Path(tmp) / "wan2.2-vae.safetensors"
+ vae_path.touch()
+
+ cfg = VAE_Checkpoint_Wan_Config.from_model_on_disk(
+ _make_mod(vae_path, state_dict=_wan_vae_state_dict(48)),
+ _build_overrides(vae_path, "Wan2.2-VAE"),
+ )
+
+ assert cfg.base == BaseModelType.Wan
+ assert cfg.format == ModelFormat.Checkpoint
+ assert cfg.latent_channels == 48
+
+ def test_16_channel_with_wan_in_filename(self):
+ with TemporaryDirectory() as tmp:
+ vae_path = Path(tmp) / "wan-vae.safetensors"
+ vae_path.touch()
+
+ cfg = VAE_Checkpoint_Wan_Config.from_model_on_disk(
+ _make_mod(vae_path, state_dict=_wan_vae_state_dict(16)),
+ _build_overrides(vae_path, "Wan VAE"),
+ )
+
+ assert cfg.latent_channels == 16
+
+ def test_16_channel_without_wan_in_filename_defers(self):
+ """Filename without 'wan' should let Qwen Image VAE win."""
+ with TemporaryDirectory() as tmp:
+ vae_path = Path(tmp) / "qwen_vae.safetensors"
+ vae_path.touch()
+
+ with pytest.raises(NotAMatchError, match="deferring to Qwen Image"):
+ VAE_Checkpoint_Wan_Config.from_model_on_disk(
+ _make_mod(vae_path, state_dict=_wan_vae_state_dict(16)),
+ _build_overrides(vae_path, "QwenImage VAE"),
+ )
+
+ def test_qwen_image_defers_when_filename_says_wan(self):
+ """The mirror case — QwenImage config refuses files whose filenames suggest Wan."""
+ with TemporaryDirectory() as tmp:
+ vae_path = Path(tmp) / "wan-vae.safetensors"
+ vae_path.touch()
+
+ with pytest.raises(NotAMatchError, match="filename suggests a Wan"):
+ VAE_Checkpoint_QwenImage_Config.from_model_on_disk(
+ _make_mod(vae_path, state_dict=_wan_vae_state_dict(16)),
+ _build_overrides(vae_path, "Wan VAE"),
+ )
+
+ def test_rejects_non_wan_state_dict(self):
+ with TemporaryDirectory() as tmp:
+ vae_path = Path(tmp) / "wan-junk.safetensors"
+ vae_path.touch()
+ sd = {"foo.bar": torch.zeros(1)}
+
+ with pytest.raises(NotAMatchError):
+ VAE_Checkpoint_Wan_Config.from_model_on_disk(
+ _make_mod(vae_path, state_dict=sd),
+ _build_overrides(vae_path, "junk"),
+ )
+
+
+class TestVAEDiffusersWanConfig:
+ """Diffusers-folder probe; latent_channels read from vae/config.json."""
+
+ def test_z_dim_from_config_json(self):
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "Wan2.2-VAE"
+ root.mkdir()
+ with (root / "config.json").open("w") as f:
+ json.dump({"_class_name": "AutoencoderKLWan", "z_dim": 48}, f)
+
+ cfg = VAE_Diffusers_Wan_Config.from_model_on_disk(
+ _make_mod(root),
+ _build_overrides(root, "Wan2.2-VAE"),
+ )
+ assert cfg.latent_channels == 48
+ assert cfg.format == ModelFormat.Diffusers
+
+ def test_default_to_16_when_z_dim_missing(self):
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "Wan-VAE"
+ root.mkdir()
+ with (root / "config.json").open("w") as f:
+ json.dump({"_class_name": "AutoencoderKLWan"}, f) # No z_dim.
+
+ cfg = VAE_Diffusers_Wan_Config.from_model_on_disk(
+ _make_mod(root),
+ _build_overrides(root, "Wan-VAE"),
+ )
+ assert cfg.latent_channels == 16
+
+ def test_rejects_non_wan_class(self):
+ with TemporaryDirectory() as tmp:
+ root = Path(tmp) / "FluxVAE"
+ root.mkdir()
+ with (root / "config.json").open("w") as f:
+ json.dump({"_class_name": "AutoencoderKL"}, f)
+
+ with pytest.raises(NotAMatchError):
+ VAE_Diffusers_Wan_Config.from_model_on_disk(
+ _make_mod(root),
+ _build_overrides(root, "FluxVAE"),
+ )
diff --git a/tests/backend/model_manager/load/test_wan_loader.py b/tests/backend/model_manager/load/test_wan_loader.py
new file mode 100644
index 00000000000..31d30522446
--- /dev/null
+++ b/tests/backend/model_manager/load/test_wan_loader.py
@@ -0,0 +1,175 @@
+"""Tests for Wan loader helpers (native -> diffusers key conversion)."""
+
+import gguf
+import torch
+
+from invokeai.backend.model_manager.load.model_loaders.wan import (
+ _convert_wan_native_to_diffusers,
+ _unwrap_unquantized_to_compute_dtype,
+)
+from invokeai.backend.quantization.gguf.ggml_tensor import GGMLTensor
+
+
+def test_converts_text_and_time_embedders():
+ sd = {
+ "text_embedding.0.weight": "a",
+ "text_embedding.0.bias": "b",
+ "text_embedding.2.weight": "c",
+ "time_embedding.0.weight": "d",
+ "time_embedding.2.weight": "e",
+ "time_projection.1.weight": "f",
+ }
+ out = _convert_wan_native_to_diffusers(sd)
+ assert "condition_embedder.text_embedder.linear_1.weight" in out
+ assert "condition_embedder.text_embedder.linear_1.bias" in out
+ assert "condition_embedder.text_embedder.linear_2.weight" in out
+ assert "condition_embedder.time_embedder.linear_1.weight" in out
+ assert "condition_embedder.time_embedder.linear_2.weight" in out
+ assert "condition_embedder.time_proj.weight" in out
+
+
+def test_converts_attention_blocks():
+ sd = {
+ "blocks.0.self_attn.q.weight": 1,
+ "blocks.0.self_attn.k.weight": 2,
+ "blocks.0.self_attn.v.weight": 3,
+ "blocks.0.self_attn.o.weight": 4,
+ "blocks.0.self_attn.norm_q.weight": 5,
+ "blocks.0.self_attn.norm_k.weight": 6,
+ "blocks.0.cross_attn.q.weight": 7,
+ "blocks.0.cross_attn.k.weight": 8,
+ "blocks.0.cross_attn.v.weight": 9,
+ "blocks.0.cross_attn.o.weight": 10,
+ }
+ out = _convert_wan_native_to_diffusers(sd)
+ assert "blocks.0.attn1.to_q.weight" in out
+ assert "blocks.0.attn1.to_k.weight" in out
+ assert "blocks.0.attn1.to_v.weight" in out
+ assert "blocks.0.attn1.to_out.0.weight" in out
+ assert "blocks.0.attn1.norm_q.weight" in out
+ assert "blocks.0.attn1.norm_k.weight" in out
+ assert "blocks.0.attn2.to_q.weight" in out
+ assert "blocks.0.attn2.to_out.0.weight" in out
+
+
+def test_converts_ffn_and_modulation():
+ sd = {
+ "blocks.0.ffn.0.weight": 1,
+ "blocks.0.ffn.0.bias": 2,
+ "blocks.0.ffn.2.weight": 3,
+ "blocks.0.modulation": 4,
+ }
+ out = _convert_wan_native_to_diffusers(sd)
+ assert "blocks.0.ffn.net.0.proj.weight" in out
+ assert "blocks.0.ffn.net.0.proj.bias" in out
+ assert "blocks.0.ffn.net.2.weight" in out
+ assert "blocks.0.scale_shift_table" in out
+
+
+def test_swaps_norm2_and_norm3():
+ """Native norm3 has params (cross-attn norm in diffusers norm2 slot)
+ while native norm2 is the elementwise-affine-False norm. The swap
+ via placeholder must not collide."""
+ sd = {
+ "blocks.0.norm2.weight": "native_norm2",
+ "blocks.0.norm3.weight": "native_norm3",
+ }
+ out = _convert_wan_native_to_diffusers(sd)
+ assert out["blocks.0.norm3.weight"] == "native_norm2"
+ assert out["blocks.0.norm2.weight"] == "native_norm3"
+
+
+def test_converts_head_keys():
+ sd = {
+ "head.head.weight": 1,
+ "head.head.bias": 2,
+ "head.modulation": 3,
+ }
+ out = _convert_wan_native_to_diffusers(sd)
+ assert "proj_out.weight" in out
+ assert "proj_out.bias" in out
+ assert "scale_shift_table" in out
+
+
+def test_diffusers_keys_pass_through_unchanged():
+ """If a state dict is already in diffusers form, the substring rules
+ must be no-ops — none of the native fingerprints are present."""
+ sd = {
+ "patch_embedding.weight": 1,
+ "condition_embedder.text_embedder.linear_1.weight": 2,
+ "blocks.0.attn1.to_q.weight": 3,
+ "blocks.0.ffn.net.0.proj.weight": 4,
+ "scale_shift_table": 5,
+ "proj_out.weight": 6,
+ }
+ out = _convert_wan_native_to_diffusers(sd)
+ assert set(out.keys()) == set(sd.keys())
+ assert all(out[k] == sd[k] for k in sd)
+
+
+def test_does_not_mutate_input():
+ sd = {"text_embedding.0.weight": 1}
+ snapshot = dict(sd)
+ _convert_wan_native_to_diffusers(sd)
+ assert sd == snapshot
+
+
+def test_non_string_keys_pass_through():
+ sd = {0: "ignored", "text_embedding.0.weight": "renamed"}
+ out = _convert_wan_native_to_diffusers(sd)
+ assert out[0] == "ignored"
+ assert "condition_embedder.text_embedder.linear_1.weight" in out
+
+
+def _ggml(data: torch.Tensor, qtype: gguf.GGMLQuantizationType, compute_dtype: torch.dtype) -> GGMLTensor:
+ return GGMLTensor(
+ data=data,
+ ggml_quantization_type=qtype,
+ tensor_shape=data.shape,
+ compute_dtype=compute_dtype,
+ )
+
+
+class TestUnwrapUnquantized:
+ """The QuantStack GGUFs store ``patch_embedding.bias`` as F16 while latents
+ flow through the model as bf16. Conv3d isn't in GGMLTensor's dispatch table,
+ so without unwrapping the F16 wrapper goes into conv3d as-is and crashes
+ with ``Input type (c10::BFloat16) and bias type (c10::Half) should be the same``.
+ These tests guard the unwrap step that prevents that."""
+
+ def test_f16_compatible_qtype_is_unwrapped_and_cast(self):
+ # F16 storage that should become bf16 plain tensor.
+ f16_data = torch.zeros((4,), dtype=torch.float16)
+ sd = {"bias": _ggml(f16_data, gguf.GGMLQuantizationType.F16, torch.bfloat16)}
+ out = _unwrap_unquantized_to_compute_dtype(sd)
+
+ result = out["bias"]
+ assert not isinstance(result, GGMLTensor)
+ assert result.dtype == torch.bfloat16
+
+ def test_f32_compatible_qtype_is_unwrapped_and_cast(self):
+ # patch_embedding.weight in QuantStack is F32 — same path.
+ f32_data = torch.zeros((4,), dtype=torch.float32)
+ sd = {"weight": _ggml(f32_data, gguf.GGMLQuantizationType.F32, torch.bfloat16)}
+ out = _unwrap_unquantized_to_compute_dtype(sd)
+
+ result = out["weight"]
+ assert not isinstance(result, GGMLTensor)
+ assert result.dtype == torch.bfloat16
+
+ def test_quantized_tensor_stays_wrapped(self):
+ # Q4_K and friends must remain GGMLTensor so on-demand dequant works
+ # via the linear/addmm dispatch path. The byte storage shape is fake
+ # but irrelevant for this test.
+ q4_data = torch.zeros((1,), dtype=torch.uint8)
+ sd = {"linear.weight": _ggml(q4_data, gguf.GGMLQuantizationType.Q4_K, torch.bfloat16)}
+ out = _unwrap_unquantized_to_compute_dtype(sd)
+
+ assert isinstance(out["linear.weight"], GGMLTensor)
+ assert out["linear.weight"]._ggml_quantization_type == gguf.GGMLQuantizationType.Q4_K
+
+ def test_plain_torch_tensor_passes_through(self):
+ plain = torch.zeros((4,), dtype=torch.bfloat16)
+ sd = {"plain": plain}
+ out = _unwrap_unquantized_to_compute_dtype(sd)
+ assert out["plain"] is plain
diff --git a/tests/backend/model_manager/load/test_wan_vae_loader.py b/tests/backend/model_manager/load/test_wan_vae_loader.py
new file mode 100644
index 00000000000..09026df26c4
--- /dev/null
+++ b/tests/backend/model_manager/load/test_wan_vae_loader.py
@@ -0,0 +1,58 @@
+"""Tests for the Wan VAE single-file loader helper.
+
+Covers the bug where ``AutoencoderKLWan`` was always instantiated with the A14B
+defaults (base_dim=96, out_channels=3, no patchify), causing the TI2V-5B VAE
+checkpoint to fail state_dict loading with shape mismatches throughout the
+encoder + decoder. The fix routes z_dim=48 to the TI2V-5B-specific
+constructor kwargs.
+"""
+
+import accelerate
+from diffusers.models.autoencoders.autoencoder_kl_wan import AutoencoderKLWan
+
+from invokeai.backend.model_manager.load.model_loaders.vae import _wan_vae_init_kwargs_for
+
+
+def test_a14b_returns_default_z_dim_only() -> None:
+ # The A14B path should still be the trivial case — only z_dim is overridden,
+ # leaving diffusers' defaults (base_dim=96, out_channels=3, etc.) intact.
+ assert _wan_vae_init_kwargs_for(16) == {"z_dim": 16}
+
+
+def test_ti2v_5b_returns_full_architectural_override() -> None:
+ kw = _wan_vae_init_kwargs_for(48)
+ assert kw["z_dim"] == 48
+ assert kw["base_dim"] == 160
+ assert kw["decoder_base_dim"] == 256
+ assert kw["in_channels"] == 12
+ assert kw["out_channels"] == 12
+ assert kw["patch_size"] == 2
+ assert kw["scale_factor_spatial"] == 16
+ assert kw["is_residual"] is True
+ # latents_mean/std need to be 48-vectors so the model can construct.
+ assert len(kw["latents_mean"]) == 48
+ assert len(kw["latents_std"]) == 48
+
+
+def test_ti2v_5b_kwargs_instantiate_with_expected_shapes() -> None:
+ # End-to-end check: the kwargs let AutoencoderKLWan build cleanly and the
+ # resulting model carries the TI2V-5B-shaped layers (z_dim=48, decoder
+ # outputs 12 channels — this is what failed before the fix).
+ with accelerate.init_empty_weights():
+ model = AutoencoderKLWan(**_wan_vae_init_kwargs_for(48))
+ assert model.z_dim == 48
+ assert model.config.base_dim == 160
+ assert model.config.decoder_base_dim == 256
+ assert model.config.out_channels == 12
+ assert model.config.patch_size == 2
+ # decoder.conv_out emits the patchified 12-channel output (3 RGB x 2x2 patch).
+ assert model.decoder.conv_out.weight.shape[0] == 12
+
+
+def test_a14b_kwargs_instantiate_with_expected_shapes() -> None:
+ with accelerate.init_empty_weights():
+ model = AutoencoderKLWan(**_wan_vae_init_kwargs_for(16))
+ assert model.z_dim == 16
+ assert model.config.base_dim == 96
+ assert model.config.out_channels == 3
+ assert model.config.patch_size is None
diff --git a/tests/backend/model_manager/test_wan_default_settings.py b/tests/backend/model_manager/test_wan_default_settings.py
new file mode 100644
index 00000000000..ff66cf4f067
--- /dev/null
+++ b/tests/backend/model_manager/test_wan_default_settings.py
@@ -0,0 +1,25 @@
+"""Tests for Wan 2.2 default settings."""
+
+from invokeai.backend.model_manager.configs.main import MainModelDefaultSettings
+from invokeai.backend.model_manager.taxonomy import BaseModelType, WanVariantType
+
+
+class TestWanDefaultSettings:
+ def test_a14b_defaults(self) -> None:
+ s = MainModelDefaultSettings.from_base(BaseModelType.Wan, WanVariantType.T2V_A14B)
+ assert s is not None
+ assert s.steps == 40
+ assert s.cfg_scale == 4.0
+ assert s.width == 1024
+ assert s.height == 1024
+
+ def test_ti2v_5b_defaults(self) -> None:
+ s = MainModelDefaultSettings.from_base(BaseModelType.Wan, WanVariantType.TI2V_5B)
+ assert s is not None
+ assert s.steps == 30
+ assert s.cfg_scale == 5.0
+
+ def test_no_variant_falls_back_to_a14b_settings(self) -> None:
+ s = MainModelDefaultSettings.from_base(BaseModelType.Wan)
+ assert s is not None
+ assert s.steps == 40
diff --git a/tests/backend/patches/lora_conversions/test_wan_lora_conversion_utils.py b/tests/backend/patches/lora_conversions/test_wan_lora_conversion_utils.py
new file mode 100644
index 00000000000..f9cac4bd61b
--- /dev/null
+++ b/tests/backend/patches/lora_conversions/test_wan_lora_conversion_utils.py
@@ -0,0 +1,175 @@
+"""Tests for Wan LoRA state-dict conversion to ModelPatchRaw."""
+
+import torch
+
+from invokeai.backend.patches.lora_conversions.wan_lora_constants import WAN_LORA_TRANSFORMER_PREFIX
+from invokeai.backend.patches.lora_conversions.wan_lora_conversion_utils import (
+ _kohya_layer_to_diffusers_path,
+ _native_layer_path_to_diffusers,
+ _strip_peft_prefix,
+ lora_model_from_wan_state_dict,
+)
+
+
+def _ab_pair(in_dim: int, out_dim: int, rank: int = 16) -> dict[str, torch.Tensor]:
+ """PEFT-style lora_A (in→rank) + lora_B (rank→out) pair."""
+ return {
+ "lora_A.weight": torch.zeros((rank, in_dim)),
+ "lora_B.weight": torch.zeros((out_dim, rank)),
+ }
+
+
+def _down_up_pair(in_dim: int, out_dim: int, rank: int = 16) -> dict[str, torch.Tensor]:
+ """Kohya-style lora_down + lora_up pair."""
+ return {
+ "lora_down.weight": torch.zeros((rank, in_dim)),
+ "lora_up.weight": torch.zeros((out_dim, rank)),
+ }
+
+
+class TestKohyaLayerToDiffusersPath:
+ def test_diffusers_self_attention(self):
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_attn1_to_q") == "blocks.0.attn1.to_q"
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_5_attn1_to_out_0") == "blocks.5.attn1.to_out.0"
+
+ def test_diffusers_cross_attention(self):
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_attn2_to_k") == "blocks.0.attn2.to_k"
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_attn2_to_v") == "blocks.0.attn2.to_v"
+
+ def test_native_self_attention_maps_to_attn1(self):
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_self_attn_q") == "blocks.0.attn1.to_q"
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_self_attn_o") == "blocks.0.attn1.to_out.0"
+
+ def test_native_cross_attention_maps_to_attn2(self):
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_2_cross_attn_v") == "blocks.2.attn2.to_v"
+
+ def test_ffn_diffusers(self):
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_ffn_net_0_proj") == "blocks.0.ffn.net.0.proj"
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_ffn_net_2") == "blocks.0.ffn.net.2"
+
+ def test_ffn_native_maps_to_diffusers(self):
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_ffn_0") == "blocks.0.ffn.net.0.proj"
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_ffn_2") == "blocks.0.ffn.net.2"
+
+ def test_unknown_submodule_returns_none(self):
+ assert _kohya_layer_to_diffusers_path("lora_unet_blocks_0_unknown_thing") is None
+
+ def test_non_kohya_returns_none(self):
+ assert _kohya_layer_to_diffusers_path("transformer.blocks.0.attn1.to_q") is None
+
+
+class TestPEFTPathConversion:
+ def test_strip_transformer_prefix(self):
+ assert _strip_peft_prefix("transformer.blocks.0.attn1.to_q") == "blocks.0.attn1.to_q"
+
+ def test_strip_diffusion_model_prefix(self):
+ assert _strip_peft_prefix("diffusion_model.blocks.0.self_attn.q") == "blocks.0.self_attn.q"
+
+ def test_strip_base_model_prefix(self):
+ assert _strip_peft_prefix("base_model.model.transformer.blocks.0.attn1.to_q") == "blocks.0.attn1.to_q"
+
+ def test_no_prefix_unchanged(self):
+ assert _strip_peft_prefix("blocks.0.attn1.to_q") == "blocks.0.attn1.to_q"
+
+ def test_diffusers_path_passes_through(self):
+ assert _native_layer_path_to_diffusers("blocks.0.attn1.to_q") == "blocks.0.attn1.to_q"
+ assert _native_layer_path_to_diffusers("blocks.0.ffn.net.0.proj") == "blocks.0.ffn.net.0.proj"
+
+ def test_native_self_attn_becomes_attn1(self):
+ assert _native_layer_path_to_diffusers("blocks.0.self_attn.q") == "blocks.0.attn1.to_q"
+ assert _native_layer_path_to_diffusers("blocks.0.self_attn.k") == "blocks.0.attn1.to_k"
+ assert _native_layer_path_to_diffusers("blocks.0.self_attn.v") == "blocks.0.attn1.to_v"
+ assert _native_layer_path_to_diffusers("blocks.0.self_attn.o") == "blocks.0.attn1.to_out.0"
+
+ def test_native_cross_attn_becomes_attn2(self):
+ assert _native_layer_path_to_diffusers("blocks.7.cross_attn.q") == "blocks.7.attn2.to_q"
+ assert _native_layer_path_to_diffusers("blocks.7.cross_attn.o") == "blocks.7.attn2.to_out.0"
+
+ def test_native_ffn_becomes_diffusers_ffn(self):
+ assert _native_layer_path_to_diffusers("blocks.0.ffn.0") == "blocks.0.ffn.net.0.proj"
+ assert _native_layer_path_to_diffusers("blocks.0.ffn.2") == "blocks.0.ffn.net.2"
+
+ def test_non_block_path_rejected(self):
+ assert _native_layer_path_to_diffusers("patch_embedding.weight") is None
+
+
+class TestLoRAModelFromStateDict:
+ """End-to-end conversion: state dict -> ModelPatchRaw."""
+
+ def test_diffusers_peft_with_transformer_prefix(self):
+ sd = {f"transformer.blocks.0.attn1.to_q.{k}": v for k, v in _ab_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ expected_key = f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.0.attn1.to_q"
+ assert expected_key in patch.layers
+
+ def test_diffusers_peft_bare(self):
+ sd = {f"blocks.5.attn2.to_k.{k}": v for k, v in _ab_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.5.attn2.to_k" in patch.layers
+
+ def test_native_peft_diffusion_model_prefix(self):
+ sd = {f"diffusion_model.blocks.0.self_attn.q.{k}": v for k, v in _ab_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ # native self_attn.q must be rewritten to attn1.to_q
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.0.attn1.to_q" in patch.layers
+
+ def test_native_peft_cross_attn_to_attn2(self):
+ sd = {f"diffusion_model.blocks.3.cross_attn.o.{k}": v for k, v in _ab_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.3.attn2.to_out.0" in patch.layers
+
+ def test_native_peft_ffn_to_diffusers(self):
+ sd = {f"diffusion_model.blocks.0.ffn.0.{k}": v for k, v in _ab_pair(5120, 13824).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.0.ffn.net.0.proj" in patch.layers
+
+ def test_kohya_diffusers_naming(self):
+ sd = {f"lora_unet_blocks_0_attn1_to_q.{k}": v for k, v in _down_up_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.0.attn1.to_q" in patch.layers
+
+ def test_kohya_native_naming(self):
+ sd = {f"lora_unet_blocks_0_self_attn_q.{k}": v for k, v in _down_up_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.0.attn1.to_q" in patch.layers
+
+ def test_kohya_ffn_native_naming(self):
+ sd = {f"lora_unet_blocks_0_ffn_0.{k}": v for k, v in _down_up_pair(5120, 13824).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.0.ffn.net.0.proj" in patch.layers
+
+ def test_multiple_layers(self):
+ """Cover a realistic mix of attn + ffn keys across multiple blocks."""
+ sd = {}
+ for block in range(3):
+ for k, v in _ab_pair(5120, 5120).items():
+ sd[f"transformer.blocks.{block}.attn1.to_q.{k}"] = v
+ sd[f"transformer.blocks.{block}.attn2.to_v.{k}"] = v
+ for k, v in _ab_pair(5120, 13824).items():
+ sd[f"transformer.blocks.{block}.ffn.net.0.proj.{k}"] = v
+
+ patch = lora_model_from_wan_state_dict(sd)
+ expected_paths = []
+ for block in range(3):
+ expected_paths.append(f"blocks.{block}.attn1.to_q")
+ expected_paths.append(f"blocks.{block}.attn2.to_v")
+ expected_paths.append(f"blocks.{block}.ffn.net.0.proj")
+ for path in expected_paths:
+ assert f"{WAN_LORA_TRANSFORMER_PREFIX}{path}" in patch.layers
+
+ def test_alpha_override_propagates(self):
+ sd = {f"blocks.0.attn1.to_q.{k}": v for k, v in _ab_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd, alpha=8.0)
+ layer = patch.layers[f"{WAN_LORA_TRANSFORMER_PREFIX}blocks.0.attn1.to_q"]
+ # any_lora_layer_from_state_dict picks LoRALayer / LoKR / etc. — the
+ # layer object should at minimum have processed the alpha into its state.
+ assert layer is not None
+
+ def test_unknown_kohya_submodule_is_skipped_silently(self):
+ sd = {f"lora_unet_blocks_0_unknown_thing.{k}": v for k, v in _down_up_pair(5120, 5120).items()}
+ patch = lora_model_from_wan_state_dict(sd)
+ assert len(patch.layers) == 0
+
+ def test_empty_state_dict(self):
+ patch = lora_model_from_wan_state_dict({})
+ assert len(patch.layers) == 0
diff --git a/tests/backend/wan/__init__.py b/tests/backend/wan/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/backend/wan/test_sampling_utils.py b/tests/backend/wan/test_sampling_utils.py
new file mode 100644
index 00000000000..ec52f357a87
--- /dev/null
+++ b/tests/backend/wan/test_sampling_utils.py
@@ -0,0 +1,91 @@
+"""Tests for Wan 2.2 sampling utilities."""
+
+import torch
+
+from invokeai.backend.model_manager.taxonomy import WanVariantType
+from invokeai.backend.wan.sampling_utils import (
+ get_default_latent_channels,
+ get_spatial_scale_factor,
+ make_noise,
+)
+
+
+class TestVariantConstants:
+ def test_a14b_uses_8x_spatial(self) -> None:
+ assert get_spatial_scale_factor(WanVariantType.T2V_A14B) == 8
+
+ def test_ti2v_5b_uses_16x_spatial(self) -> None:
+ assert get_spatial_scale_factor(WanVariantType.TI2V_5B) == 16
+
+ def test_a14b_default_channels(self) -> None:
+ assert get_default_latent_channels(WanVariantType.T2V_A14B) == 16
+
+ def test_ti2v_5b_default_channels(self) -> None:
+ assert get_default_latent_channels(WanVariantType.TI2V_5B) == 48
+
+
+class TestMakeNoise:
+ def test_a14b_shape_at_1024(self) -> None:
+ noise = make_noise(
+ batch_size=1,
+ latent_channels=16,
+ height=1024,
+ width=1024,
+ spatial_scale_factor=8,
+ device=torch.device("cpu"),
+ dtype=torch.bfloat16,
+ seed=42,
+ )
+ assert noise.shape == (1, 16, 1, 128, 128)
+ assert noise.dtype == torch.bfloat16
+
+ def test_ti2v_shape_at_1024(self) -> None:
+ noise = make_noise(
+ batch_size=1,
+ latent_channels=48,
+ height=1024,
+ width=1024,
+ spatial_scale_factor=16,
+ device=torch.device("cpu"),
+ dtype=torch.bfloat16,
+ seed=42,
+ )
+ assert noise.shape == (1, 48, 1, 64, 64)
+
+ def test_seed_is_deterministic(self) -> None:
+ kwargs = {
+ "batch_size": 1,
+ "latent_channels": 16,
+ "height": 256,
+ "width": 256,
+ "spatial_scale_factor": 8,
+ "device": torch.device("cpu"),
+ "dtype": torch.float32,
+ "seed": 123,
+ }
+ a = make_noise(**kwargs)
+ b = make_noise(**kwargs)
+ assert torch.allclose(a, b)
+
+ def test_seed_changes_output(self) -> None:
+ a = make_noise(
+ batch_size=1,
+ latent_channels=16,
+ height=256,
+ width=256,
+ spatial_scale_factor=8,
+ device=torch.device("cpu"),
+ dtype=torch.float32,
+ seed=1,
+ )
+ b = make_noise(
+ batch_size=1,
+ latent_channels=16,
+ height=256,
+ width=256,
+ spatial_scale_factor=8,
+ device=torch.device("cpu"),
+ dtype=torch.float32,
+ seed=2,
+ )
+ assert not torch.allclose(a, b)
diff --git a/tests/backend/wan/test_wan_ref_image_extension.py b/tests/backend/wan/test_wan_ref_image_extension.py
new file mode 100644
index 00000000000..fda879bb0d8
--- /dev/null
+++ b/tests/backend/wan/test_wan_ref_image_extension.py
@@ -0,0 +1,230 @@
+"""Tests for the Wan 2.2 I2V reference-image VAE-latent encoder helper."""
+
+from unittest.mock import MagicMock
+
+import pytest
+import torch
+from PIL import Image
+
+from invokeai.backend.wan.extensions.wan_ref_image_extension import (
+ encode_reference_image_to_condition,
+ encode_reference_image_to_video_condition,
+ preprocess_reference_image,
+)
+
+
+def _make_fake_vae(z_dim: int = 16, spatial_scale: int = 8, temporal_scale: int = 4) -> MagicMock:
+ """Stand-in for ``AutoencoderKLWan`` that returns deterministic latents.
+
+ ``encode(pixel)`` returns a fake distribution whose ``sample()`` yields
+ a tensor sized exactly as the real Wan VAE would: ``[B, z_dim, T_lat, H/8, W/8]``.
+ """
+ vae = MagicMock()
+
+ # ``next(iter(vae.parameters())).dtype`` is queried; pin to float32.
+ param = torch.zeros(1, dtype=torch.float32)
+ vae.parameters = MagicMock(return_value=iter([param]))
+
+ # Config carries per-channel normalisation stats.
+ vae.config = MagicMock()
+ vae.config.latents_mean = [0.0] * z_dim
+ vae.config.latents_std = [1.0] * z_dim
+
+ def fake_encode(pixel: torch.Tensor, return_dict: bool = False):
+ b, _, t, h, w = pixel.shape
+ t_lat = (t - 1) // temporal_scale + 1
+ h_lat = h // spatial_scale
+ w_lat = w // spatial_scale
+ latents = torch.zeros(b, z_dim, t_lat, h_lat, w_lat, dtype=pixel.dtype)
+
+ dist = MagicMock()
+ dist.sample = MagicMock(return_value=latents)
+ # The pipeline does ``vae.encode(...)[0]`` for non-dict returns.
+ return (dist,) if return_dict is False else MagicMock(latent_dist=dist)
+
+ vae.encode = fake_encode
+ return vae
+
+
+class TestPreprocess:
+ def test_resize_to_target_dims(self):
+ img = Image.new("RGB", (200, 300), (128, 128, 128))
+ out = preprocess_reference_image(img, width=64, height=64)
+ # Shape: [batch=1, channels=3, time=1, H, W]
+ assert out.shape == (1, 3, 1, 64, 64)
+
+ def test_normalised_to_minus_one_to_one(self):
+ # Pure-grey image preprocessed should be exactly 0 (since 128/255*2 - 1 ≈ 0.004).
+ img = Image.new("RGB", (64, 64), (255, 255, 255))
+ out = preprocess_reference_image(img, width=64, height=64)
+ # White → 1.0
+ assert torch.allclose(out, torch.ones_like(out), atol=1e-4)
+
+ black = Image.new("RGB", (64, 64), (0, 0, 0))
+ out_b = preprocess_reference_image(black, width=64, height=64)
+ # Black → -1.0
+ assert torch.allclose(out_b, -torch.ones_like(out_b), atol=1e-4)
+
+ def test_rejects_non_multiple_of_8(self):
+ img = Image.new("RGB", (100, 100))
+ import pytest
+
+ with pytest.raises(ValueError, match="multiples of 8"):
+ preprocess_reference_image(img, width=65, height=64)
+
+
+class TestEncodeReferenceImageToCondition:
+ """The condition tensor must be 20-channel (4 mask + 16 image latents)
+ and shaped for the denoise step's later concat with 16-ch noise latents."""
+
+ def test_shape_at_64x64(self):
+ img = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae()
+ cond = encode_reference_image_to_condition(
+ image=img, vae=vae, width=64, height=64, device=torch.device("cpu"), dtype=torch.float32
+ )
+ # [1, 20, 1, 8, 8] — 4-ch mask + 16-ch latents at H/8, W/8.
+ assert cond.shape == (1, 20, 1, 8, 8)
+
+ def test_shape_at_1024x1024(self):
+ img = Image.new("RGB", (1024, 1024))
+ vae = _make_fake_vae()
+ cond = encode_reference_image_to_condition(
+ image=img, vae=vae, width=1024, height=1024, device=torch.device("cpu"), dtype=torch.float32
+ )
+ # 1024/8 = 128 latent spatial dim.
+ assert cond.shape == (1, 20, 1, 128, 128)
+
+ def test_first_four_channels_are_all_ones_mask(self):
+ img = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae()
+ cond = encode_reference_image_to_condition(
+ image=img, vae=vae, width=64, height=64, device=torch.device("cpu"), dtype=torch.float32
+ )
+ mask = cond[:, :4]
+ # First-frame mask is all-ones at num_frames=1 (every position is the first frame).
+ assert torch.equal(mask, torch.ones_like(mask))
+
+ def test_returns_dtype(self):
+ img = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae()
+ cond = encode_reference_image_to_condition(
+ image=img, vae=vae, width=64, height=64, device=torch.device("cpu"), dtype=torch.bfloat16
+ )
+ assert cond.dtype == torch.bfloat16
+
+
+class TestEncodeReferenceImageToVideoCondition:
+ """Multi-frame A14B I2V condition, including first-last-frame (FLF2V) mode.
+
+ The fake VAE returns zero latents, so we can't assert on the *content* of the
+ last frame — but the 4-channel mask is built independently of the VAE output,
+ so we can verify exactly which latent frames get anchored.
+ """
+
+ def test_video_condition_shape(self):
+ img = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae()
+ # num_frames=5 → t_lat = (5-1)//4 + 1 = 2.
+ cond = encode_reference_image_to_video_condition(
+ image=img, vae=vae, width=64, height=64, num_frames=5, device=torch.device("cpu"), dtype=torch.float32
+ )
+ assert cond.shape == (1, 20, 2, 8, 8)
+
+ def test_single_reference_anchors_only_first_latent_frame(self):
+ img = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae()
+ cond = encode_reference_image_to_video_condition(
+ image=img, vae=vae, width=64, height=64, num_frames=5, device=torch.device("cpu"), dtype=torch.float32
+ )
+ mask = cond[:, :4] # [1, 4, t_lat=2, 8, 8]
+ # First latent frame fully anchored; the rest entirely free.
+ assert torch.equal(mask[:, :, 0], torch.ones_like(mask[:, :, 0]))
+ assert torch.equal(mask[:, :, 1:], torch.zeros_like(mask[:, :, 1:]))
+
+ def test_flf2v_anchors_first_and_last_latent_frame(self):
+ img = Image.new("RGB", (64, 64))
+ end = Image.new("RGB", (64, 64), (255, 255, 255))
+ vae = _make_fake_vae()
+ cond = encode_reference_image_to_video_condition(
+ image=img,
+ vae=vae,
+ width=64,
+ height=64,
+ num_frames=5,
+ device=torch.device("cpu"),
+ dtype=torch.float32,
+ last_image=end,
+ )
+ mask = cond[:, :4] # [1, 4, 2, 8, 8]
+ # First latent frame still fully anchored.
+ assert torch.equal(mask[:, :, 0], torch.ones_like(mask[:, :, 0]))
+ # The end image anchors the final pixel-frame, which lands in the last
+ # mask channel of the last latent frame (and only there).
+ assert torch.equal(mask[:, 3, 1], torch.ones_like(mask[:, 3, 1]))
+ assert torch.equal(mask[:, :3, 1], torch.zeros_like(mask[:, :3, 1]))
+
+ def test_flf2v_requires_multiple_frames(self):
+ img = Image.new("RGB", (64, 64))
+ end = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae()
+ with pytest.raises(ValueError, match="FLF2V"):
+ encode_reference_image_to_video_condition(
+ image=img,
+ vae=vae,
+ width=64,
+ height=64,
+ num_frames=1,
+ device=torch.device("cpu"),
+ dtype=torch.float32,
+ last_image=end,
+ )
+
+
+class TestEncodeReferenceImageToTI2VCondition:
+ """TI2V-5B's condition tensor is a single 48-channel latent frame (no mask
+ channels) at the Wan2.2-VAE's 16x spatial scale. The denoise loop blends
+ this with the noise via a first_frame_mask at each step.
+ """
+
+ def test_shape_at_64x64(self):
+ from invokeai.backend.wan.extensions.wan_ref_image_extension import (
+ encode_reference_image_to_ti2v_condition,
+ )
+
+ img = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae(z_dim=48, spatial_scale=16, temporal_scale=4)
+ cond = encode_reference_image_to_ti2v_condition(
+ image=img, vae=vae, width=64, height=64, device=torch.device("cpu"), dtype=torch.float32
+ )
+ # [1, 48, 1, 4, 4] — single latent frame at H/16, W/16.
+ assert cond.shape == (1, 48, 1, 4, 4)
+
+ def test_shape_at_832x480(self):
+ # Common Wan video resolution: latent 30x52 single frame.
+ from invokeai.backend.wan.extensions.wan_ref_image_extension import (
+ encode_reference_image_to_ti2v_condition,
+ )
+
+ img = Image.new("RGB", (832, 480))
+ vae = _make_fake_vae(z_dim=48, spatial_scale=16, temporal_scale=4)
+ cond = encode_reference_image_to_ti2v_condition(
+ image=img, vae=vae, width=832, height=480, device=torch.device("cpu"), dtype=torch.float32
+ )
+ assert cond.shape == (1, 48, 1, 30, 52)
+
+ def test_no_mask_channels(self):
+ # Distinguishing feature vs A14B: no leading 4-ch mask. All 48 channels
+ # are latent content. With latents_mean=0 and latents_std=1, encoded zeros
+ # stay zero (the function returns latents straight through).
+ from invokeai.backend.wan.extensions.wan_ref_image_extension import (
+ encode_reference_image_to_ti2v_condition,
+ )
+
+ img = Image.new("RGB", (64, 64))
+ vae = _make_fake_vae(z_dim=48, spatial_scale=16, temporal_scale=4)
+ cond = encode_reference_image_to_ti2v_condition(
+ image=img, vae=vae, width=64, height=64, device=torch.device("cpu"), dtype=torch.float32
+ )
+ # Fake VAE.encode returns zero latents; normalized zeros stay zero.
+ assert torch.equal(cond, torch.zeros_like(cond))
diff --git a/tests/conftest.py b/tests/conftest.py
index 80df0aa93e9..d930a4de78e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -12,11 +12,13 @@
from invokeai.app.services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
from invokeai.app.services.board_records.board_records_sqlite import SqliteBoardRecordStorage
+from invokeai.app.services.board_video_records.board_video_records_sqlite import SqliteBoardVideoRecordStorage
from invokeai.app.services.boards.boards_default import BoardService
from invokeai.app.services.bulk_download.bulk_download_default import BulkDownloadService
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import ClientStatePersistenceSqlite
from invokeai.app.services.config.config_default import InvokeAIAppConfig
from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
+from invokeai.app.services.gallery.gallery_default import SqliteGalleryService
from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
from invokeai.app.services.images.images_default import ImageService
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
@@ -24,6 +26,7 @@
from invokeai.app.services.invocation_stats.invocation_stats_default import InvocationStatsService
from invokeai.app.services.invoker import Invoker
from invokeai.app.services.users.users_default import UserService
+from invokeai.app.services.video_records.video_records_sqlite import SqliteVideoRecordStorage
from invokeai.app.services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
from invokeai.backend.util.logging import InvokeAILogger
from tests.backend.model_manager.model_manager_fixtures import * # noqa: F403
@@ -70,6 +73,13 @@ def mock_services() -> InvocationServices:
model_relationships=None, # type: ignore
client_state_persistence=ClientStatePersistenceSqlite(db=db),
users=UserService(db),
+ videos=None, # type: ignore
+ video_files=None, # type: ignore
+ video_records=SqliteVideoRecordStorage(db=db),
+ board_video_records=SqliteBoardVideoRecordStorage(db=db),
+ # Real SQLite-backed gallery service: the virtual-boards router reads dates and
+ # per-date item names through it, and MagicMock cannot exercise the filter SQL.
+ gallery=SqliteGalleryService(db=db),
)
diff --git a/tests/test_session_queue.py b/tests/test_session_queue.py
index 40116422ff4..7a363faea4b 100644
--- a/tests/test_session_queue.py
+++ b/tests/test_session_queue.py
@@ -3,6 +3,8 @@
import pytest
from pydantic import TypeAdapter, ValidationError
+from invokeai.app.invocations.fields import VideoField
+from invokeai.app.invocations.video_frame_extract import VideoFrameExtractInvocation
from invokeai.app.services.session_queue.session_queue_common import (
Batch,
BatchDataCollection,
@@ -88,6 +90,32 @@ def test_create_sessions_from_batch_with_runs(batch_data_collection, batch_graph
assert json.loads(t[7][1])["graph"]["nodes"]["4"]["prompt"] == "Nissan"
+def test_create_sessions_from_batch_with_video_fields():
+ """VideoField batch data must validate and expand into separate sessions, just like
+ ImageField — video workflows use the same generic batching capability (JPPhoto PR #9163
+ July-10 follow-up: VideoField values used to fail Batch validation before enqueueing)."""
+ g = Graph()
+ g.add_node(VideoFrameExtractInvocation(id="1", video=VideoField(video_name="placeholder.mp4")))
+ b = Batch(
+ graph=g,
+ data=[
+ [
+ BatchDatum(
+ node_path="1",
+ field_name="video",
+ items=[VideoField(video_name="first.mp4"), VideoField(video_name="second.mp4")],
+ )
+ ]
+ ],
+ )
+
+ assert calc_session_count(batch=b) == 2
+ t = list(create_session_nfv_tuples(batch=b, maximum=1000))
+ assert len(t) == 2
+ assert json.loads(t[0][1])["graph"]["nodes"]["1"]["video"]["video_name"] == "first.mp4"
+ assert json.loads(t[1][1])["graph"]["nodes"]["1"]["video"]["video_name"] == "second.mp4"
+
+
def test_create_sessions_from_batch_without_runs(batch_data_collection, batch_graph):
b = Batch(graph=batch_graph, data=batch_data_collection)
t = list(create_session_nfv_tuples(batch=b, maximum=1000))
diff --git a/uv.lock b/uv.lock
index 6d1b0a462f8..f74d2318b0f 100644
--- a/uv.lock
+++ b/uv.lock
@@ -990,6 +990,38 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
]
+[[package]]
+name = "imageio"
+version = "2.37.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "numpy", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
+ { name = "pillow", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673, upload-time = "2026-03-09T11:31:12.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl", hash = "sha256:46f5bb8522cd421c0f5ae104d8268f569d856b29eb1a13b92829d1970f32c9f0", size = 317646, upload-time = "2026-03-09T11:31:10.771Z" },
+]
+
+[package.optional-dependencies]
+ffmpeg = [
+ { name = "imageio-ffmpeg", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
+ { name = "psutil", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
+]
+
+[[package]]
+name = "imageio-ffmpeg"
+version = "0.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/44/bd/c3343c721f2a1b0c9fc71c1aebf1966a3b7f08c2eea8ed5437a2865611d6/imageio_ffmpeg-0.6.0.tar.gz", hash = "sha256:e2556bed8e005564a9f925bb7afa4002d82770d6b08825078b7697ab88ba1755", size = 25210, upload-time = "2025-01-16T21:34:32.747Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/da/58/87ef68ac83f4c7690961bce288fd8e382bc5f1513860fc7f90a9c1c1c6bf/imageio_ffmpeg-0.6.0-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.whl", hash = "sha256:9d2baaf867088508d4a3458e61eeb30e945c4ad8016025545f66c4b5aaef0a61", size = 24932969, upload-time = "2025-01-16T21:34:20.464Z" },
+ { url = "https://files.pythonhosted.org/packages/40/5c/f3d8a657d362cc93b81aab8feda487317da5b5d31c0e1fdfd5e986e55d17/imageio_ffmpeg-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b1ae3173414b5fc5f538a726c4e48ea97edc0d2cdc11f103afee655c463fa742", size = 21113891, upload-time = "2025-01-16T21:34:00.277Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/2d/43c8522a2038e9d0e7dbdf3a61195ecc31ca576fb1527a528c877e87d973/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c7e46fcec401dd990405049d2e2f475e2b397779df2519b544b8aab515195282", size = 29498237, upload-time = "2025-01-16T21:34:13.726Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/13/59da54728351883c3c1d9fca1710ab8eee82c7beba585df8f25ca925f08f/imageio_ffmpeg-0.6.0-py3-none-win32.whl", hash = "sha256:196faa79366b4a82f95c0f4053191d2013f4714a715780f0ad2a68ff37483cc2", size = 19652251, upload-time = "2025-01-16T21:34:06.812Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/c6/fa760e12a2483469e2bf5058c5faff664acf66cadb4df2ad6205b016a73d/imageio_ffmpeg-0.6.0-py3-none-win_amd64.whl", hash = "sha256:02fa47c83703c37df6bfe4896aab339013f62bf02c5ebf2dce6da56af04ffc0a", size = 31246824, upload-time = "2025-01-16T21:34:28.6Z" },
+]
+
[[package]]
name = "importlib-metadata"
version = "9.0.0"
@@ -1030,6 +1062,7 @@ dependencies = [
{ name = "fastapi-events", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "gguf", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "huggingface-hub", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
+ { name = "imageio", extra = ["ffmpeg"], marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "mediapipe", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "numpy", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "onnx", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
@@ -1039,6 +1072,7 @@ dependencies = [
{ name = "picklescan", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "pillow", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "prompt-toolkit", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
+ { name = "psutil", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "pydantic", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "pydantic-settings", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
{ name = "pypatchmatch", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
@@ -1137,6 +1171,7 @@ requires-dist = [
{ name = "httpx", marker = "extra == 'test'" },
{ name = "huggingface-hub" },
{ name = "humanize", marker = "extra == 'test'", specifier = "==4.12.1" },
+ { name = "imageio", extras = ["ffmpeg"] },
{ name = "jurigged", marker = "extra == 'dev'" },
{ name = "mediapipe", specifier = "==0.10.14" },
{ name = "mypy", marker = "extra == 'test'" },
@@ -1155,6 +1190,7 @@ requires-dist = [
{ name = "polyfactory", marker = "extra == 'test'", specifier = "==2.19.0" },
{ name = "pre-commit", marker = "extra == 'test'" },
{ name = "prompt-toolkit" },
+ { name = "psutil" },
{ name = "pudb", marker = "extra == 'dev'" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
@@ -3710,10 +3746,10 @@ name = "torch"
version = "2.7.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
- "python_full_version >= '3.12' and sys_platform == 'darwin'",
- "python_full_version < '3.12' and sys_platform == 'darwin'",
"(python_full_version >= '3.12' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')",
"(python_full_version < '3.12' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform == 'win32')",
+ "python_full_version >= '3.12' and sys_platform == 'darwin'",
+ "python_full_version < '3.12' and sys_platform == 'darwin'",
]
dependencies = [
{ name = "filelock", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-8-invokeai-cpu' and extra != 'extra-8-invokeai-cuda' and extra != 'extra-8-invokeai-rocm') or (sys_platform == 'darwin' and extra == 'extra-8-invokeai-rocm') or (sys_platform == 'darwin' and extra != 'extra-8-invokeai-cpu' and extra != 'extra-8-invokeai-cuda') or (sys_platform == 'win32' and extra != 'extra-8-invokeai-cpu' and extra != 'extra-8-invokeai-cuda' and extra != 'extra-8-invokeai-rocm') or (extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },
@@ -3862,10 +3898,10 @@ name = "torchvision"
version = "0.22.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
- "python_full_version >= '3.12' and sys_platform == 'darwin'",
- "python_full_version < '3.12' and sys_platform == 'darwin'",
"(python_full_version >= '3.12' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')",
"(python_full_version < '3.12' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform == 'win32')",
+ "python_full_version >= '3.12' and sys_platform == 'darwin'",
+ "python_full_version < '3.12' and sys_platform == 'darwin'",
]
dependencies = [
{ name = "numpy", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-8-invokeai-cpu' and extra != 'extra-8-invokeai-cuda' and extra != 'extra-8-invokeai-rocm') or (sys_platform == 'darwin' and extra == 'extra-8-invokeai-rocm') or (sys_platform == 'darwin' and extra != 'extra-8-invokeai-cpu' and extra != 'extra-8-invokeai-cuda') or (sys_platform == 'win32' and extra != 'extra-8-invokeai-cpu' and extra != 'extra-8-invokeai-cuda' and extra != 'extra-8-invokeai-rocm') or (extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" },