chore: import upstream snapshot with attribution
build container image / cpu (push) Waiting to run
build container image / cuda (push) Waiting to run
build container image / rocm (push) Waiting to run
docs / deploy (push) Blocked by required conditions
docs / changes (push) Waiting to run
docs / check-and-build (push) Blocked by required conditions
frontend tests / frontend-tests (push) Waiting to run
openapi checks / openapi-checks (push) Waiting to run
python tests / py3.12: macos-default (push) Waiting to run
python tests / py3.11: windows-cpu (push) Waiting to run
python tests / py3.12: windows-cpu (push) Waiting to run
python tests / py3.11: linux-cpu (push) Waiting to run
python tests / py3.12: linux-cpu (push) Waiting to run
typegen checks / typegen-checks (push) Waiting to run
uv lock checks / uv-lock-checks (push) Waiting to run
frontend checks / frontend-checks (push) Waiting to run
lfs checks / lfs-check (push) Waiting to run
python checks / python-checks (push) Waiting to run
python tests / py3.11: macos-default (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:22:06 +08:00
commit cddb07a176
3370 changed files with 685519 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
import argparse
import numpy as np
from PIL import Image
def read_image_int16(image_path):
image = Image.open(image_path)
return np.array(image).astype(np.int16)
def calc_images_mean_L1(image1_path, image2_path):
image1 = read_image_int16(image1_path)
image2 = read_image_int16(image2_path)
assert image1.shape == image2.shape
mean_L1 = np.abs(image1 - image2).mean()
return mean_L1
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("image1_path")
parser.add_argument("image2_path")
args = parser.parse_args()
return args
if __name__ == "__main__":
args = parse_args()
mean_L1 = calc_images_mean_L1(args.image1_path, args.image2_path)
print(mean_L1)
Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

+1
View File
@@ -0,0 +1 @@
"a photograph of an astronaut riding a horse" -s50 -S42
@@ -0,0 +1,19 @@
# generate an image
PROMPT_FILE=".dev_scripts/sample_command.txt"
OUT_DIR="outputs/img-samples/test_regression_txt2img_v1_4"
SAMPLES_DIR=${OUT_DIR}
python scripts/dream.py \
--from_file ${PROMPT_FILE} \
--outdir ${OUT_DIR} \
--sampler plms
# original output by CompVis/stable-diffusion
IMAGE1=".dev_scripts/images/v1_4_astronaut_rides_horse_plms_step50_seed42.png"
# new output
IMAGE2=`ls -A ${SAMPLES_DIR}/*.png | sort | tail -n 1`
echo ""
echo "comparing the following two images"
echo "IMAGE1: ${IMAGE1}"
echo "IMAGE2: ${IMAGE2}"
python .dev_scripts/diff_images.py ${IMAGE1} ${IMAGE2}
@@ -0,0 +1,23 @@
# generate an image
PROMPT="a photograph of an astronaut riding a horse"
OUT_DIR="outputs/txt2img-samples/test_regression_txt2img_v1_4"
SAMPLES_DIR="outputs/txt2img-samples/test_regression_txt2img_v1_4/samples"
python scripts/orig_scripts/txt2img.py \
--prompt "${PROMPT}" \
--outdir ${OUT_DIR} \
--plms \
--ddim_steps 50 \
--n_samples 1 \
--n_iter 1 \
--seed 42
# original output by CompVis/stable-diffusion
IMAGE1=".dev_scripts/images/v1_4_astronaut_rides_horse_plms_step50_seed42.png"
# new output
IMAGE2=`ls -A ${SAMPLES_DIR}/*.png | sort | tail -n 1`
echo ""
echo "comparing the following two images"
echo "IMAGE1: ${IMAGE1}"
echo "IMAGE2: ${IMAGE2}"
python .dev_scripts/diff_images.py ${IMAGE1} ${IMAGE2}
+11
View File
@@ -0,0 +1,11 @@
*
!invokeai
!pyproject.toml
!uv.lock
!docker/docker-entrypoint.sh
!LICENSE
**/dist
**/node_modules
**/__pycache__
**/*.egg-info
+12
View File
@@ -0,0 +1,12 @@
# All files
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
# Python
[*.py]
indent_size = 4
+5
View File
@@ -0,0 +1,5 @@
b3dccfaeb636599c02effc377cdd8a87d658256c
218b6d0546b990fc449c876fb99f44b50c4daa35
182580ff6970caed400be178c5b888514b75d7f2
8e9d5c1187b0d36da80571ce4c8ba9b3a37b6c46
99aac5870e1092b182e6c5f21abcaab6936a4ad1
+7
View File
@@ -0,0 +1,7 @@
# Auto normalizes line endings on commit so devs don't need to change local settings.
# Only affects text files and ignores other file types.
# For more info see: https://www.aleksandrhovhannisyan.com/blog/crlf-vs-lf-normalizing-line-endings-in-git/
* text=auto
docker/** text eol=lf
tests/test_model_probe/stripped_models/** filter=lfs diff=lfs merge=lfs -text
tests/model_identification/stripped_models/** filter=lfs diff=lfs merge=lfs -text
+24
View File
@@ -0,0 +1,24 @@
# Agent Instructions
## Package Management
This project uses **pnpm** exclusively for package management in the frontend (`invokeai/frontend/web/`).
- ✅ Use `pnpm` commands (e.g., `pnpm install`, `pnpm run`)
- ❌ Never use `npm` or `yarn` commands
- ❌ Never suggest creating or using `package-lock.json` or `yarn.lock`
- ✅ The lock file is `pnpm-lock.yaml`
Use the following pnpm commands for typical operations:
- pnpm -C invokeai/frontend/web install
- pnpm -C invokeai/frontend/web build
- pnpm -C invokeai/frontend/web lint:tsc
- pnpm -C invokeai/frontend/web lint:dpdm
- pnpm -C invokeai/frontend/web lint:eslint
- pnpm -C invokeai/frontend/web lint:prettier
## Project Structure
- Backend: Python in `invokeai/`
- Frontend: TypeScript/React in `invokeai/frontend/web/` (uses pnpm)
+30
View File
@@ -0,0 +1,30 @@
# continuous integration
/.github/workflows/ @lstein @blessedcoolant
# documentation - anyone with write privileges can review
/docs/
# nodes
/invokeai/app/ @blessedcoolant @lstein @dunkeroni @JPPhoto @pfannkuchensack
# installation and configuration
/pyproject.toml @lstein @blessedcoolant
/docker/ @lstein @blessedcoolant
/scripts/ @lstein @blessedcoolant
/installer/ @lstein @blessedcoolant
/invokeai/assets @lstein @blessedcoolant
/invokeai/configs @lstein @blessedcoolant
/invokeai/version @lstein @blessedcoolant
# web ui
/invokeai/frontend @blessedcoolant @lstein @dunkeroni @jpphoto @pfannkuchensack
# generation, model management, postprocessing
/invokeai/backend @lstein @blessedcoolant @dunkeroni @JPPhoto @Pfannkuchensack
# front ends
/invokeai/frontend/CLI @lstein
/invokeai/frontend/install @lstein
/invokeai/frontend/merge @lstein @blessedcoolant
/invokeai/frontend/training @lstein @blessedcoolant
/invokeai/frontend/web @blessedcoolant @lstein @dunkeroni @Pfannkuchensack @jpphoto
+4
View File
@@ -0,0 +1,4 @@
# These are supported funding model platforms
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository
github: invoke-ai
+160
View File
@@ -0,0 +1,160 @@
name: 🐞 Bug Report
description: File a bug report
title: '[bug]: '
labels: ['bug']
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this Bug Report!
- type: checkboxes
attributes:
label: Is there an existing issue for this problem?
description: |
Please [search](https://github.com/invoke-ai/InvokeAI/issues) first to see if an issue already exists for the problem.
options:
- label: I have searched the existing issues
required: true
- type: dropdown
id: install_method
attributes:
label: Install method
description: How did you install Invoke?
multiple: false
options:
- "Invoke's Launcher"
- 'Stability Matrix'
- 'Pinokio'
- 'Manual'
validations:
required: true
- type: markdown
attributes:
value: __Describe your environment__
- type: dropdown
id: os_dropdown
attributes:
label: Operating system
description: Your computer's operating system.
multiple: false
options:
- 'Linux'
- 'Windows'
- 'macOS'
- 'other'
validations:
required: true
- type: dropdown
id: gpu_dropdown
attributes:
label: GPU vendor
description: Your GPU's vendor.
multiple: false
options:
- 'Nvidia (CUDA)'
- 'AMD (ROCm)'
- 'Apple Silicon (MPS)'
- 'None (CPU)'
validations:
required: true
- type: input
id: gpu_model
attributes:
label: GPU model
description: Your GPU's model. If on Apple Silicon, this is your Mac's chip. Leave blank if on CPU.
placeholder: ex. RTX 2080 Ti, Mac M1 Pro
validations:
required: false
- type: input
id: vram
attributes:
label: GPU VRAM
description: Your GPU's VRAM. If on Apple Silicon, this is your Mac's unified memory. Leave blank if on CPU.
placeholder: 8GB
validations:
required: false
- type: input
id: version-number
attributes:
label: Version number
description: |
The version of Invoke you have installed. If it is not the [latest version](https://github.com/invoke-ai/InvokeAI/releases/latest), please update and try again to confirm the issue still exists. If you are testing main, please include the commit hash instead.
placeholder: ex. v6.0.2
validations:
required: true
- type: input
id: browser-version
attributes:
label: Browser
description: Your web browser and version, if you do not use the Launcher's provided GUI.
placeholder: ex. Firefox 123.0b3
validations:
required: false
- type: textarea
id: python-deps
attributes:
label: System Information
description: |
Click the gear icon at the bottom left corner, then click "About". Click the copy button and then paste here.
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened
description: |
Describe what happened. Include any relevant error messages, stack traces and screenshots here.
placeholder: I clicked button X and then Y happened.
validations:
required: true
- type: textarea
id: what-you-expected
attributes:
label: What you expected to happen
description: Describe what you expected to happen.
placeholder: I expected Z to happen.
validations:
required: true
- type: textarea
id: how-to-repro
attributes:
label: How to reproduce the problem
description: List steps to reproduce the problem.
placeholder: Start the app, generate an image with these settings, then click button X.
validations:
required: false
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Any other context that might help us to understand the problem.
placeholder: Only happens when there is full moon and Friday the 13th on Christmas Eve 🎅🏻
validations:
required: false
- type: input
id: discord-username
attributes:
label: Discord username
description: If you are on the Invoke discord and would prefer to be contacted there, please provide your username.
placeholder: supercoolusername123
validations:
required: false
@@ -0,0 +1,53 @@
name: Feature Request
description: Contribute a idea or request a new feature
title: '[enhancement]: '
labels: ['enhancement']
# assignees:
# - lstein
# - tildebyte
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request!
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: |
Please make use of the [search function](https://github.com/invoke-ai/InvokeAI/labels/enhancement)
to see if a similar issue already exists for the feature you want to request
options:
- label: I have searched the existing issues
required: true
- type: input
id: contact
attributes:
label: Contact Details
description: __OPTIONAL__ How could we get in touch with you if we need more info (besides this issue)?
placeholder: ex. email@example.com, discordname, twitter, ...
validations:
required: false
- type: textarea
id: whatisexpected
attributes:
label: What should this feature add?
description: Explain the functionality this feature should add. Feature requests should be for single features. Please create multiple requests if you want to request multiple features.
placeholder: |
I'd like a button that creates an image of banana sushi every time I press it. Each image should be different. There should be a toggle next to the button that enables strawberry mode, in which the images are of strawberry sushi instead.
validations:
required: true
- type: textarea
attributes:
label: Alternatives
description: Describe alternatives you've considered
placeholder: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
attributes:
label: Additional Content
description: Add any other context or screenshots about the feature request here.
placeholder: This is a mockup of the design how I imagine it <screenshot>
+14
View File
@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: Project-Documentation
url: https://invoke.ai/
about: Should be your first place to go when looking for manuals/FAQs regarding our InvokeAI Toolkit
- name: Discord
url: https://discord.gg/ZmtBAhwWhy
about: Our Discord Community could maybe help you out via live-chat
- name: GitHub Community Support
url: https://github.com/orgs/community/discussions
about: Please ask and answer questions regarding the GitHub Platform here.
- name: GitHub Security Bug Bounty
url: https://bounty.github.com/
about: Please report security vulnerabilities of the GitHub Platform here.
@@ -0,0 +1,33 @@
name: install frontend dependencies
description: Installs frontend dependencies with pnpm, with caching
runs:
using: 'composite'
steps:
- name: setup node 22
uses: actions/setup-node@v6
with:
node-version: '22'
- name: setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10
run_install: false
- name: get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: setup cache
uses: actions/cache@v5
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: install frontend dependencies
run: pnpm install --prefer-frozen-lockfile
shell: bash
working-directory: invokeai/frontend/web
+59
View File
@@ -0,0 +1,59 @@
root:
- changed-files:
- any-glob-to-any-file: '*'
python-deps:
- changed-files:
- any-glob-to-any-file: 'pyproject.toml'
python:
- changed-files:
- all-globs-to-any-file:
- 'invokeai/**'
- '!invokeai/frontend/web/**'
python-tests:
- changed-files:
- any-glob-to-any-file: 'tests/**'
ci-cd:
- changed-files:
- any-glob-to-any-file: .github/**
docker:
- changed-files:
- any-glob-to-any-file: docker/**
installer:
- changed-files:
- any-glob-to-any-file: installer/**
docs:
- changed-files:
- any-glob-to-any-file: docs/**
invocations:
- changed-files:
- any-glob-to-any-file: 'invokeai/app/invocations/**'
backend:
- changed-files:
- any-glob-to-any-file: 'invokeai/backend/**'
api:
- changed-files:
- any-glob-to-any-file: 'invokeai/app/api/**'
services:
- changed-files:
- any-glob-to-any-file: 'invokeai/app/services/**'
frontend-deps:
- changed-files:
- any-glob-to-any-file:
- '**/*/package.json'
- '**/*/pnpm-lock.yaml'
frontend:
- changed-files:
- any-glob-to-any-file: 'invokeai/frontend/web/**'
+23
View File
@@ -0,0 +1,23 @@
## Summary
<!--A description of the changes in this PR. Include the kind of change (fix, feature, docs, etc), the "why" and the "how". Screenshots or videos are useful for frontend changes.-->
## Related Issues / Discussions
<!--WHEN APPLICABLE: List any related issues or discussions on github or discord. If this PR closes an issue, please use the "Closes #1234" format, so that the issue will be automatically closed when the PR merges.-->
## QA Instructions
<!--WHEN APPLICABLE: Describe how you have tested the changes in this PR. Provide enough detail that a reviewer can reproduce your tests.-->
## Merge Plan
<!--WHEN APPLICABLE: Large PRs, or PRs that touch sensitive things like DB schemas, may need some care when merging. For example, a careful rebase by the change author, timing to not interfere with a pending release, or a message to contributors on discord after merging.-->
## Checklist
- [ ] _The PR has a short but descriptive title, suitable for a changelog_
- [ ] _Tests added / updated (if applicable)_
- [ ] _❗Changes to a redux slice have a corresponding migration_
- [ ] _Documentation added / updated (if applicable)_
- [ ] _Updated `What's New` copy (if doing a release after this PR)_
+19
View File
@@ -0,0 +1,19 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 28
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Please
update the ticket if this is still a problem on the latest release.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Due to inactivity, this issue has been automatically closed. If this is
still a problem on the latest release, please recreate the issue.
+118
View File
@@ -0,0 +1,118 @@
name: build container image
on:
push:
branches:
- 'main'
paths:
- 'pyproject.toml'
- '.dockerignore'
- 'invokeai/**'
- 'docker/Dockerfile'
- 'docker/docker-entrypoint.sh'
- 'workflows/build-container.yml'
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
push-to-registry:
description: Push the built image to the container registry
required: false
type: boolean
default: false
permissions:
contents: write
packages: write
jobs:
docker:
if: github.event.pull_request.draft == false
strategy:
fail-fast: false
matrix:
gpu-driver:
- cuda
- cpu
- rocm
runs-on: ubuntu-latest
name: ${{ matrix.gpu-driver }}
env:
# torch/arm64 does not support GPU currently, so arm64 builds
# would not be GPU-accelerated.
# re-enable arm64 if there is sufficient demand.
# PLATFORMS: 'linux/amd64,linux/arm64'
PLATFORMS: 'linux/amd64'
steps:
- name: Free up more disk space on the runner
# https://github.com/actions/runner-images/issues/2840#issuecomment-1284059930
# the /mnt dir has 70GBs of free space
# /dev/sda1 74G 28K 70G 1% /mnt
# According to some online posts the /mnt is not always there, so checking before setting docker to use it
run: |
echo "----- Free space before cleanup"
df -h
sudo rm -rf /usr/share/dotnet
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
if [ -f /mnt/swapfile ]; then
sudo swapoff /mnt/swapfile
sudo rm -rf /mnt/swapfile
fi
if [ -d /mnt ]; then
sudo chmod -R 777 /mnt
echo '{"data-root": "/mnt/docker-root"}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
fi
echo "----- Free space after cleanup"
df -h
- name: Checkout
uses: actions/checkout@v6
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
images: |
ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=pep440,pattern={{version}}
type=pep440,pattern={{major}}.{{minor}}
type=pep440,pattern={{major}}
type=sha,enable=true,prefix=sha-,format=short
flavor: |
latest=${{ matrix.gpu-driver == 'cuda' && github.ref == 'refs/heads/main' }}
suffix=-${{ matrix.gpu-driver }},onlatest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
platforms: ${{ env.PLATFORMS }}
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container
timeout-minutes: 40
id: docker_build
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: ${{ env.PLATFORMS }}
build-args: |
GPU_DRIVER=${{ matrix.gpu-driver }}
push: ${{ github.ref == 'refs/heads/main' || github.ref_type == 'tag' || github.event.inputs.push-to-registry }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# cache-from: |
# type=gha,scope=${{ github.ref_name }}-${{ matrix.gpu-driver }}
# type=gha,scope=main-${{ matrix.gpu-driver }}
# cache-to: type=gha,mode=max,scope=${{ github.ref_name }}-${{ matrix.gpu-driver }}
+38
View File
@@ -0,0 +1,38 @@
# Builds and uploads python build artifacts.
name: build wheel
on:
workflow_dispatch:
workflow_call:
jobs:
build-installer:
runs-on: ubuntu-latest
timeout-minutes: 5 # expected run time: <2 min
steps:
- name: checkout
uses: actions/checkout@v6
- name: setup python
uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: pip
cache-dependency-path: pyproject.toml
- name: install pypa/build
run: pip install --upgrade build
- name: setup frontend
uses: ./.github/actions/install-frontend-deps
- name: build wheel
id: build_wheel
run: ./scripts/build_wheel.sh
- name: upload python distribution artifact
uses: actions/upload-artifact@v7
with:
name: dist
path: ${{ steps.build_wheel.outputs.DIST_PATH }}
+34
View File
@@ -0,0 +1,34 @@
name: cleanup caches by a branch
on:
pull_request:
types:
- closed
workflow_dispatch:
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
REPO=${{ github.repository }}
BRANCH=${{ github.ref }}
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,29 @@
name: Close inactive issues
on:
schedule:
- cron: "00 4 * * *"
env:
DAYS_BEFORE_ISSUE_STALE: 30
DAYS_BEFORE_ISSUE_CLOSE: 14
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
with:
days-before-issue-stale: ${{ env.DAYS_BEFORE_ISSUE_STALE }}
days-before-issue-close: ${{ env.DAYS_BEFORE_ISSUE_CLOSE }}
stale-issue-label: "Inactive Issue"
stale-issue-message: "There has been no activity in this issue for ${{ env.DAYS_BEFORE_ISSUE_STALE }} days. If this issue is still being experienced, please reply with an updated confirmation that the issue is still being experienced with the latest release."
close-issue-message: "Due to inactivity, this issue was automatically closed. If you are still experiencing the issue, please recreate the issue."
days-before-pr-stale: -1
days-before-pr-close: -1
only-labels: "bug"
exempt-issue-labels: "Active Issue"
repo-token: ${{ secrets.GITHUB_TOKEN }}
operations-per-run: 500
+149
View File
@@ -0,0 +1,149 @@
name: 'docs'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
workflow_dispatch:
inputs:
deploy_target:
description: 'Deploy target (custom = invoke.ai, ghpages = invoke-ai.github.io/InvokeAI)'
type: choice
options:
- custom
- ghpages
default: custom
permissions:
contents: read
pull-requests: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
cancel-in-progress: true
jobs:
changes:
runs-on: ubuntu-latest
outputs:
docs: ${{ steps.manual.outputs.docs || steps.filter.outputs.docs }}
steps:
- name: checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: mark manual run
if: github.event_name == 'workflow_dispatch'
id: manual
run: echo "docs=true" >> "$GITHUB_OUTPUT"
- name: detect docs-related changes
if: github.event_name != 'workflow_dispatch'
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
docs:
- '.github/workflows/deploy-docs.yml'
- 'docs/**'
- 'scripts/generate_docs_json.py'
- 'invokeai/app/**'
- 'invokeai/backend/**'
- 'pyproject.toml'
- 'uv.lock'
check-and-build:
needs: changes
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' &&
github.event.pull_request.draft == false &&
needs.changes.outputs.docs == 'true') ||
(github.event_name == 'push' && needs.changes.outputs.docs == 'true')
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- name: checkout
uses: actions/checkout@v6
# Python (needed for generate-docs-data)
- name: setup uv
uses: astral-sh/setup-uv@v8.1.0
with:
version: '0.11.12'
enable-cache: true
python-version: '3.11'
- name: setup python
uses: actions/setup-python@v6
with:
python-version: '3.11'
# generate_docs_json.py only needs the invokeai package importable
# (pydantic + invokeai.app/backend). Skip the [test] extra to keep CI fast.
- name: install python dependencies
run: uv sync --frozen
# Node (needed for docs build)
- name: setup node
uses: actions/setup-node@v6
with:
node-version: '22'
- name: setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10
run_install: false
- name: install docs dependencies
run: pnpm install --prefer-frozen-lockfile
working-directory: docs
# Checks
- name: verify generated docs data
run: pnpm run check-docs-data
working-directory: docs
- name: build docs
run: pnpm build
working-directory: docs
env:
DEPLOY_TARGET: ${{ github.event_name == 'workflow_dispatch' && inputs.deploy_target || 'custom' }}
ENABLE_ANALYTICS: ${{ github.ref == 'refs/heads/main' && (github.event_name != 'workflow_dispatch' || inputs.deploy_target == 'custom') }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: verify deploy output
run: pnpm run check-deploy-output
working-directory: docs
env:
DEPLOY_TARGET: ${{ github.event_name == 'workflow_dispatch' && inputs.deploy_target || 'custom' }}
# Upload artifact for deploy (main branch only)
- name: upload pages artifact
if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v5
with:
path: docs/dist
deploy:
if: github.ref == 'refs/heads/main'
needs: check-and-build
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
+96
View File
@@ -0,0 +1,96 @@
# Runs frontend code quality checks.
#
# Checks for changes to frontend files before running the checks.
# If always_run is true, always runs the checks.
name: 'frontend checks'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
workflow_call:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
defaults:
run:
working-directory: invokeai/frontend/web
jobs:
frontend-checks:
runs-on: ubuntu-latest
timeout-minutes: 10 # expected run time: <2 min
steps:
- uses: actions/checkout@v6
- name: Fail if package-lock.json is added/modified (pnpm only)
shell: bash
working-directory: .
run: |
set -euo pipefail
git fetch --no-tags --prune --depth=1 origin "${{ github.base_ref }}"
if git diff --name-only "origin/${{ github.base_ref }}...HEAD" | grep -E '(^|/)package-lock\.json$'; then
echo "::error::package-lock.json was added or modified. This repo uses pnpm only."
exit 1
fi
- name: check for changed frontend files
if: ${{ inputs.always_run != true }}
id: changed-files
# Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks.
# See:
# - CVE-2025-30066
# - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
# - https://github.com/tj-actions/changed-files/issues/2463
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
frontend:
- 'invokeai/frontend/web/**'
- name: install dependencies
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
uses: ./.github/actions/install-frontend-deps
- name: tsc
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm lint:tsc'
shell: bash
- name: dpdm
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm lint:dpdm'
shell: bash
- name: eslint
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm lint:eslint'
shell: bash
- name: prettier
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm lint:prettier'
shell: bash
- name: knip
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm lint:knip'
shell: bash
+65
View File
@@ -0,0 +1,65 @@
# Runs frontend tests.
#
# Checks for changes to frontend files before running the tests.
# If always_run is true, always runs the tests.
name: 'frontend tests'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
inputs:
always_run:
description: 'Always run the tests'
required: true
type: boolean
default: true
workflow_call:
inputs:
always_run:
description: 'Always run the tests'
required: true
type: boolean
default: true
defaults:
run:
working-directory: invokeai/frontend/web
jobs:
frontend-tests:
runs-on: ubuntu-latest
timeout-minutes: 10 # expected run time: <2 min
steps:
- uses: actions/checkout@v6
- name: check for changed frontend files
if: ${{ inputs.always_run != true }}
id: changed-files
# Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks.
# See:
# - CVE-2025-30066
# - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
# - https://github.com/tj-actions/changed-files/issues/2463
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
frontend:
- 'invokeai/frontend/web/**'
- name: install dependencies
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
uses: ./.github/actions/install-frontend-deps
- name: vitest
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm test:no-watch'
shell: bash
+18
View File
@@ -0,0 +1,18 @@
name: 'label PRs'
on:
- pull_request_target
jobs:
labeler:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v6
- name: label PRs
uses: actions/labeler@v6
with:
configuration-path: .github/pr_labels.yml
+30
View File
@@ -0,0 +1,30 @@
# Checks that large files and LFS-tracked files are properly checked in with pointer format.
# Uses https://github.com/ppremk/lfs-warning to detect LFS issues.
name: 'lfs checks'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
jobs:
lfs-check:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
# Required to label and comment on the PRs
pull-requests: write
steps:
- name: checkout
uses: actions/checkout@v6
- name: check lfs files
uses: ppremk/lfs-warning@v3.3
+115
View File
@@ -0,0 +1,115 @@
# Runs OpenAPI schema quality checks.
# Checked-in OpenAPI schema should match the generated server schema.
#
# Checks for changes to files before running the checks.
# If always_run is true, always runs the checks.
name: 'openapi checks'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
workflow_call:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
jobs:
openapi-checks:
env:
# uv requires a venv by default - but for this, we can simply use the system python
UV_SYSTEM_PYTHON: 1
runs-on: ubuntu-22.04
timeout-minutes: 15 # expected run time: <5 min
steps:
- name: checkout
uses: actions/checkout@v4
- name: Free up more disk space on the runner
# https://github.com/actions/runner-images/issues/2840#issuecomment-1284059930
run: |
echo "----- Free space before cleanup"
df -h
sudo rm -rf /usr/share/dotnet
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
if [ -f /mnt/swapfile ]; then
sudo swapoff /mnt/swapfile
sudo rm -rf /mnt/swapfile
fi
echo "----- Free space after cleanup"
df -h
- name: check for changed files
if: ${{ inputs.always_run != true }}
id: changed-files
# Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks.
# See:
# - CVE-2025-30066
# - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
# - https://github.com/tj-actions/changed-files/issues/2463
uses: tj-actions/changed-files@a284dc1814e3fd07f2e34267fc8f81227ed29fb8
with:
files_yaml: |
src:
- 'pyproject.toml'
- 'invokeai/**'
- name: setup uv
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
uses: astral-sh/setup-uv@v5
with:
version: '0.6.10'
enable-cache: true
python-version: '3.11'
- name: setup python
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: install dependencies
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
env:
UV_INDEX: ${{ matrix.extra-index-url }}
run: uv pip install --editable .
- name: install frontend dependencies
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
uses: ./.github/actions/install-frontend-deps
- name: copy schema
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
run: cp invokeai/frontend/web/openapi.json invokeai/frontend/web/openapi_orig.json
shell: bash
- name: generate schema
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
run: cd invokeai/frontend/web && uv run ../../../scripts/generate_openapi_schema.py > openapi.json && pnpm prettier --write openapi.json
shell: bash
- name: compare files
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
run: |
if ! diff invokeai/frontend/web/openapi.json invokeai/frontend/web/openapi_orig.json; then
echo "Files are different!";
exit 1;
fi
shell: bash
+82
View File
@@ -0,0 +1,82 @@
# Runs python code quality checks.
#
# Checks for changes to python files before running the checks.
# If always_run is true, always runs the checks.
#
# TODO: Add mypy or pyright to the checks.
name: 'python checks'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
workflow_call:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
jobs:
python-checks:
env:
# uv requires a venv by default - but for this, we can simply use the system python
UV_SYSTEM_PYTHON: 1
runs-on: ubuntu-latest
timeout-minutes: 5 # expected run time: <1 min
steps:
- name: checkout
uses: actions/checkout@v6
- name: check for changed python files
if: ${{ inputs.always_run != true }}
id: changed-files
# Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks.
# See:
# - CVE-2025-30066
# - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
# - https://github.com/tj-actions/changed-files/issues/2463
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
python:
- 'pyproject.toml'
- 'invokeai/**'
- '!invokeai/frontend/web/**'
- 'tests/**'
- name: setup uv
if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }}
uses: astral-sh/setup-uv@v8.1.0
with:
version: '0.6.10'
enable-cache: true
- name: check pypi classifiers
if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }}
run: uv run --no-project scripts/check_classifiers.py ./pyproject.toml
- name: ruff check
if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }}
run: uv tool run ruff@0.11.2 check --output-format=github .
shell: bash
- name: ruff format
if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }}
run: uv tool run ruff@0.11.2 format --check .
shell: bash
+103
View File
@@ -0,0 +1,103 @@
# Runs python tests on a matrix of python versions and platforms.
#
# Checks for changes to python files before running the tests.
# If always_run is true, always runs the tests.
name: 'python tests'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
inputs:
always_run:
description: 'Always run the tests'
required: true
type: boolean
default: true
workflow_call:
inputs:
always_run:
description: 'Always run the tests'
required: true
type: boolean
default: true
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
matrix:
strategy:
matrix:
python-version:
- '3.11'
- '3.12'
platform:
- linux-cpu
- macos-default
- windows-cpu
include:
- platform: linux-cpu
os: ubuntu-24.04
extra-index-url: 'https://download.pytorch.org/whl/cpu'
github-env: $GITHUB_ENV
- platform: macos-default
os: macOS-14
github-env: $GITHUB_ENV
- platform: windows-cpu
os: windows-2022
github-env: $env:GITHUB_ENV
name: 'py${{ matrix.python-version }}: ${{ matrix.platform }}'
runs-on: ${{ matrix.os }}
timeout-minutes: 15 # expected run time: 2-6 min, depending on platform
env:
PIP_USE_PEP517: '1'
steps:
- name: checkout
# https://github.com/nschloe/action-cached-lfs-checkout
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc
- name: check for changed python files
if: ${{ inputs.always_run != true }}
id: changed-files
# Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks.
# See:
# - CVE-2025-30066
# - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
# - https://github.com/tj-actions/changed-files/issues/2463
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
python:
- 'pyproject.toml'
- 'invokeai/**'
- '!invokeai/frontend/web/**'
- 'tests/**'
- name: setup uv
if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }}
uses: astral-sh/setup-uv@v8.1.0
with:
version: '0.6.10'
enable-cache: true
python-version: ${{ matrix.python-version }}
- name: install dependencies
if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }}
env:
UV_INDEX: ${{ matrix.extra-index-url }}
run: uv sync --no-progress --locked --extra test
- name: run pytest
if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }}
run: uv run --no-sync pytest
+108
View File
@@ -0,0 +1,108 @@
# Main release workflow. Triggered on tag push or manual trigger.
#
# - Runs all code checks and tests
# - Verifies the app version matches the tag version.
# - Builds the installer and build, uploading them as artifacts.
# - Publishes to TestPyPI and PyPI. Both are conditional on the previous steps passing and require a manual approval.
#
# See docs/RELEASE.md for more information on the release process.
name: release
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
check-version:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v6
- name: check python version
uses: samuelcolvin/check-python-version@v4
id: check-python-version
with:
version_file_path: invokeai/version/invokeai_version.py
frontend-checks:
uses: ./.github/workflows/frontend-checks.yml
with:
always_run: true
frontend-tests:
uses: ./.github/workflows/frontend-tests.yml
with:
always_run: true
python-checks:
uses: ./.github/workflows/python-checks.yml
with:
always_run: true
python-tests:
uses: ./.github/workflows/python-tests.yml
with:
always_run: true
build:
uses: ./.github/workflows/build-wheel.yml
publish-testpypi:
runs-on: ubuntu-latest
timeout-minutes: 5 # expected run time: <1 min
needs:
[
check-version,
frontend-checks,
frontend-tests,
python-checks,
python-tests,
build,
]
environment:
name: testpypi
url: https://test.pypi.org/p/invokeai
permissions:
id-token: write
steps:
- name: download distribution from build job
uses: actions/download-artifact@v8
with:
name: dist
path: dist/
- name: publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
publish-pypi:
runs-on: ubuntu-latest
timeout-minutes: 5 # expected run time: <1 min
needs:
[
check-version,
frontend-checks,
frontend-tests,
python-checks,
python-tests,
build,
]
environment:
name: pypi
url: https://pypi.org/p/invokeai
permissions:
id-token: write
steps:
- name: download distribution from build job
uses: actions/download-artifact@v8
with:
name: dist
path: dist/
- name: publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
+115
View File
@@ -0,0 +1,115 @@
# Runs typegen schema quality checks.
# Frontend types should match the server.
#
# Checks for changes to files before running the checks.
# If always_run is true, always runs the checks.
name: 'typegen checks'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
workflow_call:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
jobs:
typegen-checks:
env:
# uv requires a venv by default - but for this, we can simply use the system python
UV_SYSTEM_PYTHON: 1
runs-on: ubuntu-22.04
timeout-minutes: 15 # expected run time: <5 min
steps:
- name: checkout
uses: actions/checkout@v6
- name: Free up more disk space on the runner
# https://github.com/actions/runner-images/issues/2840#issuecomment-1284059930
run: |
echo "----- Free space before cleanup"
df -h
sudo rm -rf /usr/share/dotnet
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
if [ -f /mnt/swapfile ]; then
sudo swapoff /mnt/swapfile
sudo rm -rf /mnt/swapfile
fi
echo "----- Free space after cleanup"
df -h
- name: check for changed files
if: ${{ inputs.always_run != true }}
id: changed-files
# Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks.
# See:
# - CVE-2025-30066
# - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
# - https://github.com/tj-actions/changed-files/issues/2463
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
src:
- 'pyproject.toml'
- 'invokeai/**'
- name: setup uv
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
uses: astral-sh/setup-uv@v8.1.0
with:
version: '0.6.10'
enable-cache: true
python-version: '3.11'
- name: setup python
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: install dependencies
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
env:
UV_INDEX: ${{ matrix.extra-index-url }}
run: uv pip install --editable .
- name: install frontend dependencies
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
uses: ./.github/actions/install-frontend-deps
- name: copy schema
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
run: cp invokeai/frontend/web/src/services/api/schema.ts invokeai/frontend/web/src/services/api/schema_orig.ts
shell: bash
- name: generate schema
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
run: cd invokeai/frontend/web && uv run ../../../scripts/generate_openapi_schema.py | pnpm typegen
shell: bash
- name: compare files
if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }}
run: |
if ! diff invokeai/frontend/web/src/services/api/schema.ts invokeai/frontend/web/src/services/api/schema_orig.ts; then
echo "Files are different!";
exit 1;
fi
shell: bash
+68
View File
@@ -0,0 +1,68 @@
# Check the `uv` lockfile for consistency with `pyproject.toml`.
#
# If this check fails, you should run `uv lock` to update the lockfile.
name: 'uv lock checks'
on:
push:
branches:
- 'main'
pull_request:
types:
- 'ready_for_review'
- 'opened'
- 'synchronize'
merge_group:
workflow_dispatch:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
workflow_call:
inputs:
always_run:
description: 'Always run the checks'
required: true
type: boolean
default: true
jobs:
uv-lock-checks:
env:
# uv requires a venv by default - but for this, we can simply use the system python
UV_SYSTEM_PYTHON: 1
runs-on: ubuntu-latest
timeout-minutes: 5 # expected run time: <1 min
steps:
- name: checkout
uses: actions/checkout@v6
- name: check for changed python files
if: ${{ inputs.always_run != true }}
id: changed-files
# Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks.
# See:
# - CVE-2025-30066
# - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
# - https://github.com/tj-actions/changed-files/issues/2463
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
uvlock-pyprojecttoml:
- 'pyproject.toml'
- 'uv.lock'
- name: setup uv
if: ${{ steps.changed-files.outputs.uvlock-pyprojecttoml_any_changed == 'true' || inputs.always_run == true }}
uses: astral-sh/setup-uv@v8.1.0
with:
version: '0.6.10'
enable-cache: true
- name: check lockfile
if: ${{ steps.changed-files.outputs.uvlock-pyprojecttoml_any_changed == 'true' || inputs.always_run == true }}
run: uv lock --locked # this will exit with 1 if the lockfile is not consistent with pyproject.toml
shell: bash
+192
View File
@@ -0,0 +1,192 @@
.idea/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# emacs autosave and recovery files
*~
.#*
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coveragerc
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
cov.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
.pytest.ini
cover/
junit/
notes/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv*
env/
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
**/__pycache__/
# If it's a Mac
.DS_Store
# Let the frontend manage its own gitignore
!invokeai/frontend/web/*
# Scratch folder
.scratch/
worktrees/
.vscode/
.zed/
# source installer files
installer/*zip
installer/install.bat
installer/install.sh
installer/update.bat
installer/update.sh
installer/InvokeAI-Installer/
.aider*
.claude/
# Weblate configuration file
weblate.ini
View File
+1
View File
@@ -0,0 +1 @@
v22.14.0
+32
View File
@@ -0,0 +1,32 @@
# See https://pre-commit.com/ for usage and config
repos:
- repo: local
hooks:
- id: black
name: black
stages: [pre-commit]
language: system
entry: black
types: [python]
- id: flake8
name: flake8
stages: [pre-commit]
language: system
entry: flake8
types: [python]
- id: isort
name: isort
stages: [pre-commit]
language: system
entry: isort
types: [python]
- id: uvlock
name: uv lock
stages: [pre-commit]
language: system
entry: uv lock
files: ^pyproject\.toml$
pass_filenames: false
+13
View File
@@ -0,0 +1,13 @@
endOfLine: lf
tabWidth: 2
useTabs: false
singleQuote: true
quoteProps: as-needed
embeddedLanguageFormatting: auto
overrides:
- files: '*.md'
options:
proseWrap: preserve
printWidth: 80
parser: markdown
cursorOffset: -1
+84
View File
@@ -0,0 +1,84 @@
<img src="docs/assets/invoke_ai_banner.png" align="center">
Invoke-AI is a community of software developers, researchers, and user
interface experts who have come together on a voluntary basis to build
software tools which support cutting edge AI text-to-image
applications. This community is open to anyone who wishes to
contribute to the effort and has the skill and time to do so.
# Our Values
The InvokeAI team is a diverse community which includes individuals
from various parts of the world and many walks of life. Despite our
differences, we share a number of core values which we ask prospective
contributors to understand and respect. We believe:
1. That Open Source Software is a positive force in the world. We
create software that can be used, reused, and redistributed, without
restrictions, under a straightforward Open Source license (MIT). We
believe that Open Source benefits society as a whole by increasing the
availability of high quality software to all.
2. That those who create software should receive proper attribution
for their creative work. While we support the exchange and reuse of
Open Source Software, we feel strongly that the original authors of a
piece of code should receive credit for their contribution, and we
endeavor to do so whenever possible.
3. That there is moral ambiguity surrounding AI-assisted art. We are
aware of the moral and ethical issues surrounding the release of the
Stable Diffusion model and similar products. We are aware that, due to
the composition of their training sets, current AI-generated image
models are biased against certain ethnic groups, cultural concepts of
beauty, ethnic stereotypes, and gender roles.
1. We recognize the potential for harm to these groups that these biases
represent and trust that future AI models will take steps towards
reducing or eliminating the biases noted above, respect and give due
credit to the artists whose work is sourced, and call on developers
and users to favor these models over the older ones as they become
available.
4. We are deeply committed to ensuring that this technology benefits
everyone, including artists. We see AI art not as a replacement for
the artist, but rather as a tool to empower them. With that
in mind, we are constantly debating how to build systems that put
artists needs first: tools which can be readily integrated into an
artists existing workflows and practices, enhancing their work and
helping them to push it further. Every decision we take as a team,
which includes several artists, aims to build towards that goal.
5. That artificial intelligence can be a force for good in the world,
but must be used responsibly. Artificial intelligence technologies
have the potential to improve society, in everything from cancer care,
to customer service, to creative writing.
1. While we do not believe that software should arbitrarily limit what
users can do with it, we recognize that when used irresponsibly, AI
has the potential to do much harm. Our Discord server is actively
moderated in order to minimize the potential of harm from
user-contributed images. In addition, we ask users of our software to
refrain from using it in any way that would cause mental, emotional or
physical harm to individuals and vulnerable populations including (but
not limited to) women; minors; ethnic minorities; religious groups;
members of LGBTQIA communities; and people with disabilities or
impairments.
2. Note that some of the image generation AI models which the Invoke-AI
toolkit supports carry licensing agreements which impose restrictions
on how the model is used. We ask that our users read and agree to
these terms if they wish to make use of these models. These agreements
are distinct from the MIT license which applies to the InvokeAI
software and source code.
6. That mutual respect is key to a healthy software development
community. Members of the InvokeAI community are expected to treat
each other with respect, beneficence, and empathy. Each of us has a
different background and a unique set of skills. We strive to help
each other grow and gain new skills, and we apportion expectations in
a way that balances the members' time, skillset, and interest
area. Disputes are resolved by open and honest communication.
## Signature
This document has been collectively crafted and approved by the current InvokeAI team members, as of 28 Nov 2022: **lstein** (Lincoln Stein), **blessedcoolant**, **hipsterusername** (Kent Keirsey), **Kyle0654** (Kyle Schouviller), **damian0815**, **mauwii** (Matthias Wild), **Netsvetaev** (Artur Netsvetaev), **psychedelicious**, **tildebyte**, **keturn**, and **ebr** (Eugene Brodsky). Although individuals within the group may hold differing views on particular details and/or their implications, we are all in agreement about its fundamental statements, as well as their significance and importance to this project moving forward.
+176
View File
@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
+294
View File
@@ -0,0 +1,294 @@
Copyright (c) 2022 Robin Rombach and Patrick Esser and contributors
CreativeML Open RAIL-M
dated August 22, 2022
Section I: PREAMBLE
Multimodal generative models are being widely adopted and used, and
have the potential to transform the way artists, among other
individuals, conceive and benefit from AI or ML technologies as a tool
for content creation.
Notwithstanding the current and potential benefits that these
artifacts can bring to society at large, there are also concerns about
potential misuses of them, either due to their technical limitations
or ethical considerations.
In short, this license strives for both the open and responsible
downstream use of the accompanying model. When it comes to the open
character, we took inspiration from open source permissive licenses
regarding the grant of IP rights. Referring to the downstream
responsible use, we added use-based restrictions not permitting the
use of the Model in very specific scenarios, in order for the licensor
to be able to enforce the license in case potential misuses of the
Model may occur. At the same time, we strive to promote open and
responsible research on generative models for art and content
generation.
Even though downstream derivative versions of the model could be
released under different licensing terms, the latter will always have
to include - at minimum - the same use-based restrictions as the ones
in the original license (this license). We believe in the intersection
between open and responsible AI development; thus, this License aims
to strike a balance between both in order to enable responsible
open-science in the field of AI.
This License governs the use of the model (and its derivatives) and is
informed by the model card associated with the model.
NOW THEREFORE, You and Licensor agree as follows:
1. Definitions
- "License" means the terms and conditions for use, reproduction, and
Distribution as defined in this document.
- "Data" means a collection of information and/or content extracted
from the dataset used with the Model, including to train, pretrain,
or otherwise evaluate the Model. The Data is not licensed under this
License.
- "Output" means the results of operating a Model as embodied in
informational content resulting therefrom.
- "Model" means any accompanying machine-learning based assemblies
(including checkpoints), consisting of learnt weights, parameters
(including optimizer states), corresponding to the model
architecture as embodied in the Complementary Material, that have
been trained or tuned, in whole or in part on the Data, using the
Complementary Material.
- "Derivatives of the Model" means all modifications to the Model,
works based on the Model, or any other model which is created or
initialized by transfer of patterns of the weights, parameters,
activations or output of the Model, to the other model, in order to
cause the other model to perform similarly to the Model, including -
but not limited to - distillation methods entailing the use of
intermediate data representations or methods based on the generation
of synthetic data by the Model for training the other model.
- "Complementary Material" means the accompanying source code and
scripts used to define, run, load, benchmark or evaluate the Model,
and used to prepare data for training or evaluation, if any. This
includes any accompanying documentation, tutorials, examples, etc,
if any.
- "Distribution" means any transmission, reproduction, publication or
other sharing of the Model or Derivatives of the Model to a third
party, including providing the Model as a hosted service made
available by electronic or other remote means - e.g. API-based or
web access.
- "Licensor" means the copyright owner or entity authorized by the
copyright owner that is granting the License, including the persons
or entities that may have rights in the Model and/or distributing
the Model.
- "You" (or "Your") means an individual or Legal Entity exercising
permissions granted by this License and/or making use of the Model
for whichever purpose and in any field of use, including usage of
the Model in an end-use application - e.g. chatbot, translator,
image generator.
- "Third Parties" means individuals or legal entities that are not
under common control with Licensor or You.
- "Contribution" means any work of authorship, including the original
version of the Model and any modifications or additions to that
Model or Derivatives of the Model thereof, that is intentionally
submitted to Licensor for inclusion in the Model by the copyright
owner or by an individual or Legal Entity authorized to submit on
behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written
communication sent to the Licensor or its representatives, including
but not limited to communication on electronic mailing lists, source
code control systems, and issue tracking systems that are managed
by, or on behalf of, the Licensor for the purpose of discussing and
improving the Model, but excluding communication that is
conspicuously marked or otherwise designated in writing by the
copyright owner as "Not a Contribution."
- "Contributor" means Licensor and any individual or Legal Entity on
behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Model.
Section II: INTELLECTUAL PROPERTY RIGHTS
Both copyright and patent grants apply to the Model, Derivatives of
the Model and Complementary Material. The Model and Derivatives of the
Model are subject to additional terms as described in Section III.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare, publicly display, publicly
perform, sublicense, and distribute the Complementary Material, the
Model, and Derivatives of the Model.
3. Grant of Patent License. Subject to the terms and conditions of
this License and where and as applicable, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge,
royalty-free, irrevocable (except as stated in this paragraph) patent
license to make, have made, use, offer to sell, sell, import, and
otherwise transfer the Model and the Complementary Material, where
such license applies only to those patent claims licensable by such
Contributor that are necessarily infringed by their Contribution(s)
alone or by combination of their Contribution(s) with the Model to
which such Contribution(s) was submitted. If You institute patent
litigation against any entity (including a cross-claim or counterclaim
in a lawsuit) alleging that the Model and/or Complementary Material or
a Contribution incorporated within the Model and/or Complementary
Material constitutes direct or contributory patent infringement, then
any patent licenses granted to You under this License for the Model
and/or Work shall terminate as of the date such litigation is asserted
or filed.
Section III: CONDITIONS OF USAGE, DISTRIBUTION AND REDISTRIBUTION
4. Distribution and Redistribution. You may host for Third Party
remote access purposes (e.g. software-as-a-service), reproduce and
distribute copies of the Model or Derivatives of the Model thereof in
any medium, with or without modifications, provided that You meet the
following conditions: Use-based restrictions as referenced in
paragraph 5 MUST be included as an enforceable provision by You in any
type of legal agreement (e.g. a license) governing the use and/or
distribution of the Model or Derivatives of the Model, and You shall
give notice to subsequent users You Distribute to, that the Model or
Derivatives of the Model are subject to paragraph 5. This provision
does not apply to the use of Complementary Material. You must give
any Third Party recipients of the Model or Derivatives of the Model a
copy of this License; You must cause any modified files to carry
prominent notices stating that You changed the files; You must retain
all copyright, patent, trademark, and attribution notices excluding
those notices that do not pertain to any part of the Model,
Derivatives of the Model. You may add Your own copyright statement to
Your modifications and may provide additional or different license
terms and conditions - respecting paragraph 4.a. - for use,
reproduction, or Distribution of Your modifications, or for any such
Derivatives of the Model as a whole, provided Your use, reproduction,
and Distribution of the Model otherwise complies with the conditions
stated in this License.
5. Use-based restrictions. The restrictions set forth in Attachment A
are considered Use-based restrictions. Therefore You cannot use the
Model and the Derivatives of the Model for the specified restricted
uses. You may use the Model subject to this License, including only
for lawful purposes and in accordance with the License. Use may
include creating any content with, finetuning, updating, running,
training, evaluating and/or reparametrizing the Model. You shall
require all of Your users who use the Model or a Derivative of the
Model to comply with the terms of this paragraph (paragraph 5).
6. The Output You Generate. Except as set forth herein, Licensor
claims no rights in the Output You generate using the Model. You are
accountable for the Output you generate and its subsequent uses. No
use of the output can contravene any provision as stated in the
License.
Section IV: OTHER PROVISIONS
7. Updates and Runtime Restrictions. To the maximum extent permitted
by law, Licensor reserves the right to restrict (remotely or
otherwise) usage of the Model in violation of this License, update the
Model through electronic means, or modify the Output of the Model
based on updates. You shall undertake reasonable efforts to use the
latest version of the Model.
8. Trademarks and related. Nothing in this License permits You to make
use of Licensors trademarks, trade names, logos or to otherwise
suggest endorsement or misrepresent the relationship between the
parties; and any rights not expressly granted herein are reserved by
the Licensors.
9. Disclaimer of Warranty. Unless required by applicable law or agreed
to in writing, Licensor provides the Model and the Complementary
Material (and each Contributor provides its Contributions) on an "AS
IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied, including, without limitation, any warranties or
conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
A PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Model, Derivatives of
the Model, and the Complementary Material and assume any risks
associated with Your exercise of permissions under this License.
10. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise, unless
required by applicable law (such as deliberate and grossly negligent
acts) or agreed to in writing, shall any Contributor be liable to You
for damages, including any direct, indirect, special, incidental, or
consequential damages of any character arising as a result of this
License or out of the use or inability to use the Model and the
Complementary Material (including but not limited to damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any
and all other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
11. Accepting Warranty or Additional Liability. While redistributing
the Model, Derivatives of the Model and the Complementary Material
thereof, You may choose to offer, and charge a fee for, acceptance of
support, warranty, indemnity, or other liability obligations and/or
rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if
You agree to indemnify, defend, and hold each Contributor harmless for
any liability incurred by, or claims asserted against, such
Contributor by reason of your accepting any such warranty or
additional liability.
12. If any provision of this License is held to be invalid, illegal or
unenforceable, the remaining provisions shall be unaffected thereby
and remain valid as if such provision had not been set forth herein.
END OF TERMS AND CONDITIONS
Attachment A
Use Restrictions
You agree not to use the Model or Derivatives of the Model:
- In any way that violates any applicable national, federal, state,
local or international law or regulation;
- For the purpose of exploiting, harming or attempting to exploit or
harm minors in any way;
- To generate or disseminate verifiably false information and/or
content with the purpose of harming others;
- To generate or disseminate personal identifiable information that
can be used to harm an individual;
- To defame, disparage or otherwise harass others;
- For fully automated decision making that adversely impacts an
individuals legal rights or otherwise creates or modifies a
binding, enforceable obligation;
pp- For any use intended to or which has the effect of discriminating
against or harming individuals or groups based on online or offline
social behavior or known or predicted personal or personality
characteristics;
- To exploit any of the vulnerabilities of a specific group of persons
based on their age, social, physical or mental characteristics, in
order to materially distort the behavior of a person pertaining to
that group in a manner that causes or is likely to cause that person
or another person physical or psychological harm;
- For any use intended to or which has the effect of discriminating
against individuals or groups based on legally protected
characteristics or categories;
- To provide medical advice and medical results interpretation;
- To generate or disseminate information for the purpose to be used
for administration of justice, law enforcement, immigration or
asylum processes, such as predicting an individual will commit
fraud/crime commitment (e.g. by text profiling, drawing causal
relationships between assertions made in documents, indiscriminate
and arbitrarily-targeted use).
+290
View File
@@ -0,0 +1,290 @@
Copyright (c) 2023 Stability AI
CreativeML Open RAIL++-M License dated July 26, 2023
Section I: PREAMBLE
Multimodal generative models are being widely adopted and used, and
have the potential to transform the way artists, among other
individuals, conceive and benefit from AI or ML technologies as a tool
for content creation.
Notwithstanding the current and potential benefits that these
artifacts can bring to society at large, there are also concerns about
potential misuses of them, either due to their technical limitations
or ethical considerations.
In short, this license strives for both the open and responsible
downstream use of the accompanying model. When it comes to the open
character, we took inspiration from open source permissive licenses
regarding the grant of IP rights. Referring to the downstream
responsible use, we added use-based restrictions not permitting the
use of the model in very specific scenarios, in order for the licensor
to be able to enforce the license in case potential misuses of the
Model may occur. At the same time, we strive to promote open and
responsible research on generative models for art and content
generation.
Even though downstream derivative versions of the model could be
released under different licensing terms, the latter will always have
to include - at minimum - the same use-based restrictions as the ones
in the original license (this license). We believe in the intersection
between open and responsible AI development; thus, this agreement aims
to strike a balance between both in order to enable responsible
open-science in the field of AI.
This CreativeML Open RAIL++-M License governs the use of the model
(and its derivatives) and is informed by the model card associated
with the model.
NOW THEREFORE, You and Licensor agree as follows:
Definitions
"License" means the terms and conditions for use, reproduction, and
Distribution as defined in this document.
"Data" means a collection of information and/or content extracted from
the dataset used with the Model, including to train, pretrain, or
otherwise evaluate the Model. The Data is not licensed under this
License.
"Output" means the results of operating a Model as embodied in
informational content resulting therefrom.
"Model" means any accompanying machine-learning based assemblies
(including checkpoints), consisting of learnt weights, parameters
(including optimizer states), corresponding to the model architecture
as embodied in the Complementary Material, that have been trained or
tuned, in whole or in part on the Data, using the Complementary
Material.
"Derivatives of the Model" means all modifications to the Model, works
based on the Model, or any other model which is created or initialized
by transfer of patterns of the weights, parameters, activations or
output of the Model, to the other model, in order to cause the other
model to perform similarly to the Model, including - but not limited
to - distillation methods entailing the use of intermediate data
representations or methods based on the generation of synthetic data
by the Model for training the other model.
"Complementary Material" means the accompanying source code and
scripts used to define, run, load, benchmark or evaluate the Model,
and used to prepare data for training or evaluation, if any. This
includes any accompanying documentation, tutorials, examples, etc, if
any.
"Distribution" means any transmission, reproduction, publication or
other sharing of the Model or Derivatives of the Model to a third
party, including providing the Model as a hosted service made
available by electronic or other remote means - e.g. API-based or web
access.
"Licensor" means the copyright owner or entity authorized by the
copyright owner that is granting the License, including the persons or
entities that may have rights in the Model and/or distributing the
Model.
"You" (or "Your") means an individual or Legal Entity exercising
permissions granted by this License and/or making use of the Model for
whichever purpose and in any field of use, including usage of the
Model in an end-use application - e.g. chatbot, translator, image
generator.
"Third Parties" means individuals or legal entities that are not under
common control with Licensor or You.
"Contribution" means any work of authorship, including the original
version of the Model and any modifications or additions to that Model
or Derivatives of the Model thereof, that is intentionally submitted
to Licensor for inclusion in the Model by the copyright owner or by an
individual or Legal Entity authorized to submit on behalf of the
copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent to
the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control
systems, and issue tracking systems that are managed by, or on behalf
of, the Licensor for the purpose of discussing and improving the
Model, but excluding communication that is conspicuously marked or
otherwise designated in writing by the copyright owner as "Not a
Contribution."
"Contributor" means Licensor and any individual or Legal Entity on
behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Model.
Section II: INTELLECTUAL PROPERTY RIGHTS
Both copyright and patent grants apply to the Model, Derivatives of
the Model and Complementary Material. The Model and Derivatives of the
Model are subject to additional terms as described in
Section III.
Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare, publicly display, publicly
perform, sublicense, and distribute the Complementary Material, the
Model, and Derivatives of the Model.
Grant of Patent License. Subject to the terms and conditions of this
License and where and as applicable, each Contributor hereby grants to
You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this paragraph) patent license to
make, have made, use, offer to sell, sell, import, and otherwise
transfer the Model and the Complementary Material, where such license
applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by
combination of their Contribution(s) with the Model to which such
Contribution(s) was submitted. If You institute patent litigation
against any entity (including a cross-claim or counterclaim in a
lawsuit) alleging that the Model and/or Complementary Material or a
Contribution incorporated within the Model and/or Complementary
Material constitutes direct or contributory patent infringement, then
any patent licenses granted to You under this License for the Model
and/or Work shall terminate as of the date such litigation is asserted
or filed.
Section III: CONDITIONS OF USAGE, DISTRIBUTION AND REDISTRIBUTION
Distribution and Redistribution. You may host for Third Party remote
access purposes (e.g. software-as-a-service), reproduce and distribute
copies of the Model or Derivatives of the Model thereof in any medium,
with or without modifications, provided that You meet the following
conditions: Use-based restrictions as referenced in paragraph 5 MUST
be included as an enforceable provision by You in any type of legal
agreement (e.g. a license) governing the use and/or distribution of
the Model or Derivatives of the Model, and You shall give notice to
subsequent users You Distribute to, that the Model or Derivatives of
the Model are subject to paragraph 5. This provision does not apply to
the use of Complementary Material. You must give any Third Party
recipients of the Model or Derivatives of the Model a copy of this
License; You must cause any modified files to carry prominent notices
stating that You changed the files; You must retain all copyright,
patent, trademark, and attribution notices excluding those notices
that do not pertain to any part of the Model, Derivatives of the
Model. You may add Your own copyright statement to Your modifications
and may provide additional or different license terms and conditions -
respecting paragraph 4.a. - for use, reproduction, or Distribution of
Your modifications, or for any such Derivatives of the Model as a
whole, provided Your use, reproduction, and Distribution of the Model
otherwise complies with the conditions stated in this License.
Use-based restrictions. The restrictions set forth in Attachment A are
considered Use-based restrictions. Therefore You cannot use the Model
and the Derivatives of the Model for the specified restricted
uses. You may use the Model subject to this License, including only
for lawful purposes and in accordance with the License. Use may
include creating any content with, finetuning, updating, running,
training, evaluating and/or reparametrizing the Model. You shall
require all of Your users who use the Model or a Derivative of the
Model to comply with the terms of this paragraph (paragraph 5).
The Output You Generate. Except as set forth herein, Licensor claims
no rights in the Output You generate using the Model. You are
accountable for the Output you generate and its subsequent uses. No
use of the output can contravene any provision as stated in the
License.
Section IV: OTHER PROVISIONS
Updates and Runtime Restrictions. To the maximum extent permitted by
law, Licensor reserves the right to restrict (remotely or otherwise)
usage of the Model in violation of this License.
Trademarks and related. Nothing in this License permits You to make
use of Licensors trademarks, trade names, logos or to otherwise
suggest endorsement or misrepresent the relationship between the
parties; and any rights not expressly granted herein are reserved by
the Licensors.
Disclaimer of Warranty. Unless required by applicable law or agreed to
in writing, Licensor provides the Model and the Complementary Material
(and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Model, Derivatives of
the Model, and the Complementary Material and assume any risks
associated with Your exercise of permissions under this License.
Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise, unless
required by applicable law (such as deliberate and grossly negligent
acts) or agreed to in writing, shall any Contributor be liable to You
for damages, including any direct, indirect, special, incidental, or
consequential damages of any character arising as a result of this
License or out of the use or inability to use the Model and the
Complementary Material (including but not limited to damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any
and all other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
Accepting Warranty or Additional Liability. While redistributing the
Model, Derivatives of the Model and the Complementary Material
thereof, You may choose to offer, and charge a fee for, acceptance of
support, warranty, indemnity, or other liability obligations and/or
rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if
You agree to indemnify, defend, and hold each Contributor harmless for
any liability incurred by, or claims asserted against, such
Contributor by reason of your accepting any such warranty or
additional liability.
If any provision of this License is held to be invalid, illegal or
unenforceable, the remaining provisions shall be unaffected thereby
and remain valid as if such provision had not been set forth herein.
END OF TERMS AND CONDITIONS
Attachment A
Use Restrictions
You agree not to use the Model or Derivatives of the Model:
* In any way that violates any applicable national, federal, state,
local or international law or regulation;
* For the purpose of exploiting, harming or attempting to exploit or
harm minors in any way;
* To generate or disseminate verifiably false information and/or
content with the purpose of harming others;
* To generate or disseminate personal identifiable information that
can be used to harm an individual;
* To defame, disparage or otherwise harass others;
* For fully automated decision making that adversely impacts an
individuals legal rights or otherwise creates or modifies a
binding, enforceable obligation;
* For any use intended to or which has the effect of discriminating
against or harming individuals or groups based on online or offline
social behavior or known or predicted personal or personality
characteristics;
* To exploit any of the vulnerabilities of a specific group of persons
based on their age, social, physical or mental characteristics, in
order to materially distort the behavior of a person pertaining to
that group in a manner that causes or is likely to cause that person
or another person physical or psychological harm;
* For any use intended to or which has the effect of discriminating
against individuals or groups based on legally protected
characteristics or categories;
* To provide medical advice and medical results interpretation;
* To generate or disseminate information for the purpose to be used
for administration of justice, law enforcement, immigration or
asylum processes, such as predicting an individual will commit
fraud/crime commitment (e.g. by text profiling, drawing causal
relationships between assertions made in documents, indiscriminate
and arbitrarily-targeted use).
+108
View File
@@ -0,0 +1,108 @@
# simple Makefile with scripts that are otherwise hard to remember
# to use, run from the repo root `make <command>`
default: help
help:
@echo Developer commands:
@echo
@echo "ruff Run ruff, fixing any safely-fixable errors and formatting"
@echo "ruff-unsafe Run ruff, fixing all fixable errors and formatting"
@echo "mypy Run mypy using the config in pyproject.toml to identify type mismatches and other coding errors"
@echo "mypy-all Run mypy ignoring the config in pyproject.tom but still ignoring missing imports"
@echo "test Run the unit tests."
@echo "frontend-install Install the pnpm modules needed for the frontend"
@echo "frontend-build Build the frontend for localhost:9090"
@echo "frontend-test Run the frontend test suite once"
@echo "frontend-dev Run the frontend in developer mode on localhost:5173"
@echo "frontend-openapi Generate the OpenAPI schema"
@echo "frontend-typegen Generate types for the frontend from the OpenAPI schema"
@echo "frontend-lint Run frontend checks and fixable lint/format steps"
@echo "wheel Build the wheel for the current version"
@echo "tag-release Tag the GitHub repository with the current version (use at release time only!)"
@echo "openapi Generate the OpenAPI schema for the app, outputting to stdout"
@echo "docs-install Install the pnpm modules needed for the docs site"
@echo "docs-dev Serve the astro starlight docs site with live reload"
@echo "docs-build Build the docs site for production"
@echo "docs-preview Preview the docs site locally"
# Runs ruff, fixing any safely-fixable errors and formatting
ruff:
cd invokeai && uv tool run ruff@0.11.2 format
# Runs ruff, fixing all errors it can fix and formatting
ruff-unsafe:
ruff check . --fix --unsafe-fixes
ruff format
# Runs mypy, using the config in pyproject.toml
mypy:
mypy scripts/invokeai-web.py
# Runs mypy, ignoring the config in pyproject.toml but still ignoring missing (untyped) imports
# (many files are ignored by the config, so this is useful for checking all files)
mypy-all:
mypy scripts/invokeai-web.py --config-file= --ignore-missing-imports
# Run the unit tests
test:
pytest ./tests
# Install the pnpm modules needed for the front end
frontend-install:
rm -rf invokeai/frontend/web/node_modules
cd invokeai/frontend/web && pnpm install
# Build the frontend
frontend-build:
cd invokeai/frontend/web && pnpm build
# Run the frontend test suite once
frontend-test:
cd invokeai/frontend/web && pnpm run test:run
# Run the frontend in dev mode
frontend-dev:
cd invokeai/frontend/web && pnpm dev
# Generate the OpenAPI Schema for the app
frontend-openapi:
cd invokeai/frontend/web && \
python ../../../scripts/generate_openapi_schema.py > openapi.json && \
pnpm prettier --write openapi.json
frontend-typegen:
cd invokeai/frontend/web && python ../../../scripts/generate_openapi_schema.py | pnpm typegen
frontend-lint:
cd invokeai/frontend/web/src && \
pnpm lint:tsc && \
pnpm lint:dpdm && \
pnpm lint:eslint --fix && \
pnpm lint:prettier --write
# Tag the release
wheel:
cd scripts && ./build_wheel.sh
# Tag the release
tag-release:
cd scripts && ./tag_release.sh
# Generate the OpenAPI Schema for the app
openapi:
python scripts/generate_openapi_schema.py
# Install the pnpm modules needed for the docs site
docs-install:
cd docs && pnpm install
# Serve the astro starlight docs site w/ live reload
docs-dev:
cd docs && pnpm run dev
docs-build:
cd docs && DEPLOY_TARGET='custom' pnpm run build
docs-preview:
cd docs && pnpm run preview
+143
View File
@@ -0,0 +1,143 @@
<div align="center">
![project hero](https://github.com/invoke-ai/InvokeAI/assets/31807370/6e3728c7-e90e-4711-905c-3b55844ff5be)
# Invoke - Professional Creative AI Tools for Visual Media
[![discord badge]][discord link] [![latest release badge]][latest release link] [![github stars badge]][github stars link] [![github forks badge]][github forks link] [![CI checks on main badge]][CI checks on main link] [![latest commit to main badge]][latest commit to main link] [![github open issues badge]][github open issues link] [![github open prs badge]][github open prs link] [![translation status badge]][translation status link]
[![Sponsor Invoke](https://img.shields.io/badge/Sponsor-Invoke-ea4aaa?logo=githubsponsors&logoColor=white)][sponsor link]
</div>
Invoke is a leading creative engine built to empower professionals and enthusiasts alike. Generate and create stunning visual media using the latest AI-driven technologies. Invoke offers an industry leading web-based UI, and serves as the foundation for multiple commercial products.
- Free to use under a commercially-friendly license
- Download and install on compatible hardware
- Generate, refine, iterate on images, and build workflows
![Highlighted Features - Canvas and Workflows](https://github.com/invoke-ai/InvokeAI/assets/31807370/708f7a82-084f-4860-bfbe-e2588c53548d)
# Documentation
| **Quick Links** |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Installation and Updates][installation docs] - [Documentation and Tutorials][docs home] - [Bug Reports][github issues] - [Contributing][contributing docs] |
# Installation
To get started with Invoke, [Download the Launcher](https://github.com/invoke-ai/launcher/releases/latest).
## Troubleshooting, FAQ and Support
Please review our [FAQ][faq] for solutions to common installation problems and other issues.
For more help, please join our [Discord][discord link].
## Features
Full details on features can be found in [our documentation][features docs].
### Web Server & UI
Invoke runs a locally hosted web server & React UI with an industry-leading user experience.
### Unified Canvas
The Unified Canvas is a fully integrated canvas implementation with support for all core generation capabilities, in/out-painting, brush tools, and more. This creative tool unlocks the capability for artists to create with AI as a creative collaborator, and can be used to augment AI-generated imagery, sketches, photography, renders, and more.
### Workflows & Nodes
Invoke offers a fully featured workflow management solution, enabling users to combine the power of node-based workflows with the ease of a UI. This allows for customizable generation pipelines to be developed and shared by users looking to create specific workflows to support their production use-cases.
### Board & Gallery Management
Invoke features an organized gallery system for easily storing, accessing, and remixing your content in the Invoke workspace. Images can be dragged/dropped onto any Image-base UI element in the application, and rich metadata within the Image allows for easy recall of key prompts or settings used in your workflow.
### Model Support
- SD 1.5
- SD 2.0
- SDXL
- SD 3.5 Medium
- SD 3.5 Large
- CogView 4
- Flux.1 Dev
- Flux.1 Schnell
- Flux.1 Kontext
- Flux.1 Krea
- Flux Redux
- Flux Fill
- Flux.2 Klein 4B
- Flux.2 Klein 9B
- Z-Image Turbo
- Z-Image Base
- Anima
- Qwen Image
- Qwen Image Edit
- Nano Banana (API Only)
- GPT Image (API Only)
- Wan (API Only)
### Other features
- Support for ckpt, diffusers, and some gguf models
- Upscaling Tools
- Embedding Manager & Support
- Model Manager & Support
- Workflow creation & management
- Node-Based Architecture
- Object Segmentation & Selection Models (SAM / SAM2)
## Contributing
Anyone who wishes to contribute to this project - whether documentation, features, bug fixes, code cleanup, testing, or code reviews - is very much encouraged to do so.
Get started with contributing by reading our [contribution documentation][contributing docs], joining the [#dev-chat] or the GitHub discussion board.
We hope you enjoy using Invoke as much as we enjoy creating it, and we hope you will elect to become part of our community.
## Sponsors
Invoke's open-source development is powered by our sponsors. If Invoke is valuable to you or your business, please consider [sponsoring us][sponsor link] — it directly funds maintenance, new features, and community support.
<!-- Sponsor logos can be added below, or automated with a GitHub Action
such as `JamesIves/github-sponsors-readme-action` to keep them in sync. -->
[![Sponsor Invoke](https://img.shields.io/badge/Sponsor-Invoke-ea4aaa?logo=githubsponsors&logoColor=white)][sponsor link]
## Thanks
Invoke is a combined effort of [passionate and talented people from across the world][contributors]. We thank them for their time, hard work and effort.
Original portions of the software are Copyright © 2024 by respective contributors.
[features docs]: https://invoke.ai/
[faq]: https://invoke.ai/troubleshooting/faq/
[contributors]: https://invoke.ai/contributing/contributors/
[github issues]: https://github.com/invoke-ai/InvokeAI/issues
[docs home]: https://invoke.ai
[installation docs]: https://invoke.ai/start-here/installation/
[sponsor link]: https://github.com/sponsors/invoke-ai
[#dev-chat]: https://discord.com/channels/1020123559063990373/1049495067846524939
[contributing docs]: https://invoke.ai/contributing/
[CI checks on main badge]: https://flat.badgen.net/github/checks/invoke-ai/InvokeAI/main?label=CI%20status%20on%20main&cache=900&icon=github
[CI checks on main link]: https://github.com/invoke-ai/InvokeAI/actions?query=branch%3Amain
[discord badge]: https://flat.badgen.net/discord/members/ZmtBAhwWhy?icon=discord
[discord link]: https://discord.gg/ZmtBAhwWhy
[github forks badge]: https://flat.badgen.net/github/forks/invoke-ai/InvokeAI?icon=github
[github forks link]: https://useful-forks.github.io/?repo=invoke-ai%2FInvokeAI
[github open issues badge]: https://flat.badgen.net/github/open-issues/invoke-ai/InvokeAI?icon=github
[github open issues link]: https://github.com/invoke-ai/InvokeAI/issues?q=is%3Aissue+is%3Aopen
[github open prs badge]: https://flat.badgen.net/github/open-prs/invoke-ai/InvokeAI?icon=github
[github open prs link]: https://github.com/invoke-ai/InvokeAI/pulls?q=is%3Apr+is%3Aopen
[github stars badge]: https://flat.badgen.net/github/stars/invoke-ai/InvokeAI?icon=github
[github stars link]: https://github.com/invoke-ai/InvokeAI/stargazers
[latest commit to main badge]: https://flat.badgen.net/github/last-commit/invoke-ai/InvokeAI/main?icon=github&color=yellow&label=last%20dev%20commit&cache=900
[latest commit to main link]: https://github.com/invoke-ai/InvokeAI/commits/main
[latest release badge]: https://flat.badgen.net/github/release/invoke-ai/InvokeAI/development?icon=github
[latest release link]: https://github.com/invoke-ai/InvokeAI/releases/latest
[translation status badge]: https://hosted.weblate.org/widgets/invokeai/-/svg-badge.svg
[translation status link]: https://hosted.weblate.org/engage/invokeai/
[nvidia docker docs]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
[amd docker docs]: https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html
+7
View File
@@ -0,0 +1,7 @@
# WeHub 来源说明
- 原始项目:`invoke-ai/InvokeAI`
- 原始仓库:https://github.com/invoke-ai/InvokeAI
- 导入方式:上游默认分支的最新快照
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
+14
View File
@@ -0,0 +1,14 @@
# Security Policy
## Supported Versions
Only the latest version of Invoke will receive security updates.
We do not currently maintain multiple versions of the application with updates.
## Reporting a Vulnerability
To report a vulnerability, contact the Invoke team directly at security@invoke.ai
At this time, we do not maintain a formal bug bounty program.
You can also share identified security issues with our team on huntr.com
+140
View File
@@ -0,0 +1,140 @@
# Stable Diffusion v1 Model Card
This model card focuses on the model associated with the Stable Diffusion model, available [here](https://github.com/CompVis/stable-diffusion).
## Model Details
- **Developed by:** Robin Rombach, Patrick Esser
- **Model type:** Diffusion-based text-to-image generation model
- **Language(s):** English
- **License:** [Proprietary](LICENSE)
- **Model Description:** This is a model that can be used to generate and modify images based on text prompts. It is a [Latent Diffusion Model](https://arxiv.org/abs/2112.10752) that uses a fixed, pretrained text encoder ([CLIP ViT-L/14](https://arxiv.org/abs/2103.00020)) as suggested in the [Imagen paper](https://arxiv.org/abs/2205.11487).
- **Resources for more information:** [GitHub Repository](https://github.com/CompVis/stable-diffusion), [Paper](https://arxiv.org/abs/2112.10752).
- **Cite as:**
@InProceedings{Rombach_2022_CVPR,
author = {Rombach, Robin and Blattmann, Andreas and Lorenz, Dominik and Esser, Patrick and Ommer, Bj\"orn},
title = {High-Resolution Image Synthesis With Latent Diffusion Models},
booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
month = {June},
year = {2022},
pages = {10684-10695}
}
# Uses
## Direct Use
The model is intended for research purposes only. Possible research areas and
tasks include
- Safe deployment of models which have the potential to generate harmful content.
- Probing and understanding the limitations and biases of generative models.
- Generation of artworks and use in design and other artistic processes.
- Applications in educational or creative tools.
- Research on generative models.
Excluded uses are described below.
### Misuse, Malicious Use, and Out-of-Scope Use
_Note: This section is taken from the [DALLE-MINI model card](https://huggingface.co/dalle-mini/dalle-mini), but applies in the same way to Stable Diffusion v1_.
The model should not be used to intentionally create or disseminate images that create hostile or alienating environments for people. This includes generating images that people would foreseeably find disturbing, distressing, or offensive; or content that propagates historical or current stereotypes.
#### Out-of-Scope Use
The model was not trained to be factual or true representations of people or events, and therefore using the model to generate such content is out-of-scope for the abilities of this model.
#### Misuse and Malicious Use
Using the model to generate content that is cruel to individuals is a misuse of this model. This includes, but is not limited to:
- Generating demeaning, dehumanizing, or otherwise harmful representations of people or their environments, cultures, religions, etc.
- Intentionally promoting or propagating discriminatory content or harmful stereotypes.
- Impersonating individuals without their consent.
- Sexual content without consent of the people who might see it.
- Mis- and disinformation
- Representations of egregious violence and gore
- Sharing of copyrighted or licensed material in violation of its terms of use.
- Sharing content that is an alteration of copyrighted or licensed material in violation of its terms of use.
## Limitations and Bias
### Limitations
- The model does not achieve perfect photorealism
- The model cannot render legible text
- The model does not perform well on more difficult tasks which involve compositionality, such as rendering an image corresponding to “A red cube on top of a blue sphere”
- Faces and people in general may not be generated properly.
- The model was trained mainly with English captions and will not work as well in other languages.
- The autoencoding part of the model is lossy
- The model was trained on a large-scale dataset
[LAION-5B](https://laion.ai/blog/laion-5b/) which contains adult material
and is not fit for product use without additional safety mechanisms and
considerations.
### Bias
While the capabilities of image generation models are impressive, they can also reinforce or exacerbate social biases.
Stable Diffusion v1 was trained on subsets of [LAION-2B(en)](https://laion.ai/blog/laion-5b/),
which consists of images that are primarily limited to English descriptions.
Texts and images from communities and cultures that use other languages are likely to be insufficiently accounted for.
This affects the overall output of the model, as white and western cultures are often set as the default. Further, the
ability of the model to generate content with non-English prompts is significantly worse than with English-language prompts.
## Training
**Training Data**
The model developers used the following dataset for training the model:
- LAION-2B (en) and subsets thereof (see next section)
**Training Procedure**
Stable Diffusion v1 is a latent diffusion model which combines an autoencoder with a diffusion model that is trained in the latent space of the autoencoder. During training,
- Images are encoded through an encoder, which turns images into latent representations. The autoencoder uses a relative downsampling factor of 8 and maps images of shape H x W x 3 to latents of shape H/f x W/f x 4
- Text prompts are encoded through a ViT-L/14 text-encoder.
- The non-pooled output of the text encoder is fed into the UNet backbone of the latent diffusion model via cross-attention.
- The loss is a reconstruction objective between the noise that was added to the latent and the prediction made by the UNet.
We currently provide three checkpoints, `sd-v1-1.ckpt`, `sd-v1-2.ckpt` and `sd-v1-3.ckpt`,
which were trained as follows,
- `sd-v1-1.ckpt`: 237k steps at resolution `256x256` on [laion2B-en](https://huggingface.co/datasets/laion/laion2B-en).
194k steps at resolution `512x512` on [laion-high-resolution](https://huggingface.co/datasets/laion/laion-high-resolution) (170M examples from LAION-5B with resolution `>= 1024x1024`).
- `sd-v1-2.ckpt`: Resumed from `sd-v1-1.ckpt`.
515k steps at resolution `512x512` on "laion-improved-aesthetics" (a subset of laion2B-en,
filtered to images with an original size `>= 512x512`, estimated aesthetics score `> 5.0`, and an estimated watermark probability `< 0.5`. The watermark estimate is from the LAION-5B metadata, the aesthetics score is estimated using an [improved aesthetics estimator](https://github.com/christophschuhmann/improved-aesthetic-predictor)).
- `sd-v1-3.ckpt`: Resumed from `sd-v1-2.ckpt`. 195k steps at resolution `512x512` on "laion-improved-aesthetics" and 10\% dropping of the text-conditioning to improve [classifier-free guidance sampling](https://arxiv.org/abs/2207.12598).
- **Hardware:** 32 x 8 x A100 GPUs
- **Optimizer:** AdamW
- **Gradient Accumulations**: 2
- **Batch:** 32 x 8 x 2 x 4 = 2048
- **Learning rate:** warmup to 0.0001 for 10,000 steps and then kept constant
## Evaluation Results
Evaluations with different classifier-free guidance scales (1.5, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0) and 50 PLMS sampling
steps show the relative improvements of the checkpoints:
![pareto](assets/v1-variants-scores.jpg)
Evaluated using 50 PLMS steps and 10000 random prompts from the COCO2017 validation set, evaluated at 512x512 resolution. Not optimized for FID scores.
## Environmental Impact
**Stable Diffusion v1** **Estimated Emissions**
Based on that information, we estimate the following CO2 emissions using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700). The hardware, runtime, cloud provider, and compute region were utilized to estimate the carbon impact.
- **Hardware Type:** A100 PCIe 40GB
- **Hours used:** 150000
- **Cloud Provider:** AWS
- **Compute Region:** US-east
- **Carbon Emitted (Power consumption x Time x Carbon produced based on location of power grid):** 11250 kg CO2 eq.
## Citation
@InProceedings{Rombach_2022_CVPR,
author = {Rombach, Robin and Blattmann, Andreas and Lorenz, Dominik and Esser, Patrick and Ommer, Bj\"orn},
title = {High-Resolution Image Synthesis With Latent Diffusion Models},
booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
month = {June},
year = {2022},
pages = {10684-10695}
}
*This model card was written by: Robin Rombach and Patrick Esser and is based on the [DALL-E Mini model card](https://huggingface.co/dalle-mini/dalle-mini).*
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+31
View File
@@ -0,0 +1,31 @@
## Make a copy of this file named `.env` and fill in the values below.
## Any environment variables supported by InvokeAI can be specified here,
## in addition to the examples below.
## INVOKEAI_ROOT is the path *on the host system* where Invoke will store its data.
## It is mounted into the container and allows both containerized and non-containerized usage of Invoke.
# Usually this is the only variable you need to set. It can be relative or absolute.
# INVOKEAI_ROOT=~/invokeai
## HOST_INVOKEAI_ROOT and CONTAINER_INVOKEAI_ROOT can be used to control the on-host
## and in-container paths separately, if needed.
## HOST_INVOKEAI_ROOT is the path on the docker host's filesystem where Invoke will store data.
## If relative, it will be relative to the docker directory in which the docker-compose.yml file is located
## CONTAINER_INVOKEAI_ROOT is the path within the container where Invoke will expect to find the runtime directory.
## It MUST be absolute. There is usually no need to change this.
# HOST_INVOKEAI_ROOT=../../invokeai-data
# CONTAINER_INVOKEAI_ROOT=/invokeai
## INVOKEAI_PORT is the port on which the InvokeAI web interface will be available
# INVOKEAI_PORT=9090
## GPU_DRIVER can be set to either `cuda` or `rocm` to enable GPU support in the container accordingly.
# GPU_DRIVER=cuda #| rocm
## If you are using ROCM, you will need to ensure that the render group within the container and the host system use the same group ID.
## To obtain the group ID of the render group on the host system, run `getent group render` and grab the number.
# RENDER_GROUP_ID=
## CONTAINER_UID can be set to the UID of the user on the host system that should own the files in the container.
## It is usually not necessary to change this. Use `id -u` on the host system to find the UID.
# CONTAINER_UID=1000
+107
View File
@@ -0,0 +1,107 @@
# syntax=docker/dockerfile:1.4
#### Web UI ------------------------------------
FROM docker.io/node:22-slim AS web-builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack use pnpm@10.x && corepack enable
WORKDIR /build
COPY invokeai/frontend/web/ ./
RUN --mount=type=cache,target=/pnpm/store \
pnpm install --frozen-lockfile
RUN npx vite build
## Backend ---------------------------------------
FROM library/ubuntu:24.04
ARG DEBIAN_FRONTEND=noninteractive
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt \
--mount=type=cache,target=/var/lib/apt \
apt update && apt install -y --no-install-recommends \
ca-certificates \
git \
gosu \
libglib2.0-0 \
libgl1 \
libglx-mesa0 \
build-essential \
libopencv-dev \
libstdc++-10-dev
ENV \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
VIRTUAL_ENV=/opt/venv \
INVOKEAI_SRC=/opt/invokeai \
PYTHON_VERSION=3.12 \
UV_PYTHON=3.12 \
UV_COMPILE_BYTECODE=1 \
UV_MANAGED_PYTHON=1 \
UV_LINK_MODE=copy \
UV_PROJECT_ENVIRONMENT=/opt/venv \
INVOKEAI_ROOT=/invokeai \
INVOKEAI_HOST=0.0.0.0 \
INVOKEAI_PORT=9090 \
PATH="/opt/venv/bin:$PATH" \
CONTAINER_UID=${CONTAINER_UID:-1000} \
CONTAINER_GID=${CONTAINER_GID:-1000}
ARG GPU_DRIVER=cuda
# Install `uv` for package management
COPY --from=ghcr.io/astral-sh/uv:0.6.9 /uv /uvx /bin/
# Install python & allow non-root user to use it by traversing the /root dir without read permissions
RUN --mount=type=cache,target=/root/.cache/uv \
uv python install ${PYTHON_VERSION} && \
# chmod --recursive a+rX /root/.local/share/uv/python
chmod 711 /root
WORKDIR ${INVOKEAI_SRC}
# Install project's dependencies as a separate layer so they aren't rebuilt every commit.
# bind-mount instead of copy to defer adding sources to the image until next layer.
#
# NOTE: there are no pytorch builds for arm64 + cuda, only cpu
# x86_64/CUDA is the default
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
--mount=type=bind,source=uv.lock,target=uv.lock \
# this is just to get the package manager to recognize that the project exists, without making changes to the docker layer
--mount=type=bind,source=invokeai/version,target=invokeai/version \
ulimit -n 30000 && \
uv sync --extra $GPU_DRIVER --frozen
# Link amdgpu.ids for ROCm builds
# contributed by https://github.com/Rubonnek
RUN mkdir -p "/opt/amdgpu/share/libdrm" &&\
ln -s "/usr/share/libdrm/amdgpu.ids" "/opt/amdgpu/share/libdrm/amdgpu.ids" && groupadd render
# build patchmatch
RUN cd /usr/lib/$(uname -p)-linux-gnu/pkgconfig/ && ln -sf opencv4.pc opencv.pc
RUN python -c "from patchmatch import patch_match"
RUN mkdir -p ${INVOKEAI_ROOT} && chown -R ${CONTAINER_UID}:${CONTAINER_GID} ${INVOKEAI_ROOT}
COPY docker/docker-entrypoint.sh ./
ENTRYPOINT ["/opt/invokeai/docker-entrypoint.sh"]
CMD ["invokeai-web"]
# --link requires buldkit w/ dockerfile syntax 1.4, does not work with podman
COPY --link --from=web-builder /build/dist ${INVOKEAI_SRC}/invokeai/frontend/web/dist
# add sources last to minimize image changes on code changes
COPY invokeai ${INVOKEAI_SRC}/invokeai
# this should not increase image size because we've already installed dependencies
# in a previous layer
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
--mount=type=bind,source=uv.lock,target=uv.lock \
ulimit -n 30000 && \
uv pip install -e .[$GPU_DRIVER]
+117
View File
@@ -0,0 +1,117 @@
# Invoke in Docker
First things first:
- Ensure that Docker can use your [NVIDIA][nvidia docker docs] or [AMD][amd docker docs] GPU.
- This document assumes a Linux system, but should work similarly under Windows with WSL2.
- We don't recommend running Invoke in Docker on macOS at this time. It works, but very slowly.
## Quickstart
No `docker compose`, no persistence, single command, using the official images:
**CUDA (NVIDIA GPU):**
```bash
docker run --runtime=nvidia --gpus=all --publish 9090:9090 ghcr.io/invoke-ai/invokeai
```
**ROCm (AMD GPU):**
```bash
docker run --device /dev/kfd --device /dev/dri --publish 9090:9090 ghcr.io/invoke-ai/invokeai:main-rocm
```
Open `http://localhost:9090` in your browser once the container finishes booting, install some models, and generate away!
### Data persistence
To persist your generated images and downloaded models outside of the container, add a `--volume/-v` flag to the above command, e.g.:
```bash
docker run --volume /some/local/path:/invokeai {...etc...}
```
`/some/local/path/invokeai` will contain all your data.
It can *usually* be reused between different installs of Invoke. Tread with caution and read the release notes!
## Customize the container
The included `run.sh` script is a convenience wrapper around `docker compose`. It can be helpful for passing additional build arguments to `docker compose`. Alternatively, the familiar `docker compose` commands work just as well.
```bash
cd docker
cp .env.sample .env
# edit .env to your liking if you need to; it is well commented.
./run.sh
```
It will take a few minutes to build the image the first time. Once the application starts up, open `http://localhost:9090` in your browser to invoke!
>[!TIP]
>When using the `run.sh` script, the container will continue running after Ctrl+C. To shut it down, use the `docker compose down` command.
## Docker setup in detail
#### Linux
1. Ensure buildkit is enabled in the Docker daemon settings (`/etc/docker/daemon.json`)
2. Install the `docker compose` plugin using your package manager, or follow a [tutorial](https://docs.docker.com/compose/install/linux/#install-using-the-repository).
- The deprecated `docker-compose` (hyphenated) CLI probably won't work. Update to a recent version.
3. Ensure docker daemon is able to access the GPU.
- [NVIDIA docs](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
- [AMD docs](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html)
#### macOS
> [!TIP]
> You'll be better off installing Invoke directly on your system, because Docker can not use the GPU on macOS.
If you are still reading:
1. Ensure Docker has at least 16GB RAM
2. Enable VirtioFS for file sharing
3. Enable `docker compose` V2 support
This is done via Docker Desktop preferences.
### Configure the Invoke Environment
1. Make a copy of `.env.sample` and name it `.env` (`cp .env.sample .env` (Mac/Linux) or `copy example.env .env` (Windows)). Make changes as necessary. Set `INVOKEAI_ROOT` to an absolute path to the desired location of the InvokeAI runtime directory. It may be an existing directory from a previous installation (post 4.0.0).
1. Execute `run.sh`
The image will be built automatically if needed.
The runtime directory (holding models and outputs) will be created in the location specified by `INVOKEAI_ROOT`. The default location is `~/invokeai`. Navigate to the Model Manager tab and install some models before generating.
### Use a GPU
- Linux is *recommended* for GPU support in Docker.
- WSL2 is *required* for Windows.
- only `x86_64` architecture is supported.
The Docker daemon on the system must be already set up to use the GPU. In case of Linux, this involves installing `nvidia-docker-runtime` and configuring the `nvidia` runtime as default. Steps will be different for AMD. Please see Docker/NVIDIA/AMD documentation for the most up-to-date instructions for using your GPU with Docker.
To use an AMD GPU, set `GPU_DRIVER=rocm` in your `.env` file before running `./run.sh`.
## Customize
Check the `.env.sample` file. It contains some environment variables for running in Docker. Copy it, name it `.env`, and fill it in with your own values. Next time you run `run.sh`, your custom values will be used.
You can also set these values in `docker-compose.yml` directly, but `.env` will help avoid conflicts when code is updated.
Values are optional, but setting `INVOKEAI_ROOT` is highly recommended. The default is `~/invokeai`. Example:
```bash
INVOKEAI_ROOT=/Volumes/WorkDrive/invokeai
HUGGINGFACE_TOKEN=the_actual_token
CONTAINER_UID=1000
GPU_DRIVER=cuda
```
Any environment variables supported by InvokeAI can be set here. See the [Configuration docs](https://invoke.ai/configuration/invokeai-yaml/) for further detail.
---
[nvidia docker docs]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
[amd docker docs]: https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html
+55
View File
@@ -0,0 +1,55 @@
# Copyright (c) 2023 Eugene Brodsky https://github.com/ebr
x-invokeai: &invokeai
image: "ghcr.io/invoke-ai/invokeai:latest"
build:
context: ..
dockerfile: docker/Dockerfile
# Create a .env file in the same directory as this docker-compose.yml file
# and populate it with environment variables. See .env.sample
env_file:
- .env
# variables without a default will automatically inherit from the host environment
environment:
# if set, CONTAINER_INVOKEAI_ROOT will override the Invoke runtime directory location *inside* the container
- INVOKEAI_ROOT=${CONTAINER_INVOKEAI_ROOT:-/invokeai}
- HF_HOME
ports:
- "${INVOKEAI_PORT:-9090}:${INVOKEAI_PORT:-9090}"
volumes:
- type: bind
source: ${HOST_INVOKEAI_ROOT:-${INVOKEAI_ROOT:-~/invokeai}}
target: ${CONTAINER_INVOKEAI_ROOT:-/invokeai}
bind:
create_host_path: true
- ${HF_HOME:-~/.cache/huggingface}:${HF_HOME:-/invokeai/.cache/huggingface}
tty: true
stdin_open: true
services:
invokeai-cuda:
<<: *invokeai
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
invokeai-cpu:
<<: *invokeai
profiles:
- cpu
invokeai-rocm:
<<: *invokeai
environment:
- AMD_VISIBLE_DEVICES=all
- RENDER_GROUP_ID=${RENDER_GROUP_ID}
runtime: amd
profiles:
- rocm
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
set -e -o pipefail
### Container entrypoint
# Runs the CMD as defined by the Dockerfile or passed to `docker run`
# Can be used to configure the runtime dir
# Bypass by using ENTRYPOINT or `--entrypoint`
### Set INVOKEAI_ROOT pointing to a valid runtime directory
# Otherwise configure the runtime dir first.
### Set the CONTAINER_UID envvar to match your user.
# Ensures files created in the container are owned by you:
# docker run --rm -it -v /some/path:/invokeai -e CONTAINER_UID=$(id -u) <this image>
# Default UID: 1000 chosen due to popularity on Linux systems. Possibly 501 on MacOS.
USER_ID=${CONTAINER_UID:-1000}
USER=ubuntu
# if the user does not exist, create it. It is expected to be present on ubuntu >=24.x
_=$(id ${USER} 2>&1) || useradd -u ${USER_ID} ${USER}
# ensure the UID is correct
usermod -u ${USER_ID} ${USER} 1>/dev/null
## ROCM specific configuration
# render group within the container must match the host render group
# otherwise the container will not be able to access the host GPU.
if [[ -v "RENDER_GROUP_ID" ]] && [[ ! -z "${RENDER_GROUP_ID}" ]]; then
# ensure the render group exists
groupmod -g ${RENDER_GROUP_ID} render
usermod -a -G render ${USER}
usermod -a -G video ${USER}
fi
### Set the $PUBLIC_KEY env var to enable SSH access.
# We do not install openssh-server in the image by default to avoid bloat.
# but it is useful to have the full SSH server e.g. on Runpod.
# (use SCP to copy files to/from the image, etc)
if [[ -v "PUBLIC_KEY" ]] && [[ ! -d "${HOME}/.ssh" ]]; then
apt-get update
apt-get install -y openssh-server
pushd "$HOME"
mkdir -p .ssh
echo "${PUBLIC_KEY}" >.ssh/authorized_keys
chmod -R 700 .ssh
popd
service ssh start
fi
mkdir -p "${INVOKEAI_ROOT}"
chown --recursive ${USER} "${INVOKEAI_ROOT}" || true
cd "${INVOKEAI_ROOT}"
export HF_HOME=${HF_HOME:-$INVOKEAI_ROOT/.cache/huggingface}
export MPLCONFIGDIR=${MPLCONFIGDIR:-$INVOKEAI_ROOT/.matplotlib}
# Run the CMD as the Container User (not root).
exec gosu ${USER} "$@"
Executable
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -e -o pipefail
run() {
local scriptdir=$(dirname "${BASH_SOURCE[0]}")
cd "$scriptdir" || exit 1
local build_args=""
local profile=""
# create .env file if it doesn't exist, otherwise docker compose will fail
touch .env
# parse .env file for build args
build_args=$(awk '$1 ~ /=[^$]/ && $0 !~ /^#/ {print "--build-arg " $0 " "}' .env) &&
profile="$(awk -F '=' '/GPU_DRIVER=/ {print $2}' .env)"
# default to 'cuda' profile
[[ -z "$profile" ]] && profile="cuda"
local service_name="invokeai-$profile"
if [[ ! -z "$build_args" ]]; then
printf "%s\n" "docker compose build args:"
printf "%s\n" "$build_args"
fi
docker compose build $build_args $service_name
unset build_args
printf "%s\n" "starting service $service_name"
docker compose --profile "$profile" up -d "$service_name"
docker compose --profile "$profile" logs -f
}
run
+60
View File
@@ -0,0 +1,60 @@
# InvokeAI - A Stable Diffusion Toolkit
Stable Diffusion distribution by InvokeAI: https://github.com/invoke-ai
The Docker image tracks the `main` branch of the InvokeAI project, which means it includes the latest features, but may contain some bugs.
Your working directory is mounted under the `/workspace` path inside the pod. The models are in `/workspace/invokeai/models`, and outputs are in `/workspace/invokeai/outputs`.
> **Only the /workspace directory will persist between pod restarts!**
> **If you _terminate_ (not just _stop_) the pod, the /workspace will be lost.**
## Quickstart
1. Launch a pod from this template. **It will take about 5-10 minutes to run through the initial setup**. Be patient.
1. Wait for the application to load.
- TIP: you know it's ready when the CPU usage goes idle
- You can also check the logs for a line that says "_Point your browser at..._"
1. Open the Invoke AI web UI: click the `Connect` => `connect over HTTP` button.
1. Generate some art!
## Other things you can do
At any point you may edit the pod configuration and set an arbitrary Docker command. For example, you could run a command to downloads some models using `curl`, or fetch some images and place them into your outputs to continue a working session.
If you need to run *multiple commands*, define them in the Docker Command field like this:
`bash -c "cd ${INVOKEAI_ROOT}/outputs; wormhole receive 2-foo-bar; invoke.py --web --host 0.0.0.0"`
### Copying your data in and out of the pod
This image includes a couple of handy tools to help you get the data into the pod (such as your custom models or embeddings), and out of the pod (such as downloading your outputs). Here are your options for getting your data in and out of the pod:
- **SSH server**:
1. Make sure to create and set your Public Key in the RunPod settings (follow the official instructions)
1. Add an exposed port 22 (TCP) in the pod settings!
1. When your pod restarts, you will see a new entry in the `Connect` dialog. Use this SSH server to `scp` or `sftp` your files as necessary, or SSH into the pod using the fully fledged SSH server.
- [**Magic Wormhole**](https://magic-wormhole.readthedocs.io/en/latest/welcome.html):
1. On your computer, `pip install magic-wormhole` (see above instructions for details)
1. Connect to the command line **using the "light" SSH client** or the browser-based console. _Currently there's a bug where `wormhole` isn't available when connected to "full" SSH server, as described above_.
1. `wormhole send /workspace/invokeai/outputs` will send the entire `outputs` directory. You can also send individual files.
1. Once packaged, you will see a `wormhole receive <123-some-words>` command. Copy it
1. Paste this command into the terminal on your local machine to securely download the payload.
1. It works the same in reverse: you can `wormhole send` some models from your computer to the pod. Again, save your files somewhere in `/workspace` or they will be lost when the pod is stopped.
- **RunPod's Cloud Sync feature** may be used to sync the persistent volume to cloud storage. You could, for example, copy the entire `/workspace` to S3, add some custom models to it, and copy it back from S3 when launching new pod configurations. Follow the Cloud Sync instructions.
### Disable the NSFW checker
The NSFW checker is enabled by default. To disable it, edit the pod configuration and set the following command:
```
invoke --web --host 0.0.0.0 --no-nsfw_checker
```
---
Template ©2023 Eugene Brodsky [ebr](https://github.com/ebr)
+21
View File
@@ -0,0 +1,21 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
+1
View File
@@ -0,0 +1 @@
# Invoke AI Documentation
+92
View File
@@ -0,0 +1,92 @@
// @ts-check
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
// Plugins
import starlightLinksValidator from 'starlight-links-validator';
import starlightLlmsText from 'starlight-llms-txt';
import starlightChangelogs from 'starlight-changelogs';
import { rehypePrefixBaseToRootLinks } from './plugins/rehype-prefix-base-to-root-links.mjs';
import starlightContextualMenu from 'starlight-contextual-menu';
// Configs
import {
createHeadConfig,
createRedirects,
sidebarConfig,
socialConfig,
} from './src/config';
// Deployment target: 'custom' (default, custom domain at invoke.ai) or 'ghpages'
// (GitHub Pages project URL at invoke-ai.github.io/InvokeAI). Drive site/base from this
// so the same source can be deployed to either target.
const deployTarget = process.env.DEPLOY_TARGET ?? 'custom';
const isGhPages = deployTarget === 'ghpages';
const enableAnalytics = process.env.ENABLE_ANALYTICS === 'true';
const base = isGhPages ? '/InvokeAI' : '';
const site = isGhPages ? 'https://invoke-ai.github.io' : 'https://invoke.ai';
const redirects = createRedirects(base);
const head = createHeadConfig({ base, enableAnalytics, isGhPages, site });
// https://astro.build/config
export default defineConfig({
site,
base: base || undefined,
markdown: {
rehypePlugins: [[rehypePrefixBaseToRootLinks, { base }]],
},
integrations: [
starlight({
// Content
title: {
en: 'InvokeAI Documentation',
},
logo: {
src: './src/assets/invoke-icon-wide.svg',
alt: 'InvokeAI Logo',
replacesTitle: true,
},
favicon: 'favicon.svg',
editLink: {
baseUrl: 'https://github.com/invoke-ai/InvokeAI/edit/main/docs',
},
head,
defaultLocale: 'root',
locales: {
root: {
label: 'English',
lang: 'en',
},
},
social: socialConfig,
tableOfContents: {
maxHeadingLevel: 4,
},
customCss: [
'@fontsource-variable/inter',
'@fontsource-variable/roboto-mono',
'./src/styles/custom.css',
],
sidebar: sidebarConfig,
components: {
ThemeProvider: './src/lib/components/ForceDarkTheme.astro',
ThemeSelect: './src/lib/components/EmptyComponent.astro',
Footer: './src/lib/components/Footer.astro',
PageFrame: './src/layouts/PageFrameExtended.astro',
},
plugins: [
starlightLinksValidator({
errorOnRelativeLinks: false,
errorOnLocalLinks: false,
}),
starlightLlmsText(),
starlightChangelogs(),
starlightContextualMenu({
actions: ['copy', 'view', 'chatgpt', 'claude'],
}),
],
}),
],
redirects,
});
View File
+34
View File
@@ -0,0 +1,34 @@
{
"name": "docs",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"generate-docs-data": "uv run python ../scripts/generate_docs_json.py",
"check-docs-data": "pnpm run generate-docs-data && git diff --exit-code -- src/generated",
"check-deploy-output": "node ./scripts/verify-deploy-output.mjs",
"check-redirects": "node ./scripts/validate-redirect-targets.mjs",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/starlight": "^0.39.2",
"@fontsource-variable/inter": "^5.2.8",
"@fontsource-variable/roboto-mono": "^5.2.9",
"astro": "^6.3.7",
"mermaid": "^11.15.0",
"rehype-external-links": "^3.0.0",
"sharp": "^0.34.5",
"starlight-changelogs": "^0.5.0",
"starlight-contextual-menu": "^0.1.5",
"starlight-links-validator": "^0.24.0",
"starlight-llms-txt": "^0.10.0"
},
"devDependencies": {
"node-addon-api": "^8.8.0",
"node-gyp": "^12.3.0"
},
"packageManager": "pnpm@10.12.4"
}
@@ -0,0 +1,53 @@
export function rehypePrefixBaseToRootLinks(options = {}) {
const base = normalizeBase(options.base);
return (tree) => {
if (!base) {
return;
}
walk(tree, (node) => {
if (node.tagName !== 'a') {
return;
}
const href = node.properties?.href;
if (typeof href !== 'string') {
return;
}
if (!href.startsWith('/') || href.startsWith('//') || href.startsWith(`${base}/`)) {
return;
}
node.properties.href = `${base}${href}`;
});
};
}
function walk(node, visitor) {
if (!node || typeof node !== 'object') {
return;
}
if (node.type === 'element') {
visitor(node);
}
if (!Array.isArray(node.children)) {
return;
}
for (const child of node.children) {
walk(child, visitor);
}
}
function normalizeBase(base) {
if (!base || base === '/') {
return '';
}
return base.endsWith('/') ? base.slice(0, -1) : base;
}
+5443
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
onlyBuiltDependencies:
- esbuild
- sharp
+1
View File
@@ -0,0 +1 @@
invoke.ai
Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_98_2)">
<rect width="512" height="512" rx="54" fill="#E6FD13"/>
<path d="M313.561 165.334H416V96H96V165.334H198.439L313.561 346.666H416V416H96V346.666H198.439" stroke="black" stroke-width="31"/>
</g>
<defs>
<clipPath id="clip0_98_2">
<rect width="512" height="512" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 432 B

@@ -0,0 +1,65 @@
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import { dirname, join, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
const docsRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
const contentRoot = join(docsRoot, 'src', 'content', 'docs');
const redirectsFile = join(docsRoot, 'src', 'config', 'redirects.ts');
const normalizeRoute = (route) => {
const normalized = route
.replace(/^\/+|\/+$/g, '')
.split('/')
.filter(Boolean)
.map((segment) => segment.toLowerCase().replaceAll(' ', '-'))
.join('/');
return normalized ? `/${normalized}` : '/';
};
const collectDocsRoutes = (dir, routes = new Set()) => {
for (const entry of readdirSync(dir)) {
const entryPath = join(dir, entry);
const stats = statSync(entryPath);
if (stats.isDirectory()) {
collectDocsRoutes(entryPath, routes);
continue;
}
if (!entry.endsWith('.md') && !entry.endsWith('.mdx')) {
continue;
}
const relativePath = relative(contentRoot, entryPath).replace(/\\/g, '/').replace(/\.mdx?$/, '');
const route = relativePath.endsWith('/index') ? relativePath.slice(0, -'/index'.length) : relativePath;
routes.add(normalizeRoute(route));
const segments = route.split('/').filter(Boolean);
for (let index = 1; index < segments.length; index++) {
routes.add(normalizeRoute(segments.slice(0, index).join('/')));
}
}
return routes;
};
if (!existsSync(contentRoot)) {
throw new Error(`Docs content directory not found: ${contentRoot}`);
}
const redirectsSource = readFileSync(redirectsFile, 'utf8');
const redirectMatches = redirectsSource.matchAll(/^\s*['"]([^'"]+)['"]:\s*['"]([^'"]+)['"]/gm);
const redirectTargets = Array.from(redirectMatches, ([, from, to]) => ({ from, to }));
const docsRoutes = collectDocsRoutes(contentRoot);
const missingTargets = redirectTargets.filter(({ to }) => !docsRoutes.has(normalizeRoute(to)));
if (missingTargets.length > 0) {
console.error('Redirect targets must resolve to generated docs routes:');
for (const { from, to } of missingTargets) {
console.error(` ${from} -> ${to}`);
}
process.exit(1);
}
console.log(`Validated ${redirectTargets.length} redirect targets.`);
+70
View File
@@ -0,0 +1,70 @@
import { readFileSync } from 'node:fs';
const deployTarget = process.env.DEPLOY_TARGET ?? 'custom';
const base = deployTarget === 'ghpages' ? '/InvokeAI' : '';
const withBase = (path) => `${base}${path}`;
const expectations = [
{
file: 'index.html',
includes: [
`href="${withBase('/_astro/')}`,
`src="${withBase('/_astro/')}`,
`href="${withBase('/start-here/installation/')}`,
],
excludes: deployTarget === 'custom' ? ['href="/InvokeAI/', 'src="/InvokeAI/'] : ['href="/_astro/', 'src="/_astro/'],
},
{
file: 'contributing/index.html',
includes: [`href="${withBase('/contributing/new-contributor-guide/')}`],
excludes: [
deployTarget === 'custom'
? 'href="/InvokeAI/contributing/new-contributor-guide/"'
: 'href="/contributing/new-contributor-guide/"',
'newContributorChecklist.md',
],
},
{
file: 'contributing/contribution_guides/newContributorChecklist/index.html',
includes: [
`Redirecting to: ${withBase('/contributing/new-contributor-guide')}`,
`content="0;url=${withBase('/contributing/new-contributor-guide')}`,
`href="${withBase('/contributing/new-contributor-guide')}`,
],
excludes: deployTarget === 'custom'
? [
'Redirecting to: /InvokeAI/contributing/new-contributor-guide',
'content="0;url=/InvokeAI/contributing/new-contributor-guide',
'href="/InvokeAI/contributing/new-contributor-guide',
]
: [
'Redirecting to: /contributing/new-contributor-guide',
'content="0;url=/contributing/new-contributor-guide',
'href="/contributing/new-contributor-guide',
],
},
];
const errors = [];
for (const { file, includes = [], excludes = [] } of expectations) {
const html = readFileSync(new URL(`../dist/${file}`, import.meta.url), 'utf8');
for (const expected of includes) {
if (!html.includes(expected)) {
errors.push(`${file} is missing ${expected}`);
}
}
for (const unexpected of excludes) {
if (html.includes(unexpected)) {
errors.push(`${file} still contains ${unexpected}`);
}
}
}
if (errors.length > 0) {
throw new Error(`${deployTarget} output validation failed:\n- ${errors.join('\n- ')}`);
}
console.log(`${deployTarget} output links and assets look correct.`);
Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

+12
View File
@@ -0,0 +1,12 @@
<svg width="75" height="32" viewBox="0 0 75 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="75" height="32" rx="2" fill="#E6FD13"/>
<g clip-path="url(#clip0_4_5)">
<path d="M18.6457 11.8788H23.3021V8.72727H8.75662V11.8788H13.413L18.6457 20.1212H23.3021V23.2727H8.75662V20.1212H13.413" stroke="black" stroke-width="1.21212"/>
</g>
<path d="M30.9033 12.3906H35.0928V13.3623H33.5742V18.5332H35.0928V19.5H30.9033V18.5332H32.3877V13.3623H30.9033V12.3906ZM36.7627 19.5V14.2168H37.8174L37.8906 14.9688C37.9622 14.8678 38.0404 14.7751 38.125 14.6904C38.2129 14.6025 38.3057 14.5244 38.4033 14.4561C38.5596 14.3486 38.7305 14.2656 38.916 14.207C39.1016 14.1484 39.2969 14.1191 39.502 14.1191C39.7721 14.1191 40.0195 14.1582 40.2441 14.2363C40.4688 14.3145 40.6608 14.4382 40.8203 14.6074C40.9798 14.7767 41.1035 14.9932 41.1914 15.2568C41.2793 15.5173 41.3232 15.833 41.3232 16.2041V19.5H40.1562V16.2236C40.1562 16.0055 40.1318 15.8232 40.083 15.6768C40.0342 15.5303 39.9626 15.4131 39.8682 15.3252C39.7738 15.2373 39.6598 15.1755 39.5264 15.1396C39.3929 15.1006 39.2399 15.0811 39.0674 15.0811C38.9242 15.0811 38.7907 15.1006 38.667 15.1396C38.5433 15.1755 38.431 15.2275 38.3301 15.2959C38.252 15.348 38.1787 15.4115 38.1104 15.4863C38.042 15.5612 37.9818 15.6426 37.9297 15.7305V19.5H36.7627ZM44.4971 19.5L42.417 14.2168H43.6279L44.9365 18.0498L45.0146 18.4014L45.0928 18.0498L46.3867 14.2168H47.5977L45.5273 19.5H44.4971ZM48.5596 16.8096C48.5596 16.4255 48.6149 16.0706 48.7256 15.7451C48.8363 15.4163 48.9974 15.1315 49.209 14.8906C49.4173 14.6497 49.6729 14.4609 49.9756 14.3242C50.2783 14.1875 50.6234 14.1191 51.0107 14.1191C51.3981 14.1191 51.7432 14.1875 52.0459 14.3242C52.3519 14.4609 52.6107 14.6497 52.8223 14.8906C53.0306 15.1315 53.1901 15.4163 53.3008 15.7451C53.4115 16.0706 53.4668 16.4255 53.4668 16.8096V16.9121C53.4668 17.2995 53.4115 17.6559 53.3008 17.9814C53.1901 18.307 53.0306 18.5902 52.8223 18.8311C52.6139 19.0719 52.3568 19.2607 52.0508 19.3975C51.748 19.5342 51.4046 19.6025 51.0205 19.6025C50.6331 19.6025 50.2865 19.5342 49.9805 19.3975C49.6745 19.2607 49.4173 19.0719 49.209 18.8311C48.9974 18.5902 48.8363 18.307 48.7256 17.9814C48.6149 17.6559 48.5596 17.2995 48.5596 16.9121V16.8096ZM49.7266 16.9121C49.7266 17.1497 49.751 17.3743 49.7998 17.5859C49.8519 17.7975 49.9316 17.9831 50.0391 18.1426C50.1432 18.3021 50.2767 18.429 50.4395 18.5234C50.6022 18.6146 50.7959 18.6602 51.0205 18.6602C51.2386 18.6602 51.429 18.6146 51.5918 18.5234C51.7546 18.429 51.888 18.3021 51.9922 18.1426C52.0964 17.9831 52.1729 17.7975 52.2217 17.5859C52.2738 17.3743 52.2998 17.1497 52.2998 16.9121V16.8096C52.2998 16.5785 52.2738 16.3571 52.2217 16.1455C52.1696 15.9339 52.0931 15.7484 51.9922 15.5889C51.8848 15.4294 51.7497 15.3024 51.5869 15.208C51.4274 15.1136 51.2354 15.0664 51.0107 15.0664C50.7894 15.0664 50.5973 15.1136 50.4346 15.208C50.2751 15.3024 50.1432 15.4294 50.0391 15.5889C49.9316 15.7484 49.8519 15.9339 49.7998 16.1455C49.751 16.3571 49.7266 16.5785 49.7266 16.8096V16.9121ZM56.5967 17.2002L55.9619 17.8008V19.5H54.79V12H55.9619V16.4141L56.4453 15.877L58.0225 14.2168H59.4287L57.373 16.4189L59.7266 19.5H58.2812L56.5967 17.2002ZM63.252 19.5977C62.8613 19.5977 62.5033 19.5326 62.1777 19.4023C61.8555 19.2721 61.5788 19.0915 61.3477 18.8604C61.1165 18.6325 60.9375 18.3639 60.8105 18.0547C60.6868 17.7422 60.625 17.4053 60.625 17.0439V16.8438C60.625 16.4303 60.6901 16.056 60.8203 15.7207C60.9505 15.3854 61.1296 15.099 61.3574 14.8613C61.5853 14.6237 61.849 14.4414 62.1484 14.3145C62.4512 14.1842 62.7734 14.1191 63.1152 14.1191C63.4961 14.1191 63.833 14.1842 64.126 14.3145C64.4189 14.4414 64.6647 14.6188 64.8633 14.8467C65.0618 15.0778 65.2116 15.3529 65.3125 15.6719C65.4134 15.9909 65.4639 16.3392 65.4639 16.7168V17.2197H61.8018V17.2441C61.8376 17.4753 61.8929 17.6689 61.9678 17.8252C62.0426 17.9814 62.1452 18.1214 62.2754 18.2451C62.4056 18.3753 62.5586 18.4762 62.7344 18.5479C62.9134 18.6195 63.1087 18.6553 63.3203 18.6553C63.61 18.6553 63.8802 18.5999 64.1309 18.4893C64.3815 18.3753 64.5882 18.2142 64.751 18.0059L65.376 18.6113C65.2002 18.8652 64.93 19.0931 64.5654 19.2949C64.2041 19.4967 63.7663 19.5977 63.252 19.5977ZM63.1104 15.0664C62.9443 15.0664 62.7897 15.0973 62.6465 15.1592C62.5065 15.2178 62.3812 15.3024 62.2705 15.4131C62.1598 15.527 62.0671 15.6637 61.9922 15.8232C61.9173 15.9827 61.862 16.1634 61.8262 16.3652H64.3115V16.2871C64.3115 16.1341 64.2839 15.9827 64.2285 15.833C64.1732 15.68 64.0951 15.5465 63.9941 15.4326C63.8965 15.3219 63.7728 15.234 63.623 15.1689C63.4766 15.1006 63.3057 15.0664 63.1104 15.0664Z" fill="black"/>
<defs>
<clipPath id="clip0_4_5">
<rect width="16" height="16" fill="white" transform="translate(8 8)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="66" height="66" viewBox="0 0 66 66" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M43.9137 16H63.1211V3H3.12109V16H22.3285L43.9137 50H63.1211V63H3.12109V50H22.3285" stroke="white" stroke-width="5"/>
</svg>

After

Width:  |  Height:  |  Size: 229 B

+79
View File
@@ -0,0 +1,79 @@
import type { StarlightUserConfig } from '@astrojs/starlight/types';
type HeadConfig = NonNullable<StarlightUserConfig['head']>;
type CreateHeadConfigParams = {
base: string;
enableAnalytics: boolean;
isGhPages: boolean;
site: string;
};
const plausibleScriptUrl =
'https://plausible.tracking.events/js/pa-BHcumuOemKz4XIQeWkTn4.js';
const plausibleInitScript =
'window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};plausible.init()';
function createHeadConfig({
base,
enableAnalytics,
isGhPages,
site,
}: CreateHeadConfigParams): HeadConfig {
const coverImageUrl = new URL(`${base}/coverimage.png`, site).toString();
return [
{
tag: 'meta',
attrs: {
property: 'og:image',
content: coverImageUrl,
},
},
{
tag: 'meta',
attrs: {
property: 'og:image:width',
content: '1200',
},
},
{
tag: 'meta',
attrs: {
property: 'og:image:height',
content: '630',
},
},
{
tag: 'meta',
attrs: {
name: 'twitter:card',
content: 'summary_large_image',
},
},
{
tag: 'meta',
attrs: {
name: 'twitter:image',
content: coverImageUrl,
},
},
...(enableAnalytics && !isGhPages
? ([
{
tag: 'script',
attrs: {
async: true,
src: plausibleScriptUrl,
},
},
{
tag: 'script',
content: plausibleInitScript,
},
] satisfies HeadConfig)
: []),
] satisfies HeadConfig;
}
export { createHeadConfig };
+4
View File
@@ -0,0 +1,4 @@
export * from './head';
export * from './redirects';
export * from './sidebar';
export * from './social';
+55
View File
@@ -0,0 +1,55 @@
import type { AstroConfig } from 'astro';
type RedirectsConfig = AstroConfig['redirects'];
const redirects: RedirectsConfig = {
'/CODE_OF_CONDUCT': '/contributing/code-of-conduct',
'/RELEASE': '/development/process/release-process',
'/installation': '/start-here/installation',
'/installation/docker': '/configuration/docker',
'/installation/manual': '/start-here/manual',
'/installation/models': '/concepts/models',
'/installation/patchmatch': '/configuration/patchmatch',
'/installation/quick_start': '/start-here/installation',
'/installation/requirements': '/start-here/system-requirements',
'/configuration': '/configuration/invokeai-yaml',
'/features/low-vram/': '/configuration/low-vram-mode/',
'/features/lasso-tool': '/features/canvas/lasso-tool',
'/features/shapes-tool': '/features/canvas/shapes-tool',
'/faq': '/troubleshooting/faq',
'/help/SAMPLER_CONVERGENCE': '/concepts/parameters',
'/help/diffusion': '/concepts/diffusion',
'/help/gettingStartedWithAI': '/concepts/image-generation',
'/nodes/NODES': '/features/workflows/editor-interface',
'/nodes/NODES_MIGRATION_V3_V4': '/development/guides/api-development',
'/nodes/comfyToInvoke': '/features/workflows/comfyui-migration',
'/nodes/communityNodes': '/features/workflows/community-nodes',
'/nodes/contributingNodes': '/development/guides/creating-nodes',
'/nodes/detailedNodes/faceTools': '/features/workflows/face-tools',
'/nodes/invocation-api': '/development/guides/api-development',
'/contributing/ARCHITECTURE': '/development/architecture/overview',
'/contributing/DOWNLOAD_QUEUE': '/development/architecture/model-manager',
'/contributing/HOTKEYS': '/features/hotkeys',
'/contributing/INVOCATIONS': '/development/architecture/invocations',
'/contributing/LOCAL_DEVELOPMENT': '/development/setup/dev-environment',
'/contributing/MODEL_MANAGER': '/development/architecture/model-manager',
'/contributing/NEW_MODEL_INTEGRATION': '/development/guides/models',
'/contributing/PR-MERGE-POLICY': '/development/process/pr-merge-policy',
'/contributing/TESTS': '/development/guides/tests',
'/contributing/contribution_guides/development': '/development',
'/contributing/contribution_guides/newContributorChecklist':
'/contributing/new-contributor-guide',
'/contributing/dev-environment': '/development/setup/dev-environment',
'/contributing/frontend': '/development/front-end',
'/contributing/frontend/state-management':
'/development/front-end/state-management',
'/contributing/frontend/workflows': '/development/front-end/workflows',
};
function createRedirects(base: string): RedirectsConfig {
return Object.fromEntries(
Object.entries(redirects).map(([from, to]) => [from, base + to]),
);
}
export { createRedirects };
+80
View File
@@ -0,0 +1,80 @@
import type { StarlightUserConfig } from '@astrojs/starlight/types';
import { makeChangelogsSidebarLinks } from 'starlight-changelogs';
type SidebarConfig = StarlightUserConfig['sidebar'];
const sidebar: SidebarConfig = [
{
label: 'Start Here',
items: [
{
autogenerate: { directory: 'start-here' },
},
],
},
{
label: 'Configuration',
items: [
{
autogenerate: { directory: 'configuration' },
},
],
},
{
label: 'Concepts',
items: [
{
autogenerate: { directory: 'concepts' },
},
],
},
{
label: 'Features',
items: [
{
autogenerate: { directory: 'features' },
},
],
},
{
label: 'Development',
items: [
{
autogenerate: { directory: 'development', collapsed: true },
},
],
collapsed: true,
},
{
label: 'Contributing',
items: [
{
autogenerate: { directory: 'contributing' },
},
],
collapsed: true,
},
{
label: 'Troubleshooting & Help',
items: [
{
autogenerate: { directory: 'troubleshooting' },
},
],
collapsed: true,
},
{
label: 'Releases',
collapsed: true,
items: [
...makeChangelogsSidebarLinks([
{
type: 'recent',
base: 'releases',
},
]),
],
},
];
export { sidebar as sidebarConfig };
+23
View File
@@ -0,0 +1,23 @@
import type { StarlightUserConfig } from '@astrojs/starlight/types';
type SocialConfig = StarlightUserConfig['social'];
const social: SocialConfig = [
{
icon: 'github',
label: 'GitHub',
href: 'https://github.com/invoke-ai/InvokeAI',
},
{
icon: 'discord',
label: 'Discord',
href: 'https://discord.gg/ZmtBAhwWhy',
},
{
icon: 'youtube',
label: 'YouTube',
href: 'https://www.youtube.com/@invokeai',
},
];
export { social as socialConfig };
+28
View File
@@ -0,0 +1,28 @@
import { defineCollection } from 'astro:content';
import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders';
import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
import { changelogsLoader } from 'starlight-changelogs/loader';
export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }),
changelogs: defineCollection({
loader: changelogsLoader([
{
title: "Releases",
provider: 'github',
base: 'releases',
owner: 'invoke-ai',
repo: 'InvokeAI',
pagefind: false,
// Authenticate GitHub API requests so the release changelog loader uses
// the 5000 req/hr authenticated rate limit instead of the 60 req/hr
// unauthenticated limit (shared per CI runner IP), which causes
// intermittent "403 - rate limit exceeded" build failures. The token is
// optional, so local builds without it fall back to unauthenticated.
token: process.env.GITHUB_TOKEN,
}
]),
})
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,77 @@
---
title: Diffusion
lastUpdated: 2026-02-20
sidebar:
order: 5
---
import { Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
Taking the time to understand the diffusion process will help you to understand how to more effectively use InvokeAI.
## Image Space vs. Latent Space
There are two main ways Stable Diffusion works — with images, and latents.
<CardGrid>
<Card title="Image Space" icon="seti:image">
Represents images in pixel form that you look at. This is the final visual output you see.
</Card>
<Card title="Latent Space" icon="puzzle">
Represents compressed inputs. It's in latent space that Stable Diffusion processes images.
</Card>
</CardGrid>
:::note[What is a VAE?]
A **VAE (Variational Auto Encoder)** is responsible for compressing and encoding inputs into *latent space*, as well as decoding outputs back into *image space*.
:::
## Core Components
To fully understand the diffusion process, we need to understand a few more terms: **U-Net**, **CLIP**, and **conditioning**.
<CardGrid>
<Card title="U-Net" icon="setting">
A model trained on a large number of latent images with known amounts of random noise added. The U-Net can be given a slightly noisy image and it will predict the pattern of noise needed to subtract from the image in order to recover the original.
</Card>
<Card title="CLIP & Conditioning" icon="document">
**CLIP** is a model that tokenizes and encodes text into **conditioning**. This conditioning guides the model during the denoising steps to produce a new image.
</Card>
</CardGrid>
The U-Net and CLIP work together during the image generation process at each denoising step. The U-Net removes noise so that the result is similar to images in its training set, while CLIP guides the U-Net towards creating images that are most similar to your prompt.
## The Generation Process
<Tabs>
<TabItem label="Text-to-Image" icon="seti:default">
When you generate an image using text-to-image, multiple steps occur in latent space:
<Steps>
1. **Noise Generation:** Random noise is generated at the chosen height and width. The noise's characteristics are dictated by the seed. This noise tensor is passed into latent space. We'll call this *noise A*.
2. **Noise Prediction:** Using a model's U-Net, a noise predictor examines *noise A* and the words tokenized by CLIP from your prompt (conditioning). It generates its own noise tensor to predict what the final image might look like in latent space. We'll call this *noise B*.
3. **Subtraction:** *Noise B* is subtracted from *noise A* in an attempt to create a latent image consistent with the prompt. This step is repeated for the number of sampler steps chosen.
4. **Decoding:** The VAE decodes the final latent image from latent space into image space.
</Steps>
</TabItem>
<TabItem label="Image-to-Image" icon="seti:image">
Image-to-image is a similar process, with only the first step being different:
<Steps>
1. **Encoding & Adding Noise:** The input image is encoded from image space into latent space by the VAE. Noise is then added to the input latent image.
* **Denoising Strength** dictates how many noise steps are added, and the amount of noise added at each step.
* A strength of `0` means there are 0 steps and no noise added, resulting in an unchanged image.
* A strength of `1` results in the image being completely replaced with noise and a full set of denoising steps are performed.
2. **Noise Prediction:** Using a model's U-Net, a noise predictor examines the noisy latent image and the conditioning from your prompt. It generates its own noise tensor to predict the final image.
3. **Subtraction:** The predicted noise is subtracted from the current noise in an attempt to create a latent image consistent with the prompt. This step is repeated for the remaining sampler steps.
4. **Decoding:** The VAE decodes the final latent image from latent space into image space.
</Steps>
</TabItem>
</Tabs>
## Summary
<Card title="Putting it all together" icon="star">
- A **Model** provides the CLIP prompt tokenizer, the VAE, and a U-Net (where noise prediction occurs given a prompt and initial noise tensor).
- A **Noise Scheduler** (e.g. `DPM++ 2M Karras`) schedules the subtraction of noise from the latent image across the sampler steps chosen. Less noise is usually subtracted at higher sampler steps.
</Card>
@@ -0,0 +1,133 @@
---
title: Dynamic Prompting
lastUpdated: 2026-03-30
sidebar:
order: 4
---
import { Card, CardGrid, Steps, LinkCard } from '@astrojs/starlight/components';
Dynamic prompting expands a single prompt into many prompt variations. It is useful for brainstorming, prompt exploration, and batch testing without rewriting the same prompt by hand.
## Basic syntax
Put alternatives inside braces and separate them with `|`.
```text
a {red|green|blue} balloon
```
This can expand into:
```text
a red balloon
a green balloon
a blue balloon
```
You can use more than one dynamic group in the same prompt:
```text
a {red|green} {balloon|kite}
```
That creates a set of prompt combinations such as `a red balloon`, `a red kite`, `a green balloon`, and `a green kite`.
## Select more than one option with `$$`
Prefix a group with a number and `$$` to choose multiple distinct options from the same set.
```text
portrait, {2$$rim light|fog|rain|neon reflections}
```
Possible results include:
```text
portrait, rim light, fog
portrait, fog, rain
portrait, rim light, neon reflections
```
This is useful when you want controlled variety without writing every combination by hand.
## Random vs combinatorial expansion
<CardGrid>
<Card title="Combinatorial" icon="setting">
Walks the possible prompt combinations systematically until `Max Prompts` is reached.
</Card>
<Card title="Random" icon="star">
Samples prompt variations instead of enumerating every combination. A seed can make random expansion repeatable.
</Card>
</CardGrid>
InvokeAI supports both modes, but where you can choose them depends on the workflow.
- In the current linear UI, dynamic prompt preview is driven from the positive prompt and currently follows the standard combinatorial expansion path.
- In node and backend contexts, random and combinatorial generation are exposed more explicitly.
## Max Prompts
`Max Prompts` limits how many expanded prompts InvokeAI will generate.
This matters because combinations grow quickly. For example:
```text
a {red|green|blue} balloon in {morning mist|golden hour|rain}
```
Even this small prompt already has nine possible combinations.
:::tip[Start small]
Preview a handful of prompt variants first. Once the combinations look useful, increase `Max Prompts` for a larger batch.
:::
## Seed Behaviour
In the current UI, the `Seed Behaviour` setting controls how seeds are reused across expanded prompts.
<CardGrid>
<Card title="Seed per Iteration" icon="seti:image">
Uses one seed per iteration, so prompt variants in the same iteration share a seed. This is useful when you want to compare prompt wording more directly.
</Card>
<Card title="Seed per Image" icon="star">
Uses a different seed for every generated image. This is useful when you want the widest possible variety.
</Card>
</CardGrid>
## Using dynamic prompting in the linear UI
<Steps>
1. **Put dynamic prompt syntax in the positive prompt**
In the current linear UI, dynamic prompt expansion is driven from the positive prompt.
2. **Open the preview**
Use `Show Dynamic Prompts` or the prompts preview to inspect the expanded list before you generate.
3. **Set `Max Prompts`**
Keep the expansion under control before launching a large batch.
4. **Choose the right seed behavior**
Use `Seed per Iteration` for easier comparison, or `Seed per Image` for more variety.
5. **Generate a small batch first**
Sanity-check the combinations before scaling up.
</Steps>
:::note[Current linear UI behavior]
The linear UI currently exposes `Max Prompts`, preview, and seed behavior. It does not expose a separate random-versus-combinatorial mode switch in the main positive prompt flow.
:::
## Tips
- Keep each option group internally compatible.
- Be careful with multiple groups, because the number of combinations grows quickly.
- Review the expanded prompt list before launching a large batch.
- Use dynamic prompting for variation, not to avoid thinking through the base prompt.
- When one specific term needs more emphasis, use [Prompting Syntax](../prompt-syntax) instead of adding more dynamic groups.
@@ -0,0 +1,153 @@
---
title: Image Generation
lastUpdated: 2026-03-30
sidebar:
order: 1
---
import { Card, CardGrid, Steps, LinkCard } from '@astrojs/starlight/components';
:::tip[New to image generation with AI?]
You're in the right place! This is a high-level walkthrough of some of the concepts and terms you'll see as you start using Invoke. Please note, this is not an exhaustive guide and may be out of date due to the rapidly changing nature of the space.
:::
## Using InvokeAI
### Prompt Crafting
Prompts are the basis of using InvokeAI, providing the models directions on what to generate. As a general rule of thumb, the more detailed your prompt is, the better your result will be.
<Card title="Prompt Structuring Template" icon="pencil">
To get started, here's an easy template to use for structuring your prompts:
**Subject, Style, Quality, Aesthetic**
- **Subject:** What your image will be about. E.g. “a futuristic city with trains”, “penguins floating on icebergs”, “friends sharing beers”.
- **Style:** The style or medium in which your image will be in. E.g. “photograph”, “pencil sketch”, “oil paints”, or “pop art”, “cubism”, “abstract”.
- **Quality:** A particular aspect or trait that you would like to see emphasized in your image. E.g. "award-winning", "featured in relevant set of high quality works", "professionally acclaimed". Many people often use "masterpiece".
- **Aesthetics:** The visual impact and design of the artwork. This can be colors, mood, lighting, setting, etc.
</Card>
There are two prompt boxes: **Positive Prompt** & **Negative Prompt**.
- A **Positive Prompt** includes words you want the model to reference when creating an image.
- A **Negative Prompt** is for anything you want the model to eliminate when creating an image. It doesnt always interpret things exactly the way you would, but helps control the generation process. Always try to include a few terms - you can typically use lower quality image terms like “blurry” or “distorted” with good success.
**Some example prompts you can try on your own:**
- *A detailed oil painting of a tranquil forest at sunset with vibrant colors and soft, golden light filtering through the trees*
- *friends sharing beers in a busy city, realistic colored pencil sketch, twilight, masterpiece, bright, lively*
### Advanced Prompting
<CardGrid>
<LinkCard
title="Prompting Guide"
description="Learn how to structure prompts, use positive and negative prompts well, and iterate toward better results."
href="../prompting-guide"
/>
<LinkCard
title="Prompting Syntax"
description="Learn InvokeAI's advanced prompt weighting and composition syntax, including `+`, `-`, `.blend()`, and `.and()`."
href="../prompt-syntax"
/>
<LinkCard
title="Dynamic Prompting"
description="Expand one prompt into many prompt variations with curly-brace syntax."
href="../dynamic-prompting"
/>
</CardGrid>
### Generation Workflows
Invoke offers a number of different workflows for interacting with models to produce images. Each is extremely powerful on its own, but together provide you an unparalleled way of producing high quality creative outputs that align with your vision.
<CardGrid>
<Card title="Text to Image" icon="seti:default">
Focuses on the key workflow of using a prompt to generate a new image. It includes other features that help control the generation process as well.
</Card>
<Card title="Image to Image" icon="seti:image">
Provide an image as a reference (called the “initial image”), which provides more guidance around color and structure to the AI as it generates a new image.
</Card>
<Card title="Unified Canvas" icon="pencil">
An advanced AI-first image editing tool. Drag an image onto the canvas to regenerate elements, edit content or colors (**inpainting**), or extend the image with consistency and clarity (**outpainting**).
</Card>
</CardGrid>
### Improving Image Quality
<Steps>
1. **Fine-tuning your prompt:**
The more specific you are, the closer the image will turn out to what is in your head. Adding more details in the Positive or Negative Prompt can help add or remove parts of the image. You can also use advanced techniques like upweighting and downweighting to control the influence of specific words. Learn more in the [Prompting Guide](../prompting-guide) and [Prompting Syntax](../prompt-syntax).
:::tip
If you're seeing poor results, try adding the things you don't like about the image to your negative prompt. E.g. *distorted, low quality, unrealistic, etc.*
:::
2. **Explore different models:**
Other models can produce different results due to the data they've been trained on. Each model has specific language and settings it works best with; a model's documentation is your friend here. Play around with some and see what works best for you!
3. **Increasing Steps:**
The number of steps used controls how much time the model is given to produce an image, and depends on the "Scheduler" used. More steps tends to mean better results, but will take longer. We recommend at least 30 steps for most.
4. **Tweak and Iterate:**
Remember, it's best to change one thing at a time so you know what is working and what isn't. Sometimes you just need to try a new image, and other times using a new prompt might be the ticket.
*For testing, consider turning off the "random" Seed. Using the same seed with the same settings will produce the same image, which makes it the perfect way to learn exactly what your changes are doing.*
5. **Explore Advanced Settings:**
InvokeAI has a full suite of tools available to allow you complete control over your image creation process. Check out our [features docs](../../features/gallery) if you want to learn more.
</Steps>
## Terms & Concepts
:::note
If you're interested in learning more, check out [this presentation](https://docs.google.com/presentation/d/1IO78i8oEXFTZ5peuHHYkVF-Y3e2M6iM5tCnc-YBfcCM/edit?usp=sharing) from one of our maintainers (@lstein).
:::
### Stable Diffusion
Stable Diffusion is a deep learning, text-to-image model that is the foundation of the capabilities found in InvokeAI. Since the release of Stable Diffusion, there have been many subsequent models created based on Stable Diffusion that are designed to generate specific types of images.
### Prompts
Prompts provide the models directions on what to generate. As a general rule of thumb, the more detailed your prompt is, the better your result will be.
### Models
Models are the magic that power InvokeAI. These files represent the output of training a machine on understanding massive amounts of images - providing them with the capability to generate new images using just a text description of what you'd like to see.
Invoke offers a simple way to download several different models upon installation, but many more can be discovered online, including at [civitai.com](https://civitai.com). Each model can produce a unique style of output, based on the images it was trained on.
:::note
Models that contain "inpainting" in the name are designed for use with the inpainting feature of the Unified Canvas.
:::
### Schedulers & Steps
**Schedulers** guide the process of removing noise (de-noising) from data. They determine:
1. The number of steps to take to remove the noise.
2. Whether the steps are random (stochastic) or predictable (deterministic).
3. The specific method (algorithm) used for de-noising.
**Steps** represent the number of de-noising iterations each generation goes through. Schedulers can be intricate and there's often a balance to strike between how quickly they can de-noise data and how well they can do it. It's typically advised to experiment with different schedulers to see which one gives the best results.
### Additional Concepts
<CardGrid>
<Card title="Low-Rank Adaptations (LoRAs)">
LoRAs are like a smaller, more focused version of models, intended to focus on training a better understanding of how a specific character, style, or concept looks.
</Card>
<Card title="Textual Inversion Embeddings">
Like LoRAs, embeddings assist with more easily prompting for certain characters, styles, or concepts. They are trained to update the relationship between a specific word (known as the "trigger") and the intended output.
</Card>
<Card title="ControlNet">
ControlNets are neural network models that are able to extract key features from an existing image and use these features to guide the output of the image generation model.
</Card>
<Card title="VAE">
A Variational Auto-Encoder (VAE) is an encode/decode model that translates the "latents" image produced during the image generation process to the large pixel images that we see.
</Card>
</CardGrid>
+133
View File
@@ -0,0 +1,133 @@
---
title: Models
sidebar:
order: 8
---
## Checkpoint and Diffusers Models
The model checkpoint files (`*.ckpt`) are the Stable Diffusion "secret sauce". They are the product of training the AI on millions of captioned images gathered from multiple sources.
Originally there was only a single Stable Diffusion weights file, which many people named `model.ckpt`.
Today, there are thousands of models, fine tuned to excel at specific styles, genres, or themes.
:::tip[Model Formats]
We also have two more popular model formats, both created by [HuggingFace](https://huggingface.co/):
- `safetensors`: Single file, like `.ckpt` files. Prevents malware from lurking in a model.
- `diffusers`: Splits the model components into separate files, allowing very fast loading.
InvokeAI supports all three formats.
:::
## Starter Models
When you first start InvokeAI, you'll see a popup prompting you to install some starter models from the Model Manager. Click the `Starter Models` tab to see the list.
You'll find a collection of popular and high-quality models available for easy download.
Some models carry license terms that limit their use in commercial applications or on public servers. It's your responsibility to adhere to the license terms.
## Other Models
There are a few ways to install other models:
- **URL or Local Path**: Provide the path to a model on your computer, or a direct link to the model. Some sites require you to use an API token to download models, which you can [set up in the config file]. You can also paste a HuggingFace Repo ID here directly — it is detected and routed to the HuggingFace installer automatically.
- **HuggingFace**: Paste a HF Repo ID to install it. If there are multiple models in the repo, you'll get a list to choose from. Repo IDs look like this: `XpucT/Deliberate`. There is a copy button on each repo to copy the ID.
- **Scan Folder**: Scan a local folder for models. You can install all of the detected models in one click.
### Diffusers models in HF repo subfolders
HuggingFace repos can be structured in any way. Some model authors include multiple models within the same folder.
In this situation, you may need to provide some additional information to identify the model you want, by adding `:subfolder_name` to the repo ID.
:::note[Example]
Say you have a repo ID `monster-labs/control_v1p_sd15_qrcode_monster`, and the model you want is inside the `v2` subfolder.
Add `:v2` to the repo ID and use that when installing the model: `monster-labs/control_v1p_sd15_qrcode_monster:v2`
:::
[set up in the config file]: ../../configuration/invokeai-yaml
## Editing model metadata
Every model has an editable **Source URL** field alongside its name and description. Use it to record where a model came from — for example a Civitai or HuggingFace page — independent of how it was originally installed. The URL is editable from the model's **Edit** view and appears as a clickable link in the model header once set. Models without a URL simply hide the field.
This is purely metadata: the URL has no effect on loading and is not used to refresh or reinstall the model. It is mainly useful for going back to the model's documentation, license, or example prompts later.
## Bulk actions in the Model Manager
The Model Manager supports multi-selection for batch operations.
- **Select multiple models** by clicking with **Ctrl** (Windows / Linux) or **Cmd** (macOS) held, or by using the checkboxes on each row. A sticky header at the top shows the current selection count and is always visible while you scroll.
- Open the **Actions** dropdown for the selection. The available actions are:
- **Delete Models** — removes every selected model in a single confirmation step. Partial failures (e.g. permission issues) are reported per-model in the result toast.
- **Reidentify Models** — re-probes every selected model, updating fields that depend on the file contents (type, base, format, variant, etc.). This is the bulk version of the per-model reidentify action.
:::caution[Reidentify resets custom settings]
Reidentifying a model re-derives its configuration from the file on disk. Any custom settings you've adjusted on those models — default settings, descriptions, trigger phrases — may be overwritten. The confirmation modal warns you about this before running.
:::
Both actions handle partial failures: if some models succeed and others fail, the toast lists succeeded and failed counts and the list view updates immediately for the ones that worked.
## Finding orphaned models
If a model file is deleted or moved outside the Model Manager, its database entry sticks around. To find these orphaned entries:
1. Open the Model Manager.
2. Open the **type filter** dropdown and pick **Missing Files**.
3. The list now shows only models whose files are no longer present on disk. Each one also displays a **Missing Files** badge in its row.
Orphaned models are automatically excluded from selection dropdowns (main model, LoRA, VAE, etc.), so you cannot accidentally pick one for generation. Use the [bulk delete action](#bulk-actions-in-the-model-manager) to clean them out in one step.
## Synchronizing orphaned model directories
The **Missing Files** filter finds database records whose files are gone. InvokeAI also has a separate sync workflow for the opposite situation: model directories that still exist on disk but are not referenced in the database.
This can happen after a failed import, a manual database edit, or deleting a model record while leaving files behind. The sync workflow scans the models directory for top-level folders containing model files with common model extensions, including `.safetensors`, `.ckpt`, `.pt`, `.pth`, `.bin`, `.onnx`, and `.gguf`.
To review these directories:
1. In multi-user mode, sign in as an administrator. In single-user mode, the Model Manager controls are available by default.
2. Open the Model Manager.
3. Click **Sync Models** to scan for orphaned model directories.
4. Review each reported relative directory path, contained model files, and total size before deleting anything.
:::caution[Deletion removes directories]
Deleting an orphaned model directory removes the entire reported directory from disk. The server deletes it directly with recursive directory deletion, so make sure the directory contains only files you intend to remove.
:::
Only administrators can use this workflow in multi-user mode. The underlying API is `/api/v2/models/sync/orphaned`; API results also include the absolute path for each reported directory.
## Exporting and Importing Model Settings
Each installed model has an **Export Settings** and **Import Settings** action in the Model Manager. Use these to back up a model's configuration, move it to another install, or share a curated setup with someone else.
### What gets exported
The exported `.json` file captures the configuration you have set on the model, not the model weights themselves:
- `default_settings` — steps, CFG / guidance, scheduler, dimensions, FP8 storage toggle, VAE precision, etc.
- `trigger_phrases` — for LoRAs and similar.
- `cpu_only` — for encoder-type models.
- `name`, `description`, `source_url` — the model's identifying metadata.
- `cover_image` — the model's thumbnail, embedded as a base64 data URL.
Fields you have not set are omitted from the file. The format is forward and backward compatible: older clients ignore newer fields, and a file produced by a newer version still imports cleanly into an older one (it just skips the fields it does not understand).
### Importing
Importing applies the JSON to the currently selected model:
- `default_settings`, `trigger_phrases`, `cpu_only`, `name`, `description`, and `source_url` are applied via the normal model update path. Any field that the target model type does not support (e.g. `cpu_only` on a model that has no such setting) is listed in a "skipped" toast — everything else still applies.
- `cover_image` is uploaded and set as the model's thumbnail.
Imports are validated before they run. The file is rejected if `source_url` is not an `http(s)://` URL or if `cover_image` is not a valid image data URL — so a malformed or hand-edited file cannot quietly poison a model's configuration.
### Typical workflows
- **Back up a model you've spent time tuning** so you can restore its settings after a reinstall, or roll back after experimenting.
- **Copy settings between two installs of the same model** — e.g. between a desktop and a workstation.
- **Share a curated setup** (name, description, thumbnail, default steps / CFG / scheduler, trigger phrases) for a model you have configured well.
@@ -0,0 +1,29 @@
---
title: Nodes and Workflows
sidebar:
order: 7
---
import { Card, CardGrid } from '@astrojs/starlight/components';
## What are Nodes?
A **Node** is simply a single operation that takes in inputs and returns outputs. Multiple nodes can be linked together to create more complex functionality. All InvokeAI features are added through nodes.
With nodes, you can easily extend the image generation capabilities of InvokeAI and build workflows that suit your specific needs.
### Anatomy of a Node
Individual nodes are made up of the following:
<CardGrid>
<Card title="Inputs" icon="left-arrow">
Edge points on the **left side** of the node window where you connect outputs from other nodes.
</Card>
<Card title="Outputs" icon="right-arrow">
Edge points on the **right side** of the node window where you connect to inputs on other nodes.
</Card>
<Card title="Options" icon="setting">
Various options which are either manually configured, or overridden by connecting an output from another node to the input.
</Card>
</CardGrid>
@@ -0,0 +1,143 @@
---
title: Generation Parameters
lastUpdated: 2026-02-20
sidebar:
order: 6
---
import { Card, CardGrid, Steps } from '@astrojs/starlight/components';
# Sampler Convergence
As features keep increasing, making the right choices for your needs can become increasingly difficult. What sampler to use? And for how many steps? Do you change the CFG value? Do you use prompt weighting? Do you allow variations?
Even once you have a result, do you blend it with other images? Pass it through `img2img`? With what strength? Do you use inpainting to correct small details? Outpainting to extend cropped sections?
The purpose of this series of documents is to help you better understand these tools, so you can make the best out of them. Feel free to contribute with your own findings!
In this document, we will talk about **sampler convergence**.
<Card title="TL;DR" icon="rocket">
Looking for a short version? Here is the summary:
- Results converge as steps (`-s`) are increased (except for `K_DPM_2_A` and `K_EULER_A`). Often at ≥ `-s100`, but may require ≥ `-s700`.
- Producing a batch of candidate images at low (`-s8` to `-s30`) step counts can save you hours of computation.
- `K_HEUN` and `K_DPM_2` converge in fewer steps (but are slower per step).
- `K_DPM_2_A` and `K_EULER_A` incorporate a lot of creativity and variability.
</Card>
## Sampler Performance Overview
<CardGrid>
<Card title="Speed (it/s)" icon="setting">
*(Tested on M1 Max 64GB, 512x512, 3 sample average)*
| Sampler | it/s |
| :--- | :--- |
| `DDIM` | 1.89 |
| `PLMS` | 1.86 |
| `K_EULER` | 1.86 |
| `K_LMS` | **1.91** (Fastest) |
| `K_EULER_A` | 1.86 |
| `K_HEUN` | 0.95 *(Slower)* |
| `K_DPM_2` | 0.95 *(Slower)* |
| `K_DPM_2_A` | 0.95 *(Slower)* |
</Card>
<Card title="Suggestions" icon="star">
For most use cases, `K_LMS`, `K_HEUN` and `K_DPM_2` are the best choices.
While `K_HEUN` and `K_DPM_2` run half as fast, they tend to converge twice as quickly as `K_LMS`.
At very low steps (≤ `-s8`), `K_HEUN` and `K_DPM_2` are not recommended. Use `K_LMS` instead.
For high variability between steps, use `K_EULER_A` (which runs twice as fast as `K_DPM_2_A`).
</Card>
</CardGrid>
---
## Sampler Results by Subject
Let's start by choosing a prompt and using it with each of our 8 samplers, running it for 10, 20, 30, 40, 50 and 100 steps.
### Anime
> `"an anime girl" -W512 -H512 -C7.5 -S3031912972`
![Anime Comparison Grid](https://user-images.githubusercontent.com/50542132/191868725-7f7af991-e254-4c1f-83e7-bed8c9b2d34f.png)
Immediately, you can notice results tend to converge — that is, as `-s` (step) values increase, images look more and more similar until there comes a point where the image no longer changes.
You can also notice how `DDIM` and `PLMS` eventually tend to converge to K-sampler results as steps are increased. Among K-samplers, `K_HEUN` and `K_DPM_2` seem to require the fewest steps to converge, and even at low step counts they are good indicators of the final result. Finally, `K_DPM_2_A` and `K_EULER_A` seem to do a bit of their own thing and don't keep much similarity with the rest of the samplers.
### Nature
Now, these results seem interesting, but do they hold for other topics? Let's try!
> `"valley landscape wallpaper, d&d art, fantasy, painted, 4k, high detail, sharp focus, washed colors, elaborate excellent painted illustration" -W512 -H512 -C7.5 -S1458228930`
![Nature Comparison Grid](https://user-images.githubusercontent.com/50542132/191868763-b151c69e-0a72-4cf1-a151-5a64edd0c93e.png)
With nature, you can see how initial results are even more indicative of the final result — more so than with characters/people. `K_HEUN` and `K_DPM_2` are again the quickest indicators, almost right from the start. Results also converge faster (e.g. `K_HEUN` converged at `-s21`).
### Food
> `"a hamburger with a bowl of french fries" -W512 -H512 -C7.5 -S4053222918`
![Food Comparison Grid](https://user-images.githubusercontent.com/50542132/191868898-98801a62-885f-4ea1-aee8-563503522aa9.png)
Again, `K_HEUN` and `K_DPM_2` take the fewest number of steps to be good indicators of the final result. `K_DPM_2_A` and `K_EULER_A` seem to incorporate a lot of creativity/variability, capable of producing rotten hamburgers, but also of adding lettuce to the mix. And they're the only samplers that produced an actual 'bowl of fries'!
### Animals
> `"grown tiger, full body" -W512 -H512 -C7.5 -S3721629802`
![Animal Comparison Grid](https://user-images.githubusercontent.com/50542132/191868870-9e3b7d82-b909-429f-893a-13f6ec343454.png)
`K_HEUN` and `K_DPM_2` once again require the least number of steps to be indicative of the final result (around `-s30`), while other samplers are still struggling with several tails or malformed back legs.
It also takes longer to converge (for comparison, `K_HEUN` required around 150 steps to converge). This is normal, as producing human/animal faces/bodies is one of the things the model struggles the most with. For these topics, running for more steps will often increase coherence within the composition.
### People
> `"Ultra realistic photo, (Miranda Bloom-Kerr), young, stunning model, blue eyes, blond hair, beautiful face, intricate, highly detailed, smooth, art by artgerm and greg rutkowski and alphonse mucha, stained glass" -W512 -H512 -C7.5 -S2131956332`. *(This time, we will go up to 300 steps).*
![People Comparison Grid 1](https://user-images.githubusercontent.com/50542132/191871743-6802f199-0ffd-4986-98c5-df2d8db30d18.png)
Observing the results, it again takes longer for all samplers to converge (`K_HEUN` took around 150 steps), but we can observe good indicative results much earlier (see: `K_HEUN`). Conversely, `DDIM` and `PLMS` are still undergoing moderate changes (see: lace around her neck), even at `-s300`.
In fact, as we can see in this other experiment, some samplers can take 700+ steps to converge when generating people.
![People Comparison Grid 2](https://user-images.githubusercontent.com/50542132/191992123-7e0759d6-6220-42c4-a961-88c7071c5ee6.png)
Note also the point of convergence may not be the most desirable state (e.g. you might prefer an earlier version of the face that is more rounded), but it will probably be the most coherent regarding arms/hands/face attributes. You can always merge different images with a photo editing tool and pass it through `img2img` to smoothen the composition.
---
## Batch Generation Speedup
This realization about convergence is very useful because it means you don't need to create a batch of 100 images (`-n100`) at `-s100` just to choose your favorite 2 or 3 images.
You can produce the same 100 images at `-s10` to `-s30` using a K-sampler (since they converge faster), get a rough idea of the final result, choose your 2 or 3 favorite ones, and then run `-s100` on those specific images to polish details. This technique is **3-8x as quick**.
:::tip[Time Savings Example]
Assuming 60 seconds per 100 steps:
- **Method A:** 60s * 100 images = **6000s** (100 images at `-s100`, manually picking 3 favorites). Total time: **1 hour and 40 minutes.**
- **Method B:** 6s * 100 images + 60s * 3 images = **780s** (100 images at `-s10`, manually picking 3 favorites, and running those 3 at `-s100` to polish details). Total time: **13 minutes.**
:::
## Three Key Takeaways
Finally, it is relevant to mention that, in general, there are 3 important moments in the process of image formation as steps increase:
<Steps>
1. **The Indicator Stage:**
The earliest point at which an image becomes a good indicator of the final result. This is useful for batch generation at low step values to preview outputs before committing to higher steps.
2. **The Coherence Stage:**
The point at which an image becomes coherent, even if different from the final converged result. This is useful for low-step batch generation where quality is improved via other techniques (like inpainting) rather than raw step count.
3. **The Convergence Stage:**
The point at which an image fully converges and stops changing.
</Steps>
:::note[Workflow Dictates Strategy]
Remember that your workflow/strategy should define your optimal number of steps, even for the same prompt and seed. For example, if you seek full convergence, you may run `K_LMS` for `-s200`. However, running `K_LMS` for `-s20` (taking one-tenth the time) may perform just as well if your workflow includes adding small missing details via `img2img`.
:::
![Low Step Sampler Comparison](https://user-images.githubusercontent.com/50542132/192046823-2714cb29-bbf3-4eb1-9213-e27a0963905c.png)
@@ -0,0 +1,138 @@
---
title: Prompting Syntax
lastUpdated: 2026-03-30
sidebar:
order: 3
---
import { Card, LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard
title="Prompting Guide"
href="../prompting-guide"
description="Learn how to write effective prompts for InvokeAI."
/>
<LinkCard
title="Dynamic Prompting"
href="../dynamic-prompting"
description="Learn how to create many prompt variations from a single template."
/>
</CardGrid>
InvokeAI supports Compel-style prompt weighting and prompt functions for `SD 1.5` and `SDXL` text conditioning workflows. Recent model families, including `FLUX`, `Z-Image`, `CogView4`, and `Qwen Image`, bypass Compel and do not use the syntax documented on this page. This page documents syntax for those Compel-based workflows only. If you want general advice on writing better prompts, start with [Prompting Guide](../prompting-guide).
:::note[Compatibility note]
If a weighted prompt seems to be ignored, check whether you are using an `SD 1.5` or `SDXL` workflow. Compel syntax on this page does not apply to newer model families such as `FLUX`, `Z-Image`, `CogView4`, and `Qwen Image`.
:::
## Quick reference
<Card title="Supported syntax" icon="rocket">
- Increase a single word: `trees+`
- Decrease a single word: `fog-`
- Weight a phrase: `(golden hour light)+`
- Use an exact numeric weight: `(cinematic lighting)1.25`
- Nest weights: `(portrait with (blue eyes)1.3)1.1`
- Blend prompts: `("portrait photo", "oil painting").blend(0.7, 0.3)`
- Conjoin clauses: `("red silk dress", "studio portrait", "soft rim light").and()`
- Escape literal parentheses: `colored pencil \(medium\)`
</Card>
## Attention weighting with `+` and `-`
Append `+` to increase influence, or `-` to reduce it.
```text
freckles+
background crowd-
(soft rim light)++
```
Rules of thumb:
- Single words can be weighted directly.
- Multi-word phrases should be wrapped in parentheses.
- Each additional `+` compounds upward.
- Each additional `-` compounds downward in roughly 10% steps.
:::tip[Start small]
One or two steps is usually enough. Extreme weighting can overpower the rest of the prompt.
:::
## Numeric weights
Use numeric weights when you want precise control instead of repeated plus or minus markers.
```text
(cinematic lighting)1.25
(background crowd)0.8
(sharp focus)1.1
```
Guidelines:
- `1` is neutral.
- Values greater than `1` increase emphasis.
- Values between `0` and `1` reduce emphasis.
- Wrap the weighted phrase in parentheses.
## Grouping and nesting
You can group phrases and apply weight to the whole group, then nest another weighted phrase inside it.
```text
(portrait with (blue eyes)1.3)1.1
```
In this example, the outer group strengthens the whole phrase, and the inner group gives `blue eyes` even more emphasis.
## Blend prompts with `.blend()`
Use `.blend()` to mix the meaning of two or more prompts.
```text
("portrait photo, 85mm lens", "oil painting, visible brushstrokes").blend(0.7, 0.3)
```
This is most useful for combining concepts or styles that you want balanced deliberately.
Tips:
- Provide one weight for each prompt argument.
- Keeping the weights near a total of `1` makes the result easier to reason about.
- Quoted arguments are the safest choice, especially when the prompts contain commas.
## Combine clauses with `.and()`
Use `.and()` when you want separate prompt clauses encoded individually instead of as one long comma-separated sentence.
```text
("red silk dress", "studio portrait", "soft rim light").and()
```
This can behave differently from:
```text
red silk dress, studio portrait, soft rim light
```
If a normal prompt keeps collapsing ideas together, `.and()` is worth testing.
## Escape literal parentheses
Unescaped parentheses are treated as prompt syntax. If you want actual parentheses in the text, escape them with backslashes.
```text
colored pencil \(medium\)
portrait \(realistic\) (high quality)1.2
A bear \(with razor-sharp teeth\) in a forest
```
Use unescaped parentheses only when you mean grouping or weighting.
## Related pages
- For practical prompt-writing advice, read [Prompting Guide](../prompting-guide).
- For prompt expansion and permutations, read [Dynamic Prompting](../dynamic-prompting).
@@ -0,0 +1,180 @@
---
title: Prompting Guide
lastUpdated: 2026-03-30
sidebar:
order: 2
---
import { Card, CardGrid, Steps, LinkCard } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard
title="Prompting Syntax"
href="../prompt-syntax"
description="Learn how to weight prompt terms, blend concepts, and use prompt conjunctions for more control."
/>
<LinkCard
title="Dynamic Prompting"
href="../dynamic-prompting"
description="Learn how to create many prompt variations from a single template."
/>
</CardGrid>
Prompting in InvokeAI works best when you describe the image clearly, then refine only the parts that matter. This page focuses on practical prompt-writing habits.
<CardGrid>
<Card title="Subject" icon="seti:image">
Start with the main thing you want to see: a character, object, scene, or action.
</Card>
<Card title="Style or Medium" icon="pencil">
Add the visual language: photograph, watercolor, oil painting, 3D render, anime illustration, and so on.
</Card>
<Card title="Lighting and Composition" icon="setting">
Describe the camera angle, framing, lighting, environment, color palette, or mood that will shape the image.
</Card>
<Card title="Detail and Finish" icon="star">
Add a few high-value quality cues such as fabric texture, shallow depth of field, natural skin texture, or painterly brushwork.
</Card>
</CardGrid>
A simple pattern that works well is:
`subject, style or medium, lighting or composition, a few important details`
Not every prompt needs every category. Start simple, then add detail only when the model needs more direction.
## Positive and negative prompts
<CardGrid>
<Card title="Positive Prompt" icon="seti:default">
Use the positive prompt to describe what you want the model to create. Put the most important idea early and keep the wording concrete.
</Card>
<Card title="Negative Prompt" icon="document">
Use the negative prompt to remove recurring problems or unwanted traits. Keep it short and targeted instead of pasting a giant list into every generation.
</Card>
</CardGrid>
Good negative prompts usually name specific failure modes: `blurry`, `distorted hands`, `low detail`, `extra limbs`.
:::tip[Negative prompts are strong]
A negative term can suppress nearby concepts too. If you negate something broad like `green` or `moss`, you may also weaken grass, foliage, or other related ideas.
:::
## A practical prompting workflow
<Steps>
1. Start with the core image
Write the clearest version of the image you want before adding stylistic extras.
2. Add style and composition
Once the subject is right, add medium, lens, lighting, mood, background, or framing details.
3. Test with a fixed seed
When you are learning what a prompt change does, keep the seed stable so you can compare results directly.
4. Change one thing at a time
If you add five new terms at once, you will not know which one helped.
5. Escalate only when needed
If the result is close but one element is too weak or too strong, move to [Prompting Syntax](../prompt-syntax) for weighting. If you want lots of variations, use [Dynamic Prompting](../dynamic-prompting).
</Steps>
Here is the same idea refined in stages:
```text
portrait of a woman
portrait of a woman, studio photograph, soft key light
portrait of a woman, studio photograph, soft key light, 85mm lens, shallow depth of field, natural skin texture
```
## Write for the model you are using
The same prompt can behave very differently across models.
- Photo-oriented models respond well to camera, lens, lighting, and texture language.
- Illustration models often respond better to medium, art direction, and shape language.
- Specialty models may expect specific trigger words, subjects, or styles from their own model card.
- If a prompt works beautifully on one model and poorly on another, that does not always mean the prompt is bad. The model may just speak a different visual language.
## When advanced syntax helps
Reach for advanced syntax when a normal comma-separated prompt is almost right, but you need more control.
- Use [Prompting Syntax](../prompt-syntax) when one term needs more or less influence.
- Use `.blend()` when you want to mix concepts or styles deliberately.
- Use `.and()` when you want separate prompt clauses encoded individually.
- Use [Dynamic Prompting](../dynamic-prompting) when you want many prompt variations from one template.
## Common mistakes
- Packing too many unrelated ideas into one prompt.
- Using long generic quality-word lists before you know the base prompt works.
- Treating the negative prompt as a trash can for every bad outcome.
- Expecting identical behavior across models, schedulers, and workflows.
- Changing prompt, model, seed, and settings all at once while troubleshooting.
## Example prompts
### Photographic portrait
**Positive prompt**
```text
editorial portrait of a woman in a charcoal coat, studio photograph, soft key light, subtle rim light, 85mm lens, shallow depth of field, natural skin texture
```
**Negative prompt**
```text
blurry, low detail, waxy skin, extra fingers
```
### Environment concept art
**Positive prompt**
```text
ancient stone temple built into a cliffside, fantasy concept art, misty sunrise, towering scale, moss-covered stairs, cinematic atmosphere
```
**Negative prompt**
```text
flat lighting, low contrast, muddy details
```
### Product-style render
**Positive prompt**
```text
sleek ceramic teapot on a matte stone surface, product photography, clean studio lighting, soft shadow, high detail, minimal background
```
**Negative prompt**
```text
cluttered background, distortion, duplicate objects
```
### Stylized illustration
**Positive prompt**
```text
fox courier crossing a rainy city street, storybook illustration, bold shapes, glowing shop signs, reflective pavement, warm and cool color contrast
```
**Negative prompt**
```text
photorealistic, dull colors, low detail
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

@@ -0,0 +1,95 @@
---
title: Docker
---
import { Aside, Tabs, TabItem } from '@astrojs/starlight/components'
import SystemRequirementsLink from '@components/SystemRequirmentsLink.astro'
<SystemRequirementsLink />
:::note[Operating Systems and GPU Support]
<Tabs syncKey="operatingSystem">
<TabItem label="Windows" icon="seti:windows">
Docker Desktop on Windows [includes GPU support](https://www.docker.com/blog/wsl-2-gpu-support-for-docker-desktop-on-nvidia-gpus/).
</TabItem>
<TabItem label="MacOS" icon="apple">
Docker can not access the GPU on macOS, so your generation speeds will be slow. Use the [launcher](../../start-here/installation) instead.
</TabItem>
<TabItem label="Linux" icon="linux">
Configure Docker to access your machine's GPU.
Follow the [NVIDIA](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) or [AMD](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html) documentation.
</TabItem>
</Tabs>
:::
## TL;DR
Ensure your Docker setup is able to use your GPU. Then:
```bash
docker run --runtime=nvidia --gpus=all --publish 9090:9090 ghcr.io/invoke-ai/invokeai
```
Once the container starts up, open [http://localhost:9090](http://localhost:9090) in your browser, install some models, and start generating.
## Build-It-Yourself
All the docker materials are located inside the [docker](https://github.com/invoke-ai/InvokeAI/tree/main/docker) directory in the Git repo.
```bash
cd docker
cp .env.sample .env
docker compose up
```
We also ship the `run.sh` convenience script. See the `docker/README.md` file for detailed instructions on how to customize the docker setup to your needs.
### Prerequisites
#### Install [Docker](https://github.com/santisbon/guides#docker)
On the [Docker Desktop app](https://docs.docker.com/get-docker/), go to `Preferences` -> `Resources` -> `Advanced`. Increase the CPUs and Memory to avoid this [Issue](https://github.com/invoke-ai/InvokeAI/issues/342). You may need to increase Swap and Disk image size too.
### Setup
Set up your environment variables. In the `docker` directory, make a copy of `.env.sample` and name it `.env`. Make changes as necessary.
Any environment variables supported by InvokeAI can be set here - please see the [configuration docs](/configuration/invokeai-yaml/) for further detail.
At the very least, you might want to set the `INVOKEAI_ROOT` environment variable
to point to the location where you wish to store your InvokeAI models, configuration, and outputs.
| Environment Variable | Default value | Description |
| --- | --- | --- |
| `INVOKEAI_ROOT` | `~/invokeai` | **Required** - the location of your InvokeAI root directory. It will be created if it does not exist. |
| `HUGGING_FACE_HUB_TOKEN` | | InvokeAI will work without it, but some of the integrations with HuggingFace (like downloading from models from private repositories) may not work |
| `GPU_DRIVER` | `cuda` | Optionally change this to `rocm` to build the image for AMD GPUs. NOTE: Use the `build.sh` script to build the image for this to take effect. |
#### Build the Image
Use the standard `docker compose build` command from within the `docker` directory.
If using an AMD GPU:
a: set the `GPU_DRIVER=rocm` environment variable in `docker-compose.yml` and continue using `docker compose build` as usual, or
b: set `GPU_DRIVER=rocm` in the `.env` file and use the `build.sh` script, provided for convenience
#### Run the Container
Use the standard `docker compose up` command, and generally the `docker compose` [CLI](https://docs.docker.com/compose/reference/) as usual.
Once the container starts up (and configures the InvokeAI root directory if this is a new installation), you can access InvokeAI at [http://localhost:9090](http://localhost:9090)
## Troubleshooting / FAQ
<details>
<summary>"I am running Windows under WSL2, and am seeing a 'no such file or directory' error."</summary>
Your `docker-entrypoint.sh` might have has Windows (CRLF) line endings, depending how you cloned the repository.
To solve this, change the line endings in the `docker-entrypoint.sh` file to `LF`. You can do this in VSCode
(`Ctrl+P` and search for "line endings"), or by using the `dos2unix` utility in WSL.
Finally, you may delete `docker-entrypoint.sh` followed by `git pull; git checkout docker/docker-entrypoint.sh`
to reset the file to its most recent version.
For more information on this issue, see [Docker Desktop documentation](https://docs.docker.com/desktop/troubleshoot/topics/#avoid-unexpected-syntax-errors-use-unix-style-line-endings-for-files-in-containers)
</details>
@@ -0,0 +1,128 @@
---
title: FP8 Storage
sidebar:
order: 3
---
import { Steps } from '@astrojs/starlight/components';
FP8 Storage cuts a model's VRAM footprint roughly in half by keeping weights on the GPU in 8-bit floating-point format (`float8_e4m3fn`). During inference, each layer's weights are cast on-the-fly back up to the compute precision (FP16/BF16), then cast back to FP8 after the forward pass — so quality is largely preserved.
It pairs well with [Low-VRAM mode](/configuration/low-vram-mode/): low-VRAM mode streams layers between RAM and VRAM, while FP8 Storage shrinks the layers themselves.
:::caution[For full precision models only]
FP8 Storage only applies to **full precision** checkpoints (FP16 / BF16 / FP32). It is **silently a no-op** for already-quantized formats — **GGUF**, **NF4**, and **int8** checkpoints carry their own storage precision and the loader returns a different module type that the FP8 layer cast does not touch. If your model is already quantized, the toggle has no effect; use the full-precision variant of the model if you want to enable FP8 Storage.
:::
## Requirements
- **Nvidia GPU on Windows or Linux.** FP8 Storage uses CUDA tensor types and is silently disabled on CPU and MPS.
- **CUDA 12.x and recent PyTorch.** The `float8_e4m3fn` dtype was added in PyTorch 2.1 — InvokeAI's bundled versions satisfy this.
There is no hardware requirement for FP8 *compute* — InvokeAI casts back to FP16/BF16 for math. This means FP8 Storage works on GPUs that do not natively support FP8 matmul (e.g. RTX 30-series), at a small per-step throughput cost.
## Hardware support tiers
InvokeAI's FP8 path stores weights in FP8 and casts them back to BF16/FP16 on each forward pass via its own `register_forward_pre_hook` / `register_forward_hook` wrappers (the same skip list as diffusers' `apply_layerwise_casting`, but applied to every `nn.Module` — including diffusers `ModelMixin` subclasses — so it composes correctly with InvokeAI's `CustomLinear` and partial loading). The practical benefit of toggling FP8 Storage depends on what your GPU can do natively. There are three tiers:
### RTX 30-series and older Ampere workstation cards — VRAM win only
The toggle works as advertised: the UNet / transformer drops by roughly 50% on the GPU. Per-step latency is the same or marginally slower because every forward pass adds an FP8 → BF16 cast on entry and a BF16 → FP8 cast on exit. This is the **largest target group**: 3090 owners squeezing FLUX into 24 GB benefit the most.
### RTX 40-series, RTX 50-series, and Hopper — VRAM win today, compute win possible later
These GPUs have native FP8 tensor cores. The toggle still buys you the same ~50% VRAM reduction today, because the forward pass still runs in BF16 — the hook casts weights back up to compute precision before each layer. If InvokeAI later wires up a true FP8 matmul path (e.g. via `torchao`), the same toggle will *also* unlock compute speedups on this hardware. Until then, treat the benefit as "VRAM only, same as Ampere".
### Older CUDA cards — still a VRAM win
`float8_e4m3fn` is a pure storage dtype in PyTorch and works on any CUDA device, so pre-Ampere cards (GTX 16-series, RTX 20-series, etc.) get the same ~50% VRAM reduction as Ampere. There are no native FP8 tensor cores on these GPUs, so the throughput trade-off is the same as on the 30-series: cast in, compute in BF16/FP16, cast back out.
### MPS and CPU — no-op
FP8 Storage is silently disabled on anything that is not CUDA. On CPU PyTorch *technically* supports FP8 dtypes, but the cast operations are software-emulated and end up costing more than the memory savings buy back, so InvokeAI gates the entire path on `device.type == "cuda"`. If you toggle it on CPU or MPS, the loader skips the cast and returns the model unchanged with no log line.
## Enabling FP8 Storage
FP8 Storage is a **per-model setting**, configured from the Model Manager:
<Steps>
1. Open the **Model Manager**.
2. Select a model (Main, ControlNet, or T2I-Adapter).
3. Under **Default Settings**, toggle **FP8 Storage (Save VRAM)**.
4. Click **Save**.
</Steps>
The setting takes effect on the next load. If the model is already in the cache, InvokeAI evicts the cached copy automatically so the new setting applies — even if a generation is currently using the model (the eviction is deferred until the generation finishes).
:::tip[When to enable]
Enable FP8 Storage on large models that don't fit comfortably in VRAM — FLUX dev/Klein, large SDXL checkpoints, ControlNet-XL adapters. For smaller SD1 / SD2 models, the savings are negligible and not worth the small precision trade-off.
:::
## What FP8 Storage applies to
FP8 Storage is **only** applied to layers where the precision trade-off is acceptable:
| Model type | FP8 applied? |
| ----------------------------- | -------------------------------------- |
| Main models (SD1, SD2, SDXL) | Yes |
| FLUX.1 / FLUX.2 Klein | Yes |
| ControlNet, T2I-Adapter | Yes |
| VAE | No — visible decode-quality regression |
| Text encoders, tokenizers | No — small models, no benefit |
| Z-Image (any variant) | No — dtype mismatch with skipped layers|
| LoRA, ControlLoRA | No — patched into base, not run alone |
Within a supported model, **norm layers, position/patch embeddings, and `proj_in`/`proj_out` are skipped** so precision-sensitive tiny learned scalars (e.g. FLUX `RMSNorm.scale`) aren't crushed to FP8. This mirrors the diffusers default skip list.
## Quality trade-offs
FP8 Storage is **near-lossless** for most workloads because:
- Norms and embeddings (the precision-sensitive layers) are skipped.
- The actual matmul still happens in FP16/BF16 — FP8 is only the on-GPU storage format.
That said, some artifacts have been reported on:
- **VAEs** — never cast (the toggle has no effect on VAE submodels).
- **Heavy LoRA stacks** — patching is unaffected, but very precision-sensitive LoRAs may show slight drift. Compare a side-by-side if your workflow depends on subtle LoRA behavior.
If you see unexpected quality regressions, disable FP8 Storage on the affected model and re-run.
## Combining with Low-VRAM mode
**FP8 + partial loading**: fully supported. FP8 Storage shrinks the layers; partial loading streams them between RAM and VRAM as needed. Use both on tight VRAM budgets.
(For why FP8 Storage doesn't stack on top of GGUF / NF4 / int8 checkpoints, see the callout at the top of this page.)
## Troubleshooting
### "I toggled FP8 Storage but VRAM usage didn't change"
The cache eviction is immediate for idle models, but **deferred until the next unlock** if the model is mid-generation. Wait for the current generation to finish, then start a new one — the next load will use the new setting.
If VRAM still hasn't dropped:
- Check the InvokeAI log for `FP8 layerwise casting enabled for <model name>`. If the line isn't there, the model is on the exclusion list (VAE, text encoder, Z-Image, LoRA — see table above).
- Confirm you are on CUDA. FP8 Storage is silently disabled on CPU and MPS.
### Quality regression on a specific model
Disable FP8 Storage for that model in Model Manager and reload. If quality is restored, the model has FP8-sensitive layers that fall outside the default skip list. Please open an issue with the model name and a side-by-side comparison.
### "RuntimeError: ... float8_e4m3fn ..."
You're on a PyTorch version that predates FP8 support. Reinstall InvokeAI using the official launcher — the bundled torch version supports FP8.
### Reporting an FP8 issue
If FP8 Storage misbehaves — crash, quality regression, OOM that shouldn't happen — please [open a GitHub issue](https://github.com/invoke-ai/InvokeAI/issues/new/choose) and include:
- **What you did**: the workflow / generation step that triggered the problem, and whether it reproduces every time.
- **Model**: exact name and variant (e.g. "FLUX.2 Klein 9B Diffusers", "SDXL Base 1.0 single-file"), and whether the file is a full-precision checkpoint or already quantized (GGUF / NF4 / int8).
- **LoRAs**: whether any LoRAs (or ControlLoRAs) are stacked on the model, and how many.
- **Other toggles**: Low-VRAM mode on/off, any `cpu_only` text encoder setting, configured VRAM limit.
- **GPU**: model and VRAM size (e.g. "RTX 3090 24 GB", "RTX 4070 Ti 12 GB").
- **OS**: Windows or Linux, plus driver / CUDA version if you have it.
- **Logs**: the InvokeAI log around the failure — in particular the `FP8 layerwise casting enabled for <model>` line (or its absence) and any traceback.
A side-by-side image comparison (FP8 on vs. FP8 off, same seed) is extremely useful for quality regressions.
@@ -0,0 +1,212 @@
---
title: YAML Config
sidebar:
order: 1
---
import { FileTree } from '@astrojs/starlight/components'
import SettingsDocs from '@lib/components/SettingsDocs.astro'
Runtime settings, including the location of files and directories, memory usage, and performance, are managed via the `invokeai.yaml` config file or environment variables. A subset of settings may be set via commandline arguments.
Settings sources are used in this order:
- CLI args
- Environment variables
- `invokeai.yaml` settings
- Fallback: defaults
### InvokeAI Root Directory
On startup, InvokeAI searches for its "root" directory. This is the directory that contains models, images, the database, and so on. It also contains a configuration file called `invokeai.yaml`.
<FileTree>
- models/
- outputs/
- databases/
- workflow_thumbnails/
- style_presets/
- nodes/
- configs/
- invokeai.example.yaml
- **invokeai.yaml**
</FileTree>
InvokeAI searches for the root directory in this order:
1. The `--root <path>` CLI arg.
2. The environment variable INVOKEAI_ROOT.
3. The directory containing the currently active virtual environment.
4. Fallback: a directory in the current user's home directory named `invokeai`.
### InvokeAI Configuration File
Inside the root directory, we read settings from the `invokeai.yaml` file.
It has two sections - one for internal use and one for user settings:
```yaml
# Internal metadata - do not edit:
schema_version: 4.0.2
# Put user settings here - see https://invoke.ai/configuration/invokeai-yaml/:
host: 0.0.0.0 # serve the app on your local network
models_dir: D:\invokeai\models # store models on an external drive
precision: float16 # always use fp16 precision
```
The settings in this file will override the defaults. You only need
to change this file if the default for a particular setting doesn't
work for you.
You'll find an example file next to `invokeai.yaml` that shows the default values.
Some settings, like [Model Marketplace API Keys], require the YAML
to be formatted correctly. Here is a [basic guide to YAML files].
#### Custom Config File Location
You can use any config file with the `--config` CLI arg. Pass in the path to the `invokeai.yaml` file you want to use.
Note that environment variables will trump any settings in the config file.
#### Model Marketplace API Keys
Some model marketplaces require an API key to download models. You can provide a URL pattern and appropriate token in your `invokeai.yaml` file to provide that API key.
The pattern can be any valid regex (you may need to surround the pattern with quotes):
```yaml
remote_api_tokens:
# Any URL containing `models.com` will automatically use `your_models_com_token`
- url_regex: models.com
token: your_models_com_token
# Any URL matching this contrived regex will use `some_other_token`
- url_regex: '^[a-z]{3}whatever.*\.com$'
token: some_other_token
```
The provided token will be added as a `Bearer` token to the network requests to download the model files. As far as we know, this works for all model marketplaces that require authorization.
:::tip[Hugging face Models]
If you get an error when installing a HF model using a URL instead of repo id, you may need to [set up a HF API token](https://huggingface.co/settings/tokens) and add an entry for it under `remote_api_tokens`. Use `huggingface.co` for `url_regex`.
:::
#### Model Hashing
Models are hashed during installation, providing a stable identifier for models across all platforms. Hashing is a one-time operation.
```yaml
hashing_algorithm: blake3_single # default value
```
You might want to change this setting, depending on your system:
- `blake3_single` (default): Single-threaded - best for spinning HDDs, still OK for SSDs
- `blake3_multi`: Parallelized, memory-mapped implementation - best for SSDs, terrible for spinning disks
- `random`: Skip hashing entirely - fastest but of course no hash
During the first startup after upgrading to v4, all of your models will be hashed. This can take a few minutes.
Most common algorithms are supported, like `md5`, `sha256`, and `sha512`. These are typically much, much slower than either of the BLAKE3 variants.
#### Path Settings
These options set the paths of various directories and files used by InvokeAI. Any user-defined paths should be absolute paths.
#### Image Subfolder Strategy
By default, generated images are stored in a single flat directory under `outputs/images/`. The `image_subfolder_strategy` setting lets you organize newly-created images into subfolders automatically. You can edit this setting in `invokeai.yaml` or, as an admin user, in the Settings panel.
```yaml
image_subfolder_strategy: flat # default value
```
Available strategies:
| Strategy | Example Path | Description |
| -------- | -------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `flat` | `outputs/images/abc123.png` | Store images directly in the images directory. |
| `date` | `outputs/images/2026/03/17/abc123.png` | Organize images by creation date. |
| `type` | `outputs/images/general/abc123.png` | Organize images by image category. |
| `hash` | `outputs/images/ab/abc123.png` | Use the first two characters of the image UUID for filesystem performance with large collections. |
Changing this setting only affects newly-created images. Existing images remain in their current locations unless you run [Image Storage Maintenance](/features/image-storage-maintenance/).
#### Logging
Several different log handler destinations are available, and multiple destinations are supported by providing a list:
```yaml
log_handlers:
- console
- syslog=localhost
- file=/var/log/invokeai.log
```
- `console` is the default. It prints log messages to the command-line window from which InvokeAI was launched.
- `syslog` is only available on Linux and Macintosh systems. It uses
the operating system's "syslog" facility to write log file entries
locally or to a remote logging machine. `syslog` offers a variety
of configuration options:
```yaml
syslog=/dev/log` - log to the /dev/log device
syslog=localhost` - log to the network logger running on the local machine
syslog=localhost:512` - same as above, but using a non-standard port
syslog=fredserver,facility=LOG_USER,socktype=SOCK_DRAM`
- Log to LAN-connected server "fredserver" using the facility LOG_USER and datagram packets.
```
- `http` can be used to log to a remote web server. The server must be
properly configured to receive and act on log messages. The option
accepts the URL to the web server, and a `method` argument
indicating whether the message should be submitted using the GET or
POST method.
```yaml
http=http://my.server/path/to/logger,method=POST
```
The `log_format` option provides several alternative formats:
- `color` - default format providing time, date and a message, using text colors to distinguish different log severities
- `plain` - same as above, but monochrome text only
- `syslog` - the log level and error message only, allowing the syslog system to attach the time and date
- `legacy` - a format similar to the one used by the legacy 2.3 InvokeAI releases.
### Environment Variables
All settings may be set via environment variables by prefixing `INVOKEAI_`
to the variable name. For example, `INVOKEAI_HOST` would set the `host`
setting.
For non-primitive values, pass a JSON-encoded string:
```sh
export INVOKEAI_REMOTE_API_TOKENS='[{"url_regex":"modelmarketplace", "token": "12345"}]'
```
We suggest using `invokeai.yaml`, as it is more user-friendly.
### CLI Args
A subset of settings may be specified using CLI args:
- `--root`: specify the root directory
- `--config`: override the default `invokeai.yaml` file location
### Low-VRAM Mode
See the [Low-VRAM mode docs][low-vram] for details on enabling this feature.
### All Settings
The full settings reference is below. Additional explanations for selected settings appear earlier on this page.
<SettingsDocs />
[basic guide to yaml files]: https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/
[Model Marketplace API Keys]: #model-marketplace-api-keys
[low-vram]: /configuration/low-vram-mode
@@ -0,0 +1,182 @@
---
title: Low-VRAM mode
sidebar:
order: 2
---
As of v5.6.0, Invoke has a low-VRAM mode. It works on systems with dedicated GPUs (Nvidia GPUs on Windows/Linux and AMD GPUs on Linux).
This allows you to generate even if your GPU doesn't have enough VRAM to hold full models. Most users should be able to run even the beefiest models - like the ~24GB unquantised FLUX dev model.
## Enabling Low-VRAM mode
Low-VRAM mode is **enabled by default** via the `enable_partial_loading: true` setting in `invokeai.yaml`. No action is required to turn it on.
**Windows users should also [disable the Nvidia sysmem fallback](#disabling-nvidia-sysmem-fallback-windows-only)**.
It is possible to fine-tune the settings for best performance or if you still get out-of-memory errors (OOMs).
If you want to disable partial loading (e.g. on systems with plenty of VRAM where full loading is faster), add this line to your `invokeai.yaml` and restart Invoke:
```yaml
enable_partial_loading: false
```
:::tip[How to find `invokeai.yaml`]
The `invokeai.yaml` configuration file lives in your install directory. To access it, run the **Invoke Community Edition** launcher and click the install location. This will open your install directory in a file explorer window.
You'll see `invokeai.yaml` there and can edit it with any text editor. After making changes, restart Invoke.
If you don't see `invokeai.yaml`, launch Invoke once. It will create the file on its first startup.
:::
## Details and fine-tuning
Low-VRAM mode involves 4 features, each of which can be configured or fine-tuned:
- Partial model loading (`enable_partial_loading`)
- PyTorch CUDA allocator config (`pytorch_cuda_alloc_conf`)
- Dynamic RAM and VRAM cache sizes (`max_cache_ram_gb`, `max_cache_vram_gb`)
- Working memory (`device_working_mem_gb`)
- Keeping a RAM weight copy (`keep_ram_copy_of_weights`)
Read on to learn about these features and understand how to fine-tune them for your system and use-cases.
### Partial model loading
Invoke's partial model loading works by streaming model "layers" between RAM and VRAM as they are needed.
When an operation needs layers that are not in VRAM, but there isn't enough room to load them, inactive layers are offloaded to RAM to make room.
#### Enabling partial model loading
Partial model loading is enabled by default. The corresponding setting in `invokeai.yaml` is:
```yaml
enable_partial_loading: true
```
Set it to `false` to disable partial loading.
### PyTorch CUDA allocator config
The PyTorch CUDA allocator's behavior can be configured using the `pytorch_cuda_alloc_conf` config. Tuning the allocator configuration can help to reduce the peak reserved VRAM. The optimal configuration is dependent on many factors (e.g. device type, VRAM, CUDA driver version, etc.), but switching from PyTorch's native allocator to using CUDA's built-in allocator works well on many systems. To try this, add the following line to your `invokeai.yaml` file:
```yaml
pytorch_cuda_alloc_conf: "backend:cudaMallocAsync"
```
A more complete explanation of the available configuration options is [here](https://pytorch.org/docs/stable/notes/cuda.html#optimizing-memory-usage-with-pytorch-cuda-alloc-conf).
### Dynamic RAM and VRAM cache sizes
Loading models from disk is slow and can be a major bottleneck for performance. Invoke uses two model caches - RAM and VRAM - to reduce loading from disk to a minimum.
By default, Invoke manages these caches' sizes dynamically for best performance.
#### Fine-tuning cache sizes
Prior to v5.6.0, the cache sizes were static, and for best performance, many users needed to manually fine-tune the `ram` and `vram` settings in `invokeai.yaml`.
As of v5.6.0, the caches are dynamically sized. The `ram` and `vram` settings are no longer used, and new settings are added to configure the cache.
**Most users will not need to fine-tune the cache sizes.**
But, if your GPU has enough VRAM to hold models fully, you might get a perf boost by manually setting the cache sizes in `invokeai.yaml`:
```yaml
# The default max cache RAM size is logged on InvokeAI startup. It is determined based on your system RAM / VRAM.
# You can override the default value by setting `max_cache_ram_gb`.
# Increasing `max_cache_ram_gb` will increase the amount of RAM used to cache inactive models, resulting in faster model
# reloads for the cached models.
# As an example, if your system has 32GB of RAM and no other heavy processes, setting the `max_cache_ram_gb` to 28GB
# might be a good value to achieve aggressive model caching.
max_cache_ram_gb: 28
# The default max cache VRAM size is adjusted dynamically based on the amount of available VRAM (taking into
# consideration the VRAM used by other processes).
# You can override the default value by setting `max_cache_vram_gb`.
# CAUTION: Most users should not manually set this value. See warning below.
max_cache_vram_gb: 16
```
:::caution[Max safe value for `max_cache_vram_gb`]
Most users should not manually configure the `max_cache_vram_gb`. This configuration value takes precedence over the `device_working_mem_gb` and any operations that explicitly reserve additional working memory (e.g. VAE decode). As such, manually configuring it increases the likelihood of encountering out-of-memory errors.
For users who wish to configure `max_cache_vram_gb`, the max safe value can be determined by subtracting `device_working_mem_gb` from your GPU's VRAM. As described below, the default for `device_working_mem_gb` is 3GB.
For example, if you have a 12GB GPU, the max safe value for `max_cache_vram_gb` is `12GB - 3GB = 9GB`.
If you had increased `device_working_mem_gb` to 4GB, then the max safe value for `max_cache_vram_gb` is `12GB - 4GB = 8GB`.
Most users who override `max_cache_vram_gb` are doing so because they wish to use significantly less VRAM, and should be setting `max_cache_vram_gb` to a value significantly less than the 'max safe value'.
:::
### Working memory
Invoke cannot use _all_ of your VRAM for model caching and loading. It requires some VRAM to use as working memory for various operations.
Invoke reserves 3GB VRAM as working memory by default, which is enough for most use-cases. However, it is possible to fine-tune this setting if you still get OOMs.
#### Fine-tuning working memory
You can increase the working memory size in `invokeai.yaml` to prevent OOMs:
```yaml
# The default is 3GB - bump it up to 4GB to prevent OOMs.
device_working_mem_gb: 4
```
:::tip[Operations may request more working memory]
For some operations, we can determine VRAM requirements in advance and allocate additional working memory to prevent OOMs.
VAE decoding is one such operation. This operation converts the generation process's output into an image. For large image outputs, this might use more than the default working memory size of 3GB.
During this decoding step, Invoke calculates how much VRAM will be required to decode and requests that much VRAM from the model manager. If the amount exceeds the working memory size, the model manager will offload cached model layers from VRAM until there's enough VRAM to decode.
Once decoding completes, the model manager "reclaims" the extra VRAM allocated as working memory for future model loading operations.
:::
### Keeping a RAM weight copy
Invoke has the option of keeping a RAM copy of all model weights, even when they are loaded onto the GPU. This optimization is _on_ by default, and enables faster model switching and LoRA patching. Disabling this feature will reduce the average RAM load while running Invoke (peak RAM likely won't change), at the cost of slower model switching and LoRA patching. If you have limited RAM, you can disable this optimization:
```yaml
# Set to false to reduce the average RAM usage at the cost of slower model switching and LoRA patching.
keep_ram_copy_of_weights: false
```
### Disabling Nvidia sysmem fallback (Windows only)
On Windows, Nvidia GPUs are able to use system RAM when their VRAM fills up via **sysmem fallback**. While it sounds like a good idea on the surface, in practice it causes massive slowdowns during generation.
It is strongly suggested to disable this feature:
- Open the **NVIDIA Control Panel** app.
- Expand **3D Settings** on the left panel.
- Click **Manage 3D Settings** in the left panel.
- Find **CUDA - Sysmem Fallback Policy** in the right panel and set it to **Prefer No Sysmem Fallback**.
![cuda-sysmem-fallback](./assets/cuda-sysmem-fallback.png)
:::tip[Invoke does the same thing, but better]
If the sysmem fallback feature sounds familiar, that's because Invoke's partial model loading strategy is conceptually very similar - use VRAM when there's room, else fall back to RAM.
Unfortunately, the Nvidia implementation is not optimized for applications like Invoke and does more harm than good.
:::
## Troubleshooting
### Windows page file
Invoke has high virtual memory (a.k.a. 'committed memory') requirements. This can cause issues on Windows if the page file size limits are hit. (See this issue for the technical details on why this happens: https://github.com/invoke-ai/InvokeAI/issues/7563).
If you run out of page file space, InvokeAI may crash. Often, these crashes will happen with one of the following errors:
- InvokeAI exits with Windows error code `3221225477`
- InvokeAI crashes without an error, but `eventvwr.msc` reveals an error with code `0xc0000005` (the hex equivalent of `3221225477`)
If you are running out of page file space, try the following solutions:
- Make sure that you have sufficient disk space for the page file to grow. Watch your disk usage as Invoke runs. If it climbs near 100% leading up to the crash, then this is very likely the source of the issue. Clear out some disk space to resolve the issue.
- Make sure that your page file is set to "System managed size" (this is the default) rather than a custom size. Under the "System managed size" policy, the page file will grow dynamically as needed.
@@ -0,0 +1,126 @@
---
title: Patchmatch
---
import { Tabs, TabItem, Steps } from '@astrojs/starlight/components'
PatchMatch is an algorithm used to infill images. It can greatly improve outpainting results. PyPatchMatch is a python wrapper around a C++ implementation of the algorithm.
It uses the image data around the target area as a reference to generate new image data of a similar character and quality.
## Why Use PatchMatch
In the context of image generation, "outpainting" refers to filling in a transparent area using AI-generated image data. But the AI can't generate without some initial data. We need to first fill in the transparent area with _something_.
The first step in "outpainting" then, is to fill in the transparent area with something. Generally, you get better results when that initial infill resembles the rest of the image.
Because PatchMatch generates image data so similar to the rest of the image, it works very well as the first step in outpainting, typically producing better results than other infill methods supported by Invoke (e.g. LaMA, cv2 infill, random tiles).
### Performance Caveat
PatchMatch is CPU-bound, and the amount of time it takes increases proportionally as the infill area increases. While the numbers certainly vary depending on system specs, you can expect a noticeable slowdown once you start infilling areas around 512x512 pixels. 1024x1024 pixels can take several seconds to infill.
## Installation
Unfortunately, installation can be somewhat challenging, as it requires some things that `pip` cannot install for you.
<Steps>
1. Ensure you have the necessary dependencies installed for your system (see below).
<Tabs syncKey="operatingSystem">
<TabItem label="Windows" icon="seti:windows">
You're in luck! On Windows platforms PyPatchMatch will install automatically on Windows systems with no extra intervention.
</TabItem>
<TabItem label="MacOS" icon="apple">
You need to have opencv installed so that pypatchmatch can be built:
```bash
brew install opencv
```
The next time you start `invoke`, after successfully installing opencv, pypatchmatch will be built.
</TabItem>
<TabItem label="Linux" icon="linux">
Prior to installing PyPatchMatch, you need to take the following steps:
<Tabs syncKey="linuxDistro">
<TabItem label="Debian">
<Steps>
1. Install the `build-essential` tools:
```sh
sudo apt update # Update package lists
sudo apt install build-essential
```
2. Install `opencv`:
```sh
sudo apt install python3-opencv libopencv-dev
```
3. Activate the environment you use for invokeai, either with `conda` or with a virtual environment.
</Steps>
</TabItem>
<TabItem label="Arch">
<Steps>
1. Install the `base-devel` package:
```sh
sudo pacman -Syu
sudo pacman -S --needed base-devel
```
2. Install `opencv`, `blas`, and required dependencies:
```sh
sudo pacman -S opencv blas fmt glew vtk hdf5
```
or for CUDA support
```sh
sudo pacman -S opencv-cuda blas fmt glew vtk hdf5
```
3. Fix the naming of the `opencv` package configuration file:
```sh
cd /usr/lib/pkgconfig/
ln -sf opencv4.pc opencv.pc
```
</Steps>
</TabItem>
</Tabs>
</TabItem>
</Tabs>
2. Install pypatchmatch:
```sh
pip install pypatchmatch
```
3. Confirm that pypatchmatch is installed. At the command-line prompt enter `python`, and then at the `>>>` line type `from patchmatch import patch_match`: It should look like the following:
```py
Python 3.12.3 (main, Aug 14 2025, 17:47:21) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from patchmatch import patch_match
Compiling and loading c extensions from "/home/lstein/Projects/InvokeAI/.invokeai-env/src/pypatchmatch/patchmatch".
rm -rf build/obj libpatchmatch.so
mkdir: created directory 'build/obj'
mkdir: created directory 'build/obj/csrc/'
[dep] csrc/masked_image.cpp ...
[dep] csrc/nnf.cpp ...
[dep] csrc/inpaint.cpp ...
[dep] csrc/pyinterface.cpp ...
[CC] csrc/pyinterface.cpp ...
[CC] csrc/inpaint.cpp ...
[CC] csrc/nnf.cpp ...
[CC] csrc/masked_image.cpp ...
[link] libpatchmatch.so ...
```
If you're not seeing any errors, you're ready to go!
</Steps>
@@ -0,0 +1,813 @@
---
title: Call Saved Workflow Architecture
---
## Goal
`CallSavedWorkflowInvocation` should become an engine-native workflow call boundary, not a frontend-only dynamic node
and not a compile-time graph inliner.
The long-term feature goal is:
- A parent workflow can call a saved workflow selected by ID.
- The call node redraws in the editor based on the selected workflow's exposed form fields.
- Parent values and inbound connections bind to those exposed fields as call arguments.
- Execution suspends at the call node, runs the selected workflow as a dependent workflow execution, captures explicit
return values, and then resumes the parent workflow.
- The architecture must work for Invoke frontend graphs and for externally submitted graphs that use the same node type.
This document records the current state, the target architecture, and the execution contract needed to continue
development later.
## Implementation Priority
Favor the architecturally correct design over the fastest implementation path.
The work may still proceed incrementally, but each increment should satisfy all of the following:
- testable in isolation
- compatible with the long-term architecture described here
- non-breaking to existing code and existing workflow execution behavior
Speed is not the primary goal for this phase. The primary goal is to move toward the durable design without introducing
throwaway execution semantics that would need to be unwound later.
## Current State
Implemented already in the branch:
- A real invocation exists: `call_saved_workflow`.
- A real return node exists: `workflow_return`.
- Named returns exist through `workflow_return_value`, `workflow_return`, and caller-side `workflow_return_get`.
- `workflow_return` accepts one key/value return member directly or a collected list of return members, then emits a
named `values: dict[str, Any]` map.
- Only one `workflow_return` node is allowed per workflow, enforced in both frontend validation and Python validation.
- The frontend provides a saved-workflow picker using a reusable `SavedWorkflowField` UI type.
- The node redraws dynamically based on the selected saved workflow's exposed form fields.
- Dynamic field values persist with the parent workflow.
- Compatible inbound edges are preserved when switching between workflows with matching exposed field identities and
compatible types.
- Incompatible or no-longer-exposed inbound edges are removed in the editor.
- Backend validation exists for `workflow_id` existence and access rights.
Implemented runtime scaffolding:
- `GraphExecutionState` now persists workflow-call runtime state:
- `workflow_call_stack`
- `workflow_call_history`
- `workflow_call_parent`
- `waiting_workflow_call`
- `waiting_workflow_call_execution`
- `waiting_workflow_call_child_session`
- `max_workflow_call_depth`
- Nested and recursive calls are represented by the stack, with a runtime depth cap of 4.
- Parent/child workflow-call identity is now explicit in runtime state:
- the parent tracks an active `WorkflowCallExecution` record while waiting
- completed and failed calls are preserved in `workflow_call_history`
- child sessions carry a `workflow_call_parent` reference back to the parent call relationship
- `GraphExecutionState.next()` returns no runnable node while the parent session is waiting on a child workflow call.
- `GraphExecutionState.is_complete()` stays false while waiting.
- `DefaultSessionRunner.run_node()` now treats `call_saved_workflow` as a call boundary instead of a normal executable
node.
- On boundary entry, the runner:
- validates the selected workflow
- builds a workflow call frame
- converts the saved workflow JSON into a backend `Graph`
- validates and applies parent call arguments to the child graph
- creates a child `GraphExecutionState`
- attaches that child session to the waiting parent session
- Workflow-call runtime responsibilities are now split:
- `WorkflowCallCoordinator` handles call-specific setup:
- build the child graph
- apply parent call arguments
- create the child `GraphExecutionState`
- suspend the parent and enqueue the child queue item
- `WorkflowCallQueueLifecycle` handles queue-visible parent/child lifecycle:
- run child queue items
- resume waiting parents after child success
- complete the parent call node with the child `workflow_return` values
- fail suspended parents after child failure and cascade that failure upward through parent call chains
- Child `SessionQueueItem` rows now carry explicit relationship metadata:
- `workflow_call_id`
- `parent_item_id`
- `parent_session_id`
- `root_item_id`
- `workflow_call_depth`
- this metadata is now used directly by queue-visible child execution and parent resume/failure handling
- The `session_queue` table now has matching durable columns for that relationship metadata:
- `workflow_call_id`
- `parent_item_id`
- `parent_session_id`
- `root_item_id`
- `workflow_call_depth`
- child workflow executions are now inserted as their own pending queue rows using those columns
- Parent queue items now enter a real `waiting` status while suspended on a child workflow execution.
- `_on_after_run_session()` no longer completes queue items whose sessions are incomplete but waiting.
- Dynamic call arguments now execute end-to-end in the current runner path:
- literal dynamic values are serialized into a hidden `workflow_inputs` payload on the parent node at graph-build time
- stale hidden `workflow_inputs` values from recalled graphs are ignored unless a matching current dynamic field
exists
- existing dynamic input values are preserved across refresh only while the exposed field type remains compatible; if
the selected child workflow changes the exposed field type at the same node/field path, the caller input resets to
the child workflow's current initial value
- connected dynamic values are accepted as special call-boundary edges and are resolved from parent results at runtime
- both are validated against the child workflow's exposed form interface before being applied to the child graph
- Queue lifecycle semantics now exist for workflow-call chains:
- parent queue items are suspended in `waiting` while a child queue row runs
- child success resumes the suspended parent and completes the parent call node with the child `workflow_return`
values
- child failure fails the suspended parent and cascades upward through any waiting parent chain
- canceling a parent cancels its descendant child chain
- canceling a child cancels the waiting parent chain upward
- canceling remaining siblings after a batched child failure also cancels descendants of those sibling rows
- deleting any queue row in a workflow-call chain deletes the full chain to avoid leaving orphaned parent or child
rows behind
- `cancel_all_except_current` and `delete_all_except_current` preserve the active queue item plus its workflow-call
ancestors and descendants; this also covers the handoff window where a parent is `waiting` and its child is still
`pending`; unrelated waiting chains are still canceled or deleted
- retry is root-oriented rather than child-oriented; child queue rows should not be directly retried from the UI
- the current UI policy is:
- child queue rows keep `Cancel`
- child queue rows hide `Retry`
- child queue-row creation is now fail-clean:
- if call-boundary setup fails after some child rows have already been inserted, those child rows are deleted before
the parent invocation is failed
- child queue-row fan-out is bounded by remaining queue capacity, not just the global queue-size setting:
- a workflow call that would exceed the remaining pending capacity now fails instead of silently truncating or
over-enqueuing child rows
- child insertion rechecks pending capacity in the same database transaction as the insert
Implemented conversion helper:
- `workflow_graph_builder.py` converts saved workflow JSON into an executable backend `Graph`.
- It currently supports the invocation-node subset needed for this feature.
- It flattens connector nodes and omits explicit destination field values when a connection exists, matching frontend
graph-build semantics.
- It now serves as the first explicit callable-workflow compatibility gate:
- the selected workflow must contain exactly one `workflow_return` node
- connected batch child inputs produced by ordinary non-generator upstream nodes still fail early with a clear
unsupported-feature error
- malformed batch input wiring, including multiple connected inputs to one batch field, is reported as
`unsupported_batch_input` compatibility rather than a generic unsupported-node failure
- child workflows that mix supported batch nodes with unrelated generator nodes are currently rejected with a clear
unsupported-feature error
- unsupported callees are rejected before any child queue row is created
- Compatibility metadata is now exposed through workflow library API responses:
- workflow list items and workflow detail responses include `call_saved_workflow_compatibility`
- workflow list items use structural generator-backed batch checks so list/picker rendering does not enumerate every
image in board-backed generators; workflow detail and runtime execution still resolve real generator values
- the saved-workflow picker uses that metadata to disable unsupported workflows before execution
- the picker still allows an already-selected unsupported workflow to render, with an explicit unsupported state and
a localized frontend message selected from the structured backend reason
- workflow library list items now surface an explicit unsupported badge and localized reason without blocking normal
workflow viewing or editing
What is still not implemented:
- connected batch child inputs whose batch values are produced by ordinary non-generator upstream nodes are still not
supported and must fail with a clear domain error
- child workflows that mix supported batch nodes with unrelated generator nodes are still not supported and must fail
with a clear domain error
- broader child-workflow compatibility coverage still needs to be expanded from real unsupported shapes rather than
trying to interpret every frontend-only workflow representation through the current graph-builder path
- the current workflow-call queue lifecycle is still implemented through dedicated workflow-call runtime classes rather
than a fully generalized parent/child scheduler model
Conclusion:
- the editor contract is largely in place
- the parent-side runtime call boundary is in place
- child execution, argument forwarding, explicit child return capture, suspended parent status, queue-visible child
rows, and upward failure cascade now work
- the remaining major runtime work is to harden and generalize the parent/child scheduler model rather than prove the
basic call boundary
## Architectural Direction
Use the architecture that is more likely to be kept long-term:
- `call_saved_workflow` is a call boundary.
- The parent graph does not inline the full child workflow into itself at queue time.
- Runtime execution pauses at the call node and creates a dependent child workflow execution.
- The child workflow receives arguments from the parent.
- The child workflow returns explicit outputs to the parent.
- The parent resumes once the child returns successfully.
This is preferred over full graph expansion because it:
- avoids execution-graph blowup
- preserves workflow boundaries
- matches the conceptual model of workflow reuse
- supports explicit return values
- keeps externally submitted graphs viable as long as they use the same node type and contract
## Non-Goals For The Next Phase
These should not be the first implementation target:
- full inline graph expansion of called workflows
- unlimited nested workflow call support
- automatic exposure of arbitrary internal child workflow state
- implicit output inference from arbitrary child nodes
## Execution Contract
### 1. Callable Interface
The callable interface of a saved workflow is defined by its saved workflow JSON.
Primary source:
- `workflow.form`
Fallback source for older workflows:
- `workflow.exposedFields`
Only fields exposed by the child workflow form are callable inputs. Internal child inputs that exist in the workflow
graph but are not exposed by the form are not part of the public call interface.
### 2. Input Arguments
`CallSavedWorkflowInvocation` exposes dynamic inputs in the editor based on the selected workflow's callable interface.
The saved-workflow picker sends typed search text to the workflow-list endpoint. This keeps large workflow libraries
discoverable even when the desired workflow has not already been loaded into the combobox pages.
Each dynamic input must have:
- a stable external handle name
- a type
- a default value if defined by the child workflow
- a user-facing label and description when available
Current fast-path identity is based on child `nodeId + fieldName`. That is acceptable short-term in the editor, but a
longer-term stable interface ID would be better if child workflows are frequently duplicated or refactored.
### 3. Input Binding At Runtime
At runtime, when the parent reaches `call_saved_workflow`:
- the engine resolves `workflow_id`
- the engine loads the selected child workflow record
- the engine reconstructs the callable interface from the saved workflow JSON
- the engine collects argument values from the parent node's dynamic inputs
- the engine starts a dependent child workflow execution using those arguments
Argument values may come from:
- parent literal field values
- resolved inbound connections into the call node's dynamic inputs
For batch-aware child workflows, the parent call boundary should still pass normal exposed form inputs. Batching should
emerge from the child workflow's own internal batch nodes or generators, not from a separate caller-side batch protocol.
### 4. Child Workflow Execution
The child workflow runs as its own dependent execution context, not as an inlined copy of the parent graph.
Desired semantics:
- parent execution pauses at the call node
- child execution runs with inherited context where appropriate
- child workflow finishes or fails
- parent resumes only if child execution succeeds
This implies the queue/session/runtime layer needs an explicit parent-child execution relationship.
Current limitation:
- the temporary `workflow_graph_builder.py` path still reconstructs only the ordinary invocation subset of child
workflows
- direct batch-special child workflows now bypass that path and use queue batch expansion instead
- generator-backed batch child workflows now bypass that path too when the batch is fed directly by a supported
generator node
- connected batch child inputs produced by ordinary non-generator upstream nodes are still not supported and should fail
early with a clear unsupported-feature error
- the current queue-visible child execution path still relies on `WorkflowCallCoordinator` to resume or fail parents
directly rather than a more general queue scheduler abstraction
- the current implementation is still an intermediate architecture step, but it is now materially closer to the intended
durable parent/child model than the earlier inline-runner path
### 4a. Queue Lifecycle Contract
The current queue-visible implementation uses the following lifecycle contract:
- root or parent queue items may enter `waiting` while suspended on a child workflow call
- child workflow executions are represented as real queue rows with explicit parent/child relationship metadata
- child completion resumes the suspended parent and returns control to normal queue execution
- child failure fails the suspended parent call node and cascades upward through any ancestor chain
- cancel operations are chain-aware:
- canceling a waiting parent cancels descendants
- canceling a child cancels waiting ancestors
- canceling batched siblings after one child fails includes nested descendants of those siblings
- bulk "all except current" actions preserve the active queue item and its parent/child chain, not just the single
`in_progress` row; a pending child with a waiting parent is treated as the active chain during processor handoff
- retry operations are root-aware:
- retrying a root queue item creates a new root execution
- retrying a child queue item should be normalized to the root by backend code
- retry and full-chain delete authorization is checked against the root queue item owner
- child queue rows should not expose direct retry affordances in the UI
- retry websocket delivery is owner-scoped; when an admin retries roots owned by multiple users, each non-admin user
must receive only the retry item ids for their own roots, while admins can still observe the full retried set
- workflow live-update sockets join workflow event rooms in both authenticated multiuser mode and unauthenticated
single-user mode; the frontend relies on those events to invalidate workflow library data and clear deleted saved
workflow selections; in single-user mode, workflow CRUD events emit only to the admin room to avoid duplicate delivery
to sockets that are also joined to `user:system`
- a public-to-private transition emits a schema-defined `workflow_access_revoked` event to shared-workflow subscribers;
non-owner, non-admin clients clear references to that workflow while owners and admins retain access
- the saved-workflow node picker queries owned/default workflows and public shared workflows separately, merges them by
workflow id, and fetches additional pages as the combobox menu reaches the end
- queue status events must preserve user isolation:
- `QueueItemStatusChangedEvent.queue_status` may keep global aggregate counts
- embedded current-item identifiers (`item_id`, `session_id`, `batch_id`) must only be present when the current
in-progress item belongs to the event owner, or when the status is being built for an admin/global context
- workflow-call child enqueue events use the same owner-aware redaction as ordinary status transitions, even though
they do not pass through `_set_queue_item_status`
This is now part of the intended user-facing contract, even though the orchestration still lives in
`WorkflowCallCoordinator`.
### 4b. Batch Child Workflows
The current implementation now supports direct batch-special child workflows for:
- `image_batch`
- `string_batch`
- `integer_batch`
- `float_batch`
It also supports generator-backed batch child workflows when those batch nodes are fed directly by:
- `integer_generator`
- `float_generator`
- `string_generator`
- `image_generator` using `image_generator_images_from_board`
Current semantics:
- batch-special nodes are removed from the executable child graph before ordinary graph validation
- supported generator nodes that feed those batch-special nodes are removed from the executable child graph as well
- their outgoing edges are converted into queue batch substitutions
- ungrouped batch nodes expand as a cartesian product
- grouped batch nodes zip by `batch_group_id`
- the workflow call creates one child queue row per expanded batch session
- supported generator value shapes are resolved into concrete batch items before queue batch expansion
- declared generator counts are rejected before resolution when they exceed remaining child capacity
- cartesian expansion size is computed arithmetically before session generation rather than by materializing the product
- batch outputs may feed a named `workflow_return_value.value` directly; each expanded child returns one value for that
key
- parent resume waits for all child rows tied to that workflow call
- parent return aggregation produces `values: dict[str, list[Any]]`, where each key maps to one value per child row
- all child rows in one batch call must return the same key set; mismatched keys fail the parent call clearly
- if any child row fails, remaining sibling child rows are canceled and the parent call fails
- generator-backed image batches must respect board access:
- the caller may expand images from a board they own
- admins may expand any board
- shared/public boards may be expanded by other users
- inaccessible private boards must fail before image expansion rather than leaking board contents across users
Current generator coverage:
- integer generators:
- arithmetic sequence
- linear distribution
- parse string
- seeded uniform random distribution
- float generators:
- arithmetic sequence
- linear distribution
- parse string
- seeded uniform random distribution
- string generators:
- parse string
- dynamic prompts combinatorial
- dynamic prompts random
- image generators:
- images from board
Still unsupported:
- connected batch inputs whose batch values are produced by non-generator upstream nodes
Plain-English summary:
1. The parent workflow reaches `call_saved_workflow`.
1. The parent pauses and enters `waiting`.
1. The child workflow is inspected before execution.
1. If the child contains supported batch inputs, that one call expands into multiple child executions instead of one.
1. Each expanded child execution becomes its own queue row.
1. Each child queue row keeps the substituted batch `field_values`, matching ordinary batch queue rows.
1. Those child queue rows run independently.
1. The parent does not resume until all child queue rows for that call have finished.
1. Each child execution produces its own named `workflow_return.values` map.
1. The parent aggregates those maps into `values: dict[str, list[Any]]`.
1. The `call_saved_workflow` node completes with that named values map, and the parent workflow continues.
Expansion rules:
- ungrouped batch inputs expand as a cartesian product
- batch inputs that share the same `batch_group_id` zip together by position
Example:
- ungrouped inputs `[1, 2]` and `[10, 20]` produce 4 child executions:
- `(1, 10)`
- `(1, 20)`
- `(2, 10)`
- `(2, 20)`
- grouped inputs `[1, 2, 3]` and `[10, 20, 30]` with the same `batch_group_id` produce 3 child executions:
- `(1, 10)`
- `(2, 20)`
- `(3, 30)`
### 4c. Tricky Areas
The following parts of the runtime contract are easy to misread and should stay explicit in both code and tests.
Waiting and resume:
- a parent queue row in `waiting` is suspended, not completed
- a parent resumes only after every child queue row tied to that workflow call has reached a terminal state
Return aggregation:
- each child queue row returns its own named `workflow_return.values`
- for batched calls, the parent call node output is `values: dict[str, list[Any]]`
- all child rows in one batched call must return the same key set so each returned list is row-aligned
- if one key should contain multiple images for a non-batch child, the child must collect those images into a single
list value before returning that key
Sibling failure behavior:
- if one child queue row in a batched workflow call fails, remaining sibling child rows for that same workflow call are
canceled
- if parent return aggregation rejects a completed child row, remaining sibling child rows for that same workflow call
are canceled
- after sibling cancelation, the parent call fails
- if that parent is itself a child of another workflow call, failure continues upward through the ancestor chain
Cancel behavior:
- canceling a waiting parent cancels descendant child rows
- canceling a child row cancels waiting ancestors
- cancelation should stay cancelation; it should not be rewritten into ordinary failure semantics
- startup recovery cancels any interrupted `in_progress` or `waiting` workflow-call chain, including pending
descendants, so a restart cannot leave a suspended parent waiting on a child row that will never report back
Retry behavior:
- retry is root-oriented
- child queue rows should not be directly retried from the UI
- backend retry of a child id should normalize to the root workflow call chain rather than create an isolated child-only
rerun
### 5. Return Values
Return values should be explicit.
Implemented model:
- `workflow_return` is the explicit return node for callable workflows
- the child workflow declares named return values through explicit `workflow_return_value` key/value return members
- each return member has a stable string key and a connected value
- when the workflow is run independently, the return node has no caller-visible effect
- when the workflow is run via `call_saved_workflow`, the named return map becomes the return value of the call
- `call_saved_workflow` exposes that named return map to the parent workflow
Only one workflow return node may exist per workflow. That rule is enforced in both the frontend editor and in Python
validation/runtime code.
Do not infer child outputs from arbitrary terminal nodes. That is too ambiguous and too brittle.
Named return contract:
- the called workflow builds return members with a dedicated key/value node
- `workflow_return` accepts one return member directly, or a collected list of return members when the workflow returns
multiple named values
- non-batch execution rejects duplicate return keys
- if a non-batch workflow needs to return multiple images under one key, the child workflow should collect those images
into one list value and return that list under the key
- the caller extracts a named return value with a companion caller-side extraction node
- missing keys should fail clearly unless the extraction node explicitly supports and receives a default value
Batch return aggregation:
- when a called workflow expands into multiple child queue rows, each child row produces its own named return map
- the parent aggregates those child maps as `dict[str, list[Any]]`
- each key maps to child values in child enqueue order, preserving positional correspondence with batch inputs even when
child executions complete out of order
- duplicate keys within a single child return map are still invalid; repeated keys across batch children are the normal
aggregation path
### 6. Error Propagation
If child execution fails:
- the call node fails
- the parent workflow fails unless a later design adds explicit error-handling semantics
For the first implementation, failure propagation should be simple and strict.
### 7. Access Control
Runtime must enforce the same access rules used elsewhere for saved workflows.
The caller may execute a child workflow only if it is allowed to access that saved workflow at runtime.
This matters even if the parent workflow was authored in a context where the child was once visible.
### 8. Recursion And Nesting
Nested and recursive `call_saved_workflow` execution should be allowed, but bounded.
Initial implementation should enforce:
- nested workflow calls are allowed
- recursive workflow calls are allowed
- maximum workflow call depth is capped at 4 call frames
- the depth cap is enforced at runtime, based on the active call stack, not by static validation alone
This allows legitimate recursive or conditionally terminating workflow structures while still preventing unbounded call
growth.
## Where The Runtime Work Belongs
The goal is to support externally submitted graphs, not only frontend-authored graphs. Therefore the authoritative
execution logic must live in Python.
Recommended high-level design:
- a backend `GraphExpander` or broader graph-preparation service may still exist as an abstraction point
- but for this feature, the preferred long-term runtime model is not full graph expansion
- instead, the runtime needs a call-execution mechanism in the Python execution stack
Relevant existing path:
- frontend builds and submits a graph and workflow payload
- backend receives the batch via session queue APIs
- session queue stores session state
- runtime executes through `GraphExecutionState`
Current insertion points already used:
- `DefaultSessionRunner.run_node()` detects `call_saved_workflow` and enters boundary state
- `GraphExecutionState` stores the waiting/call-stack state and attached child session
- `WorkflowCallCoordinator` currently establishes the call boundary and enqueues child workflow executions as real queue
rows
- `WorkflowCallQueueLifecycle` currently resumes or fails parents when those child rows complete
- child queue items already carry stable parent/child identifiers in both runtime objects and durable queue columns
Next runtime work still needed:
- keep `WorkflowCallQueueLifecycle` as the bounded workflow-call lifecycle component for this PR
- the current workflow-call feature is the only caller of parent/child queue semantics
- replacing it with a generalized queue dependency scheduler now would add regression risk without unlocking a
concrete user workflow
- revisit only if another feature needs dependent queue items, richer retry/cancel policies, or resumable waits
- if support expands beyond the currently supported direct and generator-backed batch shapes, route those new child
workflow execution shapes through machinery that can honor ordinary Invoke batch semantics
## Runtime Components
### CallSavedWorkflowRuntime
Call-specific runtime behavior currently lives in `WorkflowCallCoordinator` and `WorkflowCallQueueLifecycle`.
Responsibilities:
- load and validate the selected child workflow record
- validate runtime access rights
- extract callable inputs from the child workflow definition
- build child execution arguments from the parent node state
- launch dependent child queue rows
- collect declared returns
- map returned values back to the parent node outputs
### Workflow Return Node
The dedicated child-workflow return nodes are implemented. Responsibilities:
- define the return interface of the called workflow
- build named key/value return members with `workflow_return_value`
- accept either one named return member or a collected list of return members with `workflow_return`
- provide that named values map back to the parent call site when invoked through `call_saved_workflow`
- remain inert from a caller perspective when the workflow is run independently
- guarantee that only one such node exists per workflow
- behave as a normal node in the editor, with singularity enforced by both frontend and Python validation/runtime code
This should remain the canonical reusable return mechanism for any future subworkflow call behavior.
### Execution Relationship Tracking
Session/runtime state records:
- parent execution waiting on child execution
- child execution belonging to a parent node call site
- result propagation back to the parent
- strict failure propagation rules
### Workflow Return Value Flow
The workflow return value should not be persisted back into the saved workflow record and should not be derived from
frontend state.
The intended runtime flow is:
1. The child workflow computes named return members like ordinary node outputs.
1. The child workflow connects one return member directly to `workflow_return.values`, or collects multiple return
members and connects that list to `workflow_return.values`.
1. When the child reaches `workflow_return`, runtime captures the resolved named return map as the child workflow
result.
1. The child workflow result is stored in child execution state.
1. That result is handed back to the suspended parent call frame.
1. The parent `call_saved_workflow` node is completed with that returned named value map.
1. The parent graph resumes.
## Named Return Implementation Status
Named returns are implemented for backend invocation behavior, caller-side extraction, runtime propagation, and batch
aggregation. Remaining work is limited to incremental frontend UX cleanup and any future expansion of supported batch
shapes.
### Stage 1: Backend Return Contract
Status: implemented in backend invocation tests.
Goal:
- establish the named return data model and invocation primitives
Contract:
- `WorkflowReturnValueField` stores one `key: str` and one `value: Any`
- `workflow_return_value` creates a single `WorkflowReturnValueField` from a key and connected value
- `workflow_return` accepts either one `WorkflowReturnValueField` member or a list of `WorkflowReturnValueField` members
- `WorkflowReturnOutput` exposes `values: dict[str, Any]`
- duplicate keys in one non-batch `workflow_return` execution are invalid and must fail clearly
Tests first:
- `workflow_return_value` emits the requested key/value pair
- `workflow_return` emits a named value map from one or more return members
- duplicate keys in one `workflow_return` execution are rejected
- empty returns are valid only if that remains an intentional callable-workflow contract
### Stage 2: Caller-Side Extraction Primitive
Status: implemented in backend invocation tests.
Goal:
- let the calling workflow extract a named return value without relying on collection position
Contract:
- `workflow_return_get` accepts the named return map and a key
- `workflow_return_get` outputs the selected value as `Any`
- missing keys fail clearly unless a later version intentionally adds default-value support
Tests first:
- extracting an existing key returns the stored value
- extracting a missing key fails with a useful message
- extracted `Any` values can feed typed downstream nodes through the existing connection compatibility rules
### Stage 3: Runtime Propagation
Status: implemented in backend runtime tests.
Goal:
- carry named return maps through queue-visible child execution and parent resume
Contract:
- non-batch child execution returns `values: dict[str, Any]`
- `call_saved_workflow` exposes that map on its output
- failed child execution behavior is unchanged
- cancel/retry lifecycle behavior is unchanged
Tests first:
- a called workflow returning `{image: image_value}` completes the parent `call_saved_workflow` output with that key
- a caller-side extraction node can consume that output after parent resume
- missing or invalid `workflow_return` nodes still fail with the existing clear errors
### Stage 4: Batch Return Aggregation
Status: implemented in backend runtime tests.
Goal:
- define named returns for child workflows that expand into multiple queue rows
Contract:
- each child queue row produces one named return map
- the parent aggregates child maps as `dict[str, list[Any]]`
- each key maps to values returned by completed child rows for that key
- all child rows in one batch call must return the same key set
- repeated keys across child rows are expected
- duplicate keys within one child row remain invalid
- if a non-batch workflow wants multiple images under one key, it must collect those images into a single list value
before returning that key
Tests first:
- a batched child returning `{image: image_value}` from each child row produces `{image: [image_1, image_2, ...]}`
- sibling failure still cancels remaining siblings and fails the parent
- duplicate keys inside one child row are rejected rather than silently aggregated
### Stage 5: Frontend Schema, UI, And Docs
Status: mostly implemented. Schema/type generation includes the backend nodes and fields; editor connection coverage and
localized UI strings are in place for the current node wiring. Future UX cleanup should be driven by concrete user
testing rather than added as speculative work.
Goal:
- make named returns usable and visible in the editor
Contract:
- generated schema/types include the new return field, return-value node, and extraction node
- visible UI strings are localized through `en.json`
- `call_saved_workflow` exposes the named return map output
- users can wire that output to `workflow_return_get`
Tests first:
- frontend connection/type tests cover return-value collection wiring
- frontend connection/type tests cover wiring one `workflow_return_value.value` directly to `workflow_return.values`
- frontend connection/type tests cover `call_saved_workflow.values -> workflow_return_get.values`
- docs describe how a called workflow creates named returns and how a caller extracts them
## Frontend Responsibilities In The Long-Term Design
The frontend remains responsible for editor-time behavior:
- choosing the saved workflow
- redrawing dynamic inputs based on the child workflow callable interface
- persisting those dynamic fields and their values
- preserving compatible inbound edges when workflow selection changes
- clearing incompatible edges and invalid selections in a predictable way
- using backend compatibility metadata so unsupported saved workflows are not presented as callable choices
- compatibility analysis now tolerates required exposed caller inputs by synthesizing placeholder values for those
inputs during backend compatibility evaluation, so workflows that are valid once the caller supplies exposed values
are not disabled prematurely
Potential future optimization:
- add a backend endpoint that returns a normalized callable workflow interface
- this would let the frontend avoid re-parsing full saved workflow payloads to redraw the node
- it would also give the frontend a backend-authoritative interface hash for drift detection
## Tests Needed Going Forward
Already covered:
- workflow-call stack and waiting state on `GraphExecutionState`
- depth-limit enforcement
- waiting blocks scheduling
- parent sessions are not completed while waiting
- runner boundary entry for `call_saved_workflow`
- validation failures and depth-limit failures still follow normal node-error behavior
- child workflow JSON conversion to backend `Graph`
- child graph build failure does not leave the parent in a partial waiting state
- child `GraphExecutionState` is attached to the waiting parent session
- coordinator-owned child execution completes the parent queue item instead of leaving it stuck in `in_progress`
- literal and connected dynamic call arguments are applied to the child graph at runtime
- non-exposed dynamic call arguments are rejected at runtime
- child `workflow_return` output is captured and becomes the parent `call_saved_workflow` output
- named `workflow_return` values can be constructed, propagated to the parent, extracted by key, and batch-aggregated as
`dict[str, list[Any]]`
- child workflows without a `workflow_return` node fail cleanly when called
- child execution events now include stable workflow-call relationship metadata on the child `SessionQueueItem`
- parent-child resume and failure propagation through queue-visible child rows
- nested runtime execution with bounded stack depth
- direct and generator-backed batch-special child workflows through queue child-row expansion
- compatibility metadata for required exposed inputs, missing/multiple returns, supported named batch-return shapes, and
unsupported batch input wiring
Still needed in later increments:
- focused coverage for any newly supported batch or generator shape when its contract changes
- possible migration from dedicated workflow-call queue lifecycle handling to a more general scheduler or
queue-lifecycle model only if another feature needs reusable dependent queue items
## Recommended Immediate Next Step
The next incremental step should be:
- stop adding feature slices unless they close a concrete correctness gap or unlock a realistic user workflow
- stabilize the current branch with review, targeted test runs, and cleanup of stale design-doc language
- treat migration from `WorkflowCallQueueLifecycle` to a generalized parent/child queue lifecycle as a larger
architecture slice, not as small follow-on busywork
The current branch is at the point where:
- parent call-boundary state exists
- child execution state can be created from the selected saved workflow
- child execution, argument forwarding, explicit return propagation, suspended parent status, queue-visible child rows,
and upward failure cascade work through the current coordinator + queue path
- but long-term generalized parent/child scheduling semantics are still missing

Some files were not shown because too many files have changed in this diff Show More