diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b188aa951ea..f62b8c90f11 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,32 +1,30 @@ # continuous integration -/.github/workflows/ @lstein @blessedcoolant +/.github/workflows/ @lstein @blessedcoolant # documentation - anyone with write privileges can review /docs/ -/mkdocs.yml # nodes /invokeai/app/ @blessedcoolant @lstein @dunkeroni @JPPhoto # installation and configuration -/pyproject.toml @lstein @blessedcoolant +/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 +/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 +/invokeai/frontend @blessedcoolant @lstein @dunkeroni # generation, model management, postprocessing -/invokeai/backend @lstein @blessedcoolant @dunkeroni @JPPhoto @Pfannkuchensack +/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 - +/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 diff --git a/.gitignore b/.gitignore index 0353fc5f0bb..cc037f09abd 100644 --- a/.gitignore +++ b/.gitignore @@ -79,9 +79,6 @@ instance/ # Scrapy stuff: .scrapy -# Sphinx documentation -docs/_build/ - # PyBuilder .pybuilder/ target/ @@ -145,9 +142,6 @@ ENV/ # Rope project settings .ropeproject -# mkdocs documentation -/site - # mypy .mypy_cache/ .dmypy.json @@ -195,4 +189,4 @@ installer/InvokeAI-Installer/ .claude/ # Weblate configuration file -weblate.ini \ No newline at end of file +weblate.ini diff --git a/Makefile b/Makefile index 2c7d762f83e..cc8e720d3ff 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,6 @@ help: @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 "update-config-docstring Update the app's config docstring so mkdocs can autogenerate it correctly." @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" @@ -21,7 +20,10 @@ help: @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 Serve the mkdocs site with live reload" + @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: @@ -45,10 +47,6 @@ mypy-all: test: pytest ./tests -# Update config docstring -update-config-docstring: - python scripts/update_config_docstring.py - # Install the pnpm modules needed for the front end frontend-install: rm -rf invokeai/frontend/web/node_modules @@ -74,7 +72,7 @@ frontend-lint: pnpm lint:tsc && \ pnpm lint:dpdm && \ pnpm lint:eslint --fix && \ - pnpm lint:prettier --write + pnpm lint:prettier --write # Tag the release wheel: @@ -88,8 +86,16 @@ tag-release: openapi: python scripts/generate_openapi_schema.py -# Serve the mkdocs site w/ live reload -.PHONY: docs -docs: - cd docs && pnpm install && \ - pnpm run dev +# 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 diff --git a/docs-old/CODE_OF_CONDUCT.md b/docs-old/CODE_OF_CONDUCT.md deleted file mode 100644 index d68cdf98c83..00000000000 --- a/docs-old/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior -may be reported to the community leaders responsible for enforcement -at https://github.com/invoke-ai/InvokeAI/issues. All complaints will -be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/docs-old/RELEASE.md b/docs-old/RELEASE.md deleted file mode 100644 index db3e8c10bff..00000000000 --- a/docs-old/RELEASE.md +++ /dev/null @@ -1,154 +0,0 @@ -# Release Process - -The Invoke application is published as a python package on [PyPI]. This includes both a source distribution and built distribution (a wheel). - -Most users install it with the [Launcher](https://github.com/invoke-ai/launcher/), others with `pip`. - -The launcher uses GitHub as the source of truth for available releases. - -## Broad Strokes - -- Merge all changes and bump the version in the codebase. -- Tag the release commit. -- Wait for the release workflow to complete. -- Approve the PyPI publish jobs. -- Write GH release notes. - -## General Prep - -Make a developer call-out for PRs to merge. Merge and test things -out. Create a branch with a name like user/chore/vX.X.X-prep and bump the version by editing -`invokeai/version/invokeai_version.py` and commit locally. - -## Release Workflow - -The `release.yml` workflow runs a number of jobs to handle code checks, tests, build and publish on PyPI. - -It is triggered on **tag push**, when the tag matches `v*`. - -### Triggering the Workflow - -Ensure all commits that should be in the release are merged into this branch, and that you have pulled them locally. - -Run `make tag-release` to tag the current commit and kick off the workflow. You will be prompted to provide a message - use the version specifier. - -If this version's tag already exists for some reason (maybe you had to make a last minute change), the script will overwrite it. - -Push the commit to trigger the workflow. - -> In case you cannot use the Make target, the release may also be dispatched [manually] via GH. - -### Workflow Jobs and Process - -The workflow consists of a number of concurrently-run checks and tests, then two final publish jobs. - -The publish jobs require manual approval and are only run if the other jobs succeed. - -#### `check-version` Job - -This job ensures that the `invokeai` python package version specifier matches the tag for the release. The version specifier is pulled from the `__version__` variable in `invokeai/version/invokeai_version.py`. - -This job uses [samuelcolvin/check-python-version]. - -> Any valid [version specifier] works, so long as the tag matches the version. The release workflow works exactly the same for `RC`, `post`, `dev`, etc. - -#### Check and Test Jobs - -Next, these jobs run and must pass. They are the same jobs that are run for every PR. - -- **`python-tests`**: runs `pytest` on matrix of platforms -- **`python-checks`**: runs `ruff` (format and lint) -- **`frontend-tests`**: runs `vitest` -- **`frontend-checks`**: runs `prettier` (format), `eslint` (lint), `dpdm` (circular refs), `tsc` (static type check) and `knip` (unused imports) -- **`typegen-checks`**: ensures the frontend and backend types are synced - -#### `build-wheel` Job - -This sets up both python and frontend dependencies and builds the python package. Internally, this runs `./scripts/build_wheel.sh` and uploads `dist.zip`, which contains the wheel and unarchived build. - -You don't need to download or test these artifacts. - -#### Sanity Check & Smoke Test - -At this point, the release workflow pauses as the remaining publish jobs require approval. - -It's possible to test the python package before it gets published to PyPI. We've never had problems with it, so it's not necessary to do this. - -But, if you want to be extra-super careful, here's how to test it: - -- Download the `dist.zip` build artifact from the `build-wheel` job -- Unzip it and find the wheel file -- Create a fresh Invoke install by following the [manual install guide](https://invoke-ai.github.io/InvokeAI/installation/manual/) - but instead of installing from PyPI, install from the wheel -- Test the app - -##### Something isn't right - -If testing reveals any issues, no worries. Cancel the workflow, which will cancel the pending publish jobs (you didn't approve them prematurely, right?) and start over. - -#### PyPI Publish Jobs - -The publish jobs will not run if any of the previous jobs fail. - -They use [GitHub environments], which are configured as [trusted publishers] on PyPI. - -Both jobs require a @lstein or @blessedcoolant to approve them from the workflow's **Summary** tab. - -- Click the **Review deployments** button -- Select the environment (either `testpypi` or `pypi` - typically you select both) -- Click **Approve and deploy** - -> **If the version already exists on PyPI, the publish jobs will fail.** PyPI only allows a given version to be published once - you cannot change it. If version published on PyPI has a problem, you'll need to "fail forward" by bumping the app version and publishing a followup release. - -##### Failing PyPI Publish - -Check the [python infrastructure status page] for incidents. - -If there are no incidents, contact @lstein or @blessedcoolant, who have owner access to GH and PyPI, to see if access has expired or something like that. - -#### `publish-testpypi` Job - -Publishes the distribution on the [Test PyPI] index, using the `testpypi` GitHub environment. - -This job is not required for the production PyPI publish, but included just in case you want to test the PyPI release for some reason: - -- Approve this publish job without approving the prod publish -- Let it finish -- Create a fresh Invoke install by following the [manual install guide](https://invoke-ai.github.io/InvokeAI/installation/manual/), making sure to use the Test PyPI index URL: `https://test.pypi.org/simple/` -- Test the app - -#### `publish-pypi` Job - -Publishes the distribution on the production PyPI index, using the `pypi` GitHub environment. - -It's a good idea to wait to approve and run this job until you have the release notes ready! - -## Prep and publish the GitHub Release - -1. [Draft a new release] on GitHub, choosing the tag that triggered the release. -2. The **Generate release notes** button automatically inserts the changelog and new contributors. Make sure to select the correct tags for this release and the last stable release. GH often selects the wrong tags - do this manually. -3. Write the release notes, describing important changes. Contributions from community members should be shouted out. Use the GH-generated changelog to see all contributors. If there are Weblate translation updates, open that PR and shout out every person who contributed a translation. -4. Check **Set as a pre-release** if it's a pre-release. -5. Approve and wait for the `publish-pypi` job to finish if you haven't already. -6. Publish the GH release. -7. Post the release in Discord in the [releases](https://discord.com/channels/1020123559063990373/1149260708098359327) channel with abbreviated notes. For example: - > Invoke v5.7.0 (stable): - > - > It's a pretty big one - Form Builder, Metadata Nodes (thanks @SkunkWorxDark!), and much more. -8. Right click the message in releases and copy the link to it. Then, post that link in the [new-release-discussion](https://discord.com/channels/1020123559063990373/1149506274971631688) channel. For example: - > Invoke v5.7.0 (stable): - -## Manual Release - -The `release` workflow can be dispatched manually. You must dispatch the workflow from the right tag, else it will fail the version check. - -This functionality is available as a fallback in case something goes wonky. Typically, releases should be triggered via tag push as described above. - -[PyPI]: https://pypi.org/ -[Draft a new release]: https://github.com/invoke-ai/InvokeAI/releases/new -[Test PyPI]: https://test.pypi.org/ -[version specifier]: https://packaging.python.org/en/latest/specifications/version-specifiers/ -[GitHub environments]: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment -[trusted publishers]: https://docs.pypi.org/trusted-publishers/ -[samuelcolvin/check-python-version]: https://github.com/samuelcolvin/check-python-version -[manually]: #manual-release -[python infrastructure status page]: https://status.python.org/ diff --git a/docs-old/assets/Lincoln-and-Parrot-512-transparent.png b/docs-old/assets/Lincoln-and-Parrot-512-transparent.png deleted file mode 100755 index 363f3cced3f..00000000000 Binary files a/docs-old/assets/Lincoln-and-Parrot-512-transparent.png and /dev/null differ diff --git a/docs-old/assets/Lincoln-and-Parrot-512.png b/docs-old/assets/Lincoln-and-Parrot-512.png deleted file mode 100644 index acabe0f27c1..00000000000 Binary files a/docs-old/assets/Lincoln-and-Parrot-512.png and /dev/null differ diff --git a/docs-old/assets/canvas/biker_granny.png b/docs-old/assets/canvas/biker_granny.png deleted file mode 100644 index 70385014da8..00000000000 Binary files a/docs-old/assets/canvas/biker_granny.png and /dev/null differ diff --git a/docs-old/assets/canvas/biker_jacket_granny.png b/docs-old/assets/canvas/biker_jacket_granny.png deleted file mode 100644 index 3a46b8a49ce..00000000000 Binary files a/docs-old/assets/canvas/biker_jacket_granny.png and /dev/null differ diff --git a/docs-old/assets/canvas/mask_granny.png b/docs-old/assets/canvas/mask_granny.png deleted file mode 100644 index 041a0317c92..00000000000 Binary files a/docs-old/assets/canvas/mask_granny.png and /dev/null differ diff --git a/docs-old/assets/canvas/staging_area.png b/docs-old/assets/canvas/staging_area.png deleted file mode 100644 index 0e9d4ba0de9..00000000000 Binary files a/docs-old/assets/canvas/staging_area.png and /dev/null differ diff --git a/docs-old/assets/canvas_preview.png b/docs-old/assets/canvas_preview.png deleted file mode 100644 index dba4ee2ca29..00000000000 Binary files a/docs-old/assets/canvas_preview.png and /dev/null differ diff --git a/docs-old/assets/colab_notebook.png b/docs-old/assets/colab_notebook.png deleted file mode 100644 index 933664a86f7..00000000000 Binary files a/docs-old/assets/colab_notebook.png and /dev/null differ diff --git a/docs-old/assets/concepts/image1.png b/docs-old/assets/concepts/image1.png deleted file mode 100644 index f8c93efcf93..00000000000 Binary files a/docs-old/assets/concepts/image1.png and /dev/null differ diff --git a/docs-old/assets/concepts/image2.png b/docs-old/assets/concepts/image2.png deleted file mode 100644 index a22411492e7..00000000000 Binary files a/docs-old/assets/concepts/image2.png and /dev/null differ diff --git a/docs-old/assets/concepts/image3.png b/docs-old/assets/concepts/image3.png deleted file mode 100644 index e2213fc7079..00000000000 Binary files a/docs-old/assets/concepts/image3.png and /dev/null differ diff --git a/docs-old/assets/concepts/image4.png b/docs-old/assets/concepts/image4.png deleted file mode 100644 index 052479019c1..00000000000 Binary files a/docs-old/assets/concepts/image4.png and /dev/null differ diff --git a/docs-old/assets/concepts/image5.png b/docs-old/assets/concepts/image5.png deleted file mode 100644 index f3a4f764705..00000000000 Binary files a/docs-old/assets/concepts/image5.png and /dev/null differ diff --git a/docs-old/assets/contributing/html-detail.png b/docs-old/assets/contributing/html-detail.png deleted file mode 100644 index 055218002f7..00000000000 Binary files a/docs-old/assets/contributing/html-detail.png and /dev/null differ diff --git a/docs-old/assets/contributing/html-overview.png b/docs-old/assets/contributing/html-overview.png deleted file mode 100644 index 1f288fde118..00000000000 Binary files a/docs-old/assets/contributing/html-overview.png and /dev/null differ diff --git a/docs-old/assets/contributing/resize_invocation.png b/docs-old/assets/contributing/resize_invocation.png deleted file mode 100644 index a78f8eb86a3..00000000000 Binary files a/docs-old/assets/contributing/resize_invocation.png and /dev/null differ diff --git a/docs-old/assets/contributing/resize_node_editor.png b/docs-old/assets/contributing/resize_node_editor.png deleted file mode 100644 index d121ba1aa6d..00000000000 Binary files a/docs-old/assets/contributing/resize_node_editor.png and /dev/null differ diff --git a/docs-old/assets/control-panel-2.png b/docs-old/assets/control-panel-2.png deleted file mode 100644 index d7767d524a4..00000000000 Binary files a/docs-old/assets/control-panel-2.png and /dev/null differ diff --git a/docs-old/assets/dream-py-demo.png b/docs-old/assets/dream-py-demo.png deleted file mode 100644 index c6945bf07c9..00000000000 Binary files a/docs-old/assets/dream-py-demo.png and /dev/null differ diff --git a/docs-old/assets/dream_web_server.png b/docs-old/assets/dream_web_server.png deleted file mode 100644 index c8ec9756c59..00000000000 Binary files a/docs-old/assets/dream_web_server.png and /dev/null differ diff --git a/docs-old/assets/features/restoration-montage.png b/docs-old/assets/features/restoration-montage.png deleted file mode 100644 index 825a89a8dd2..00000000000 Binary files a/docs-old/assets/features/restoration-montage.png and /dev/null differ diff --git a/docs-old/assets/features/upscale-dialog.png b/docs-old/assets/features/upscale-dialog.png deleted file mode 100644 index fd91f90a65f..00000000000 Binary files a/docs-old/assets/features/upscale-dialog.png and /dev/null differ diff --git a/docs-old/assets/features/upscaling-montage.png b/docs-old/assets/features/upscaling-montage.png deleted file mode 100644 index 6b3eeba3473..00000000000 Binary files a/docs-old/assets/features/upscaling-montage.png and /dev/null differ diff --git a/docs-old/assets/gallery/board_settings.png b/docs-old/assets/gallery/board_settings.png deleted file mode 100644 index 44c4ef240bd..00000000000 Binary files a/docs-old/assets/gallery/board_settings.png and /dev/null differ diff --git a/docs-old/assets/gallery/board_tabs.png b/docs-old/assets/gallery/board_tabs.png deleted file mode 100644 index 23e5f8a91cf..00000000000 Binary files a/docs-old/assets/gallery/board_tabs.png and /dev/null differ diff --git a/docs-old/assets/gallery/board_thumbnails.png b/docs-old/assets/gallery/board_thumbnails.png deleted file mode 100644 index 1c739d48546..00000000000 Binary files a/docs-old/assets/gallery/board_thumbnails.png and /dev/null differ diff --git a/docs-old/assets/gallery/gallery.png b/docs-old/assets/gallery/gallery.png deleted file mode 100644 index 89f2dd1b463..00000000000 Binary files a/docs-old/assets/gallery/gallery.png and /dev/null differ diff --git a/docs-old/assets/gallery/image_menu.png b/docs-old/assets/gallery/image_menu.png deleted file mode 100644 index 2f10f280acf..00000000000 Binary files a/docs-old/assets/gallery/image_menu.png and /dev/null differ diff --git a/docs-old/assets/gallery/info_button.png b/docs-old/assets/gallery/info_button.png deleted file mode 100644 index 539cd6252e0..00000000000 Binary files a/docs-old/assets/gallery/info_button.png and /dev/null differ diff --git a/docs-old/assets/gallery/thumbnail_menu.png b/docs-old/assets/gallery/thumbnail_menu.png deleted file mode 100644 index a56caadbd8e..00000000000 Binary files a/docs-old/assets/gallery/thumbnail_menu.png and /dev/null differ diff --git a/docs-old/assets/gallery/top_controls.png b/docs-old/assets/gallery/top_controls.png deleted file mode 100644 index c5d3cdc854b..00000000000 Binary files a/docs-old/assets/gallery/top_controls.png and /dev/null differ diff --git a/docs-old/assets/img2img/000019.1592514025.png b/docs-old/assets/img2img/000019.1592514025.png deleted file mode 100644 index 2bc2d63ffa0..00000000000 Binary files a/docs-old/assets/img2img/000019.1592514025.png and /dev/null differ diff --git a/docs-old/assets/img2img/000019.steps.png b/docs-old/assets/img2img/000019.steps.png deleted file mode 100644 index 28899e91111..00000000000 Binary files a/docs-old/assets/img2img/000019.steps.png and /dev/null differ diff --git a/docs-old/assets/img2img/000030.1592514025.png b/docs-old/assets/img2img/000030.1592514025.png deleted file mode 100644 index 0e1641f7eb0..00000000000 Binary files a/docs-old/assets/img2img/000030.1592514025.png and /dev/null differ diff --git a/docs-old/assets/img2img/000030.step-0.png b/docs-old/assets/img2img/000030.step-0.png deleted file mode 100644 index 81beb074ec0..00000000000 Binary files a/docs-old/assets/img2img/000030.step-0.png and /dev/null differ diff --git a/docs-old/assets/img2img/000030.steps.gravity.png b/docs-old/assets/img2img/000030.steps.gravity.png deleted file mode 100644 index 2bda935a5f3..00000000000 Binary files a/docs-old/assets/img2img/000030.steps.gravity.png and /dev/null differ diff --git a/docs-old/assets/img2img/000032.1592514025.png b/docs-old/assets/img2img/000032.1592514025.png deleted file mode 100644 index 0ed2106ec48..00000000000 Binary files a/docs-old/assets/img2img/000032.1592514025.png and /dev/null differ diff --git a/docs-old/assets/img2img/000032.step-0.png b/docs-old/assets/img2img/000032.step-0.png deleted file mode 100644 index cc2da68ee43..00000000000 Binary files a/docs-old/assets/img2img/000032.step-0.png and /dev/null differ diff --git a/docs-old/assets/img2img/000032.steps.gravity.png b/docs-old/assets/img2img/000032.steps.gravity.png deleted file mode 100644 index 79058c1227a..00000000000 Binary files a/docs-old/assets/img2img/000032.steps.gravity.png and /dev/null differ diff --git a/docs-old/assets/img2img/000034.1592514025.png b/docs-old/assets/img2img/000034.1592514025.png deleted file mode 100644 index 43751da5728..00000000000 Binary files a/docs-old/assets/img2img/000034.1592514025.png and /dev/null differ diff --git a/docs-old/assets/img2img/000034.steps.png b/docs-old/assets/img2img/000034.steps.png deleted file mode 100644 index 216213162f5..00000000000 Binary files a/docs-old/assets/img2img/000034.steps.png and /dev/null differ diff --git a/docs-old/assets/img2img/000035.1592514025.png b/docs-old/assets/img2img/000035.1592514025.png deleted file mode 100644 index d298895080b..00000000000 Binary files a/docs-old/assets/img2img/000035.1592514025.png and /dev/null differ diff --git a/docs-old/assets/img2img/000035.steps.gravity.png b/docs-old/assets/img2img/000035.steps.gravity.png deleted file mode 100644 index 122c729e87c..00000000000 Binary files a/docs-old/assets/img2img/000035.steps.gravity.png and /dev/null differ diff --git a/docs-old/assets/img2img/000045.1592514025.png b/docs-old/assets/img2img/000045.1592514025.png deleted file mode 100644 index 5e70f1a5bfc..00000000000 Binary files a/docs-old/assets/img2img/000045.1592514025.png and /dev/null differ diff --git a/docs-old/assets/img2img/000045.steps.gravity.png b/docs-old/assets/img2img/000045.steps.gravity.png deleted file mode 100644 index 39e2a9b7111..00000000000 Binary files a/docs-old/assets/img2img/000045.steps.gravity.png and /dev/null differ diff --git a/docs-old/assets/img2img/000046.1592514025.png b/docs-old/assets/img2img/000046.1592514025.png deleted file mode 100644 index 70d248eb613..00000000000 Binary files a/docs-old/assets/img2img/000046.1592514025.png and /dev/null differ diff --git a/docs-old/assets/img2img/000046.steps.gravity.png b/docs-old/assets/img2img/000046.steps.gravity.png deleted file mode 100644 index d801a487015..00000000000 Binary files a/docs-old/assets/img2img/000046.steps.gravity.png and /dev/null differ diff --git a/docs-old/assets/img2img/fire-drawing.png b/docs-old/assets/img2img/fire-drawing.png deleted file mode 100644 index 36e2f111fa8..00000000000 Binary files a/docs-old/assets/img2img/fire-drawing.png and /dev/null differ diff --git a/docs-old/assets/inpainting/000019.curly.hair.deselected.png b/docs-old/assets/inpainting/000019.curly.hair.deselected.png deleted file mode 100644 index 54f2285550c..00000000000 Binary files a/docs-old/assets/inpainting/000019.curly.hair.deselected.png and /dev/null differ diff --git a/docs-old/assets/inpainting/000019.curly.hair.masked.png b/docs-old/assets/inpainting/000019.curly.hair.masked.png deleted file mode 100644 index a221c522f3e..00000000000 Binary files a/docs-old/assets/inpainting/000019.curly.hair.masked.png and /dev/null differ diff --git a/docs-old/assets/inpainting/000019.curly.hair.selected.png b/docs-old/assets/inpainting/000019.curly.hair.selected.png deleted file mode 100644 index e25bb4340c5..00000000000 Binary files a/docs-old/assets/inpainting/000019.curly.hair.selected.png and /dev/null differ diff --git a/docs-old/assets/inpainting/000024.801380492.png b/docs-old/assets/inpainting/000024.801380492.png deleted file mode 100644 index 9c72eb06b8b..00000000000 Binary files a/docs-old/assets/inpainting/000024.801380492.png and /dev/null differ diff --git a/docs-old/assets/installer-walkthrough/choose-gpu.png b/docs-old/assets/installer-walkthrough/choose-gpu.png deleted file mode 100644 index 3db23ef207b..00000000000 Binary files a/docs-old/assets/installer-walkthrough/choose-gpu.png and /dev/null differ diff --git a/docs-old/assets/installer-walkthrough/confirm-directory.png b/docs-old/assets/installer-walkthrough/confirm-directory.png deleted file mode 100644 index bc3099bbb35..00000000000 Binary files a/docs-old/assets/installer-walkthrough/confirm-directory.png and /dev/null differ diff --git a/docs-old/assets/installer-walkthrough/downloading-models.png b/docs-old/assets/installer-walkthrough/downloading-models.png deleted file mode 100644 index 975a6e39fe9..00000000000 Binary files a/docs-old/assets/installer-walkthrough/downloading-models.png and /dev/null differ diff --git a/docs-old/assets/installer-walkthrough/installing-models.png b/docs-old/assets/installer-walkthrough/installing-models.png deleted file mode 100644 index b2f6190bf5c..00000000000 Binary files a/docs-old/assets/installer-walkthrough/installing-models.png and /dev/null differ diff --git a/docs-old/assets/installer-walkthrough/settings-form.png b/docs-old/assets/installer-walkthrough/settings-form.png deleted file mode 100644 index 84e936ed56b..00000000000 Binary files a/docs-old/assets/installer-walkthrough/settings-form.png and /dev/null differ diff --git a/docs-old/assets/installer-walkthrough/unpacked-zipfile.png b/docs-old/assets/installer-walkthrough/unpacked-zipfile.png deleted file mode 100644 index 0e7e5f681f2..00000000000 Binary files a/docs-old/assets/installer-walkthrough/unpacked-zipfile.png and /dev/null differ diff --git a/docs-old/assets/installing-models/model-installer-controlnet.png b/docs-old/assets/installing-models/model-installer-controlnet.png deleted file mode 100644 index 09dfdb269fb..00000000000 Binary files a/docs-old/assets/installing-models/model-installer-controlnet.png and /dev/null differ diff --git a/docs-old/assets/installing-models/webui-models-1.png b/docs-old/assets/installing-models/webui-models-1.png deleted file mode 100644 index 648791ca270..00000000000 Binary files a/docs-old/assets/installing-models/webui-models-1.png and /dev/null differ diff --git a/docs-old/assets/installing-models/webui-models-2.png b/docs-old/assets/installing-models/webui-models-2.png deleted file mode 100644 index e58f5b9752c..00000000000 Binary files a/docs-old/assets/installing-models/webui-models-2.png and /dev/null differ diff --git a/docs-old/assets/installing-models/webui-models-3.png b/docs-old/assets/installing-models/webui-models-3.png deleted file mode 100644 index 6797a6b9076..00000000000 Binary files a/docs-old/assets/installing-models/webui-models-3.png and /dev/null differ diff --git a/docs-old/assets/installing-models/webui-models-4.png b/docs-old/assets/installing-models/webui-models-4.png deleted file mode 100644 index 75aa7ad7f16..00000000000 Binary files a/docs-old/assets/installing-models/webui-models-4.png and /dev/null differ diff --git a/docs-old/assets/invoke-control-panel-1.png b/docs-old/assets/invoke-control-panel-1.png deleted file mode 100644 index 09046676007..00000000000 Binary files a/docs-old/assets/invoke-control-panel-1.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-1.png b/docs-old/assets/invoke-web-server-1.png deleted file mode 100644 index e1cf27a2176..00000000000 Binary files a/docs-old/assets/invoke-web-server-1.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-2.png b/docs-old/assets/invoke-web-server-2.png deleted file mode 100644 index 361c113159c..00000000000 Binary files a/docs-old/assets/invoke-web-server-2.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-3.png b/docs-old/assets/invoke-web-server-3.png deleted file mode 100644 index 7d392cfecca..00000000000 Binary files a/docs-old/assets/invoke-web-server-3.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-4.png b/docs-old/assets/invoke-web-server-4.png deleted file mode 100644 index 2690356b8ac..00000000000 Binary files a/docs-old/assets/invoke-web-server-4.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-5.png b/docs-old/assets/invoke-web-server-5.png deleted file mode 100644 index 1923e641294..00000000000 Binary files a/docs-old/assets/invoke-web-server-5.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-6.png b/docs-old/assets/invoke-web-server-6.png deleted file mode 100644 index 0e8f703cb5a..00000000000 Binary files a/docs-old/assets/invoke-web-server-6.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-7.png b/docs-old/assets/invoke-web-server-7.png deleted file mode 100644 index 19769dea93e..00000000000 Binary files a/docs-old/assets/invoke-web-server-7.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-8.png b/docs-old/assets/invoke-web-server-8.png deleted file mode 100644 index dc708b11177..00000000000 Binary files a/docs-old/assets/invoke-web-server-8.png and /dev/null differ diff --git a/docs-old/assets/invoke-web-server-9.png b/docs-old/assets/invoke-web-server-9.png deleted file mode 100644 index 9d0d31bec0c..00000000000 Binary files a/docs-old/assets/invoke-web-server-9.png and /dev/null differ diff --git a/docs-old/assets/invoke_ai_banner.png b/docs-old/assets/invoke_ai_banner.png deleted file mode 100644 index 74d23f514d9..00000000000 Binary files a/docs-old/assets/invoke_ai_banner.png and /dev/null differ diff --git a/docs-old/assets/invoke_web_dark.png b/docs-old/assets/invoke_web_dark.png deleted file mode 100644 index 9141ab40f37..00000000000 Binary files a/docs-old/assets/invoke_web_dark.png and /dev/null differ diff --git a/docs-old/assets/invoke_web_light.png b/docs-old/assets/invoke_web_light.png deleted file mode 100644 index 98311ccafd6..00000000000 Binary files a/docs-old/assets/invoke_web_light.png and /dev/null differ diff --git a/docs-old/assets/invoke_web_server.png b/docs-old/assets/invoke_web_server.png deleted file mode 100644 index cdb34a0835b..00000000000 Binary files a/docs-old/assets/invoke_web_server.png and /dev/null differ diff --git a/docs-old/assets/join-us-on-discord-image.png b/docs-old/assets/join-us-on-discord-image.png deleted file mode 100644 index 53e4ee0fe05..00000000000 Binary files a/docs-old/assets/join-us-on-discord-image.png and /dev/null differ diff --git a/docs-old/assets/logo.png b/docs-old/assets/logo.png deleted file mode 100644 index b6eb33a6db5..00000000000 Binary files a/docs-old/assets/logo.png and /dev/null differ diff --git a/docs-old/assets/lora-example-0.png b/docs-old/assets/lora-example-0.png deleted file mode 100644 index f98fa53ca41..00000000000 Binary files a/docs-old/assets/lora-example-0.png and /dev/null differ diff --git a/docs-old/assets/lora-example-1.png b/docs-old/assets/lora-example-1.png deleted file mode 100644 index 29ea46e970b..00000000000 Binary files a/docs-old/assets/lora-example-1.png and /dev/null differ diff --git a/docs-old/assets/lora-example-2.png b/docs-old/assets/lora-example-2.png deleted file mode 100644 index 40eecdce849..00000000000 Binary files a/docs-old/assets/lora-example-2.png and /dev/null differ diff --git a/docs-old/assets/lora-example-3.png b/docs-old/assets/lora-example-3.png deleted file mode 100644 index be4c505d439..00000000000 Binary files a/docs-old/assets/lora-example-3.png and /dev/null differ diff --git a/docs-old/assets/multiuser/admin-add-user-1.png b/docs-old/assets/multiuser/admin-add-user-1.png deleted file mode 100644 index 706039d50cb..00000000000 Binary files a/docs-old/assets/multiuser/admin-add-user-1.png and /dev/null differ diff --git a/docs-old/assets/multiuser/admin-add-user-2.png b/docs-old/assets/multiuser/admin-add-user-2.png deleted file mode 100644 index 44bf2e180a3..00000000000 Binary files a/docs-old/assets/multiuser/admin-add-user-2.png and /dev/null differ diff --git a/docs-old/assets/multiuser/admin-add-user-3.png b/docs-old/assets/multiuser/admin-add-user-3.png deleted file mode 100644 index 708f9a85135..00000000000 Binary files a/docs-old/assets/multiuser/admin-add-user-3.png and /dev/null differ diff --git a/docs-old/assets/multiuser/admin-setup.png b/docs-old/assets/multiuser/admin-setup.png deleted file mode 100644 index fbe035e5c6e..00000000000 Binary files a/docs-old/assets/multiuser/admin-setup.png and /dev/null differ diff --git a/docs-old/assets/multiuser/user-login-1.png b/docs-old/assets/multiuser/user-login-1.png deleted file mode 100644 index 8c4bec22943..00000000000 Binary files a/docs-old/assets/multiuser/user-login-1.png and /dev/null differ diff --git a/docs-old/assets/negative_prompt_walkthru/step1.png b/docs-old/assets/negative_prompt_walkthru/step1.png deleted file mode 100644 index 6f94d7d035a..00000000000 Binary files a/docs-old/assets/negative_prompt_walkthru/step1.png and /dev/null differ diff --git a/docs-old/assets/negative_prompt_walkthru/step2.png b/docs-old/assets/negative_prompt_walkthru/step2.png deleted file mode 100644 index 0ff90eca3c1..00000000000 Binary files a/docs-old/assets/negative_prompt_walkthru/step2.png and /dev/null differ diff --git a/docs-old/assets/negative_prompt_walkthru/step3.png b/docs-old/assets/negative_prompt_walkthru/step3.png deleted file mode 100644 index f6676de3868..00000000000 Binary files a/docs-old/assets/negative_prompt_walkthru/step3.png and /dev/null differ diff --git a/docs-old/assets/negative_prompt_walkthru/step4.png b/docs-old/assets/negative_prompt_walkthru/step4.png deleted file mode 100644 index 2e73532629d..00000000000 Binary files a/docs-old/assets/negative_prompt_walkthru/step4.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupsallscale.png b/docs-old/assets/nodes/groupsallscale.png deleted file mode 100644 index 5a12fe9e131..00000000000 Binary files a/docs-old/assets/nodes/groupsallscale.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupsconditioning.png b/docs-old/assets/nodes/groupsconditioning.png deleted file mode 100644 index baaf2b44e0e..00000000000 Binary files a/docs-old/assets/nodes/groupsconditioning.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupscontrol.png b/docs-old/assets/nodes/groupscontrol.png deleted file mode 100644 index a38e4e4bbaa..00000000000 Binary files a/docs-old/assets/nodes/groupscontrol.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupsimgvae.png b/docs-old/assets/nodes/groupsimgvae.png deleted file mode 100644 index 03ac8d1f4aa..00000000000 Binary files a/docs-old/assets/nodes/groupsimgvae.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupsiterate.png b/docs-old/assets/nodes/groupsiterate.png deleted file mode 100644 index 50b762099a8..00000000000 Binary files a/docs-old/assets/nodes/groupsiterate.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupslora.png b/docs-old/assets/nodes/groupslora.png deleted file mode 100644 index 74ae8a70736..00000000000 Binary files a/docs-old/assets/nodes/groupslora.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupsmultigenseeding.png b/docs-old/assets/nodes/groupsmultigenseeding.png deleted file mode 100644 index dcd64c77581..00000000000 Binary files a/docs-old/assets/nodes/groupsmultigenseeding.png and /dev/null differ diff --git a/docs-old/assets/nodes/groupsnoise.png b/docs-old/assets/nodes/groupsnoise.png deleted file mode 100644 index d95b7ba3073..00000000000 Binary files a/docs-old/assets/nodes/groupsnoise.png and /dev/null differ diff --git a/docs-old/assets/nodes/linearview.png b/docs-old/assets/nodes/linearview.png deleted file mode 100644 index fb6b3efca0e..00000000000 Binary files a/docs-old/assets/nodes/linearview.png and /dev/null differ diff --git a/docs-old/assets/nodes/nodescontrol.png b/docs-old/assets/nodes/nodescontrol.png deleted file mode 100644 index 8b179e43acd..00000000000 Binary files a/docs-old/assets/nodes/nodescontrol.png and /dev/null differ diff --git a/docs-old/assets/nodes/nodesi2i.png b/docs-old/assets/nodes/nodesi2i.png deleted file mode 100644 index 99088338042..00000000000 Binary files a/docs-old/assets/nodes/nodesi2i.png and /dev/null differ diff --git a/docs-old/assets/nodes/nodest2i.png b/docs-old/assets/nodes/nodest2i.png deleted file mode 100644 index 7e882dbf1b6..00000000000 Binary files a/docs-old/assets/nodes/nodest2i.png and /dev/null differ diff --git a/docs-old/assets/nodes/workflow_library.png b/docs-old/assets/nodes/workflow_library.png deleted file mode 100644 index a17593d3b6b..00000000000 Binary files a/docs-old/assets/nodes/workflow_library.png and /dev/null differ diff --git a/docs-old/assets/outpainting/curly-outcrop-2.png b/docs-old/assets/outpainting/curly-outcrop-2.png deleted file mode 100644 index 595f011f27e..00000000000 Binary files a/docs-old/assets/outpainting/curly-outcrop-2.png and /dev/null differ diff --git a/docs-old/assets/outpainting/curly-outcrop.png b/docs-old/assets/outpainting/curly-outcrop.png deleted file mode 100644 index ae8d8dacd3d..00000000000 Binary files a/docs-old/assets/outpainting/curly-outcrop.png and /dev/null differ diff --git a/docs-old/assets/outpainting/curly-outpaint.png b/docs-old/assets/outpainting/curly-outpaint.png deleted file mode 100644 index 9f4a2ee431e..00000000000 Binary files a/docs-old/assets/outpainting/curly-outpaint.png and /dev/null differ diff --git a/docs-old/assets/outpainting/curly.png b/docs-old/assets/outpainting/curly.png deleted file mode 100644 index d9a4cb257ee..00000000000 Binary files a/docs-old/assets/outpainting/curly.png and /dev/null differ diff --git a/docs-old/assets/prompt-blending/blue-sphere-0.25-red-cube-0.75-hybrid.png b/docs-old/assets/prompt-blending/blue-sphere-0.25-red-cube-0.75-hybrid.png deleted file mode 100644 index 43d780a3439..00000000000 Binary files a/docs-old/assets/prompt-blending/blue-sphere-0.25-red-cube-0.75-hybrid.png and /dev/null differ diff --git a/docs-old/assets/prompt-blending/blue-sphere-0.5-red-cube-0.5-hybrid.png b/docs-old/assets/prompt-blending/blue-sphere-0.5-red-cube-0.5-hybrid.png deleted file mode 100644 index c131ae03f4d..00000000000 Binary files a/docs-old/assets/prompt-blending/blue-sphere-0.5-red-cube-0.5-hybrid.png and /dev/null differ diff --git a/docs-old/assets/prompt-blending/blue-sphere-0.5-red-cube-0.5.png b/docs-old/assets/prompt-blending/blue-sphere-0.5-red-cube-0.5.png deleted file mode 100644 index d0cb8e389ee..00000000000 Binary files a/docs-old/assets/prompt-blending/blue-sphere-0.5-red-cube-0.5.png and /dev/null differ diff --git a/docs-old/assets/prompt-blending/blue-sphere-0.75-red-cube-0.25-hybrid.png b/docs-old/assets/prompt-blending/blue-sphere-0.75-red-cube-0.25-hybrid.png deleted file mode 100644 index b7ddf6658bf..00000000000 Binary files a/docs-old/assets/prompt-blending/blue-sphere-0.75-red-cube-0.25-hybrid.png and /dev/null differ diff --git a/docs-old/assets/prompt-blending/blue-sphere-red-cube-hybrid.png b/docs-old/assets/prompt-blending/blue-sphere-red-cube-hybrid.png deleted file mode 100644 index 3ec14564e61..00000000000 Binary files a/docs-old/assets/prompt-blending/blue-sphere-red-cube-hybrid.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots--1.png b/docs-old/assets/prompt_syntax/apricots--1.png deleted file mode 100644 index 0f0c17f08b0..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots--1.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots--2.png b/docs-old/assets/prompt_syntax/apricots--2.png deleted file mode 100644 index 5c519b09aed..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots--2.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots--3.png b/docs-old/assets/prompt_syntax/apricots--3.png deleted file mode 100644 index c98ffd8b071..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots--3.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots-0.png b/docs-old/assets/prompt_syntax/apricots-0.png deleted file mode 100644 index f8ead74db2f..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots-0.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots-1.png b/docs-old/assets/prompt_syntax/apricots-1.png deleted file mode 100644 index 75ff7a24a3d..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots-1.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots-2.png b/docs-old/assets/prompt_syntax/apricots-2.png deleted file mode 100644 index e24ced76375..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots-2.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots-3.png b/docs-old/assets/prompt_syntax/apricots-3.png deleted file mode 100644 index d6edf5073c9..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots-3.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots-4.png b/docs-old/assets/prompt_syntax/apricots-4.png deleted file mode 100644 index 291c5a1b03b..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots-4.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/apricots-5.png b/docs-old/assets/prompt_syntax/apricots-5.png deleted file mode 100644 index c71b8578375..00000000000 Binary files a/docs-old/assets/prompt_syntax/apricots-5.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain-man.png b/docs-old/assets/prompt_syntax/mountain-man.png deleted file mode 100644 index 4bfa5817b8c..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain-man.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain-man1.png b/docs-old/assets/prompt_syntax/mountain-man1.png deleted file mode 100644 index 5ed98162d35..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain-man1.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain-man2.png b/docs-old/assets/prompt_syntax/mountain-man2.png deleted file mode 100644 index d4466514ded..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain-man2.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain-man3.png b/docs-old/assets/prompt_syntax/mountain-man3.png deleted file mode 100644 index 3196c5ca96f..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain-man3.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain-man4.png b/docs-old/assets/prompt_syntax/mountain-man4.png deleted file mode 100644 index 69522dba232..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain-man4.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain1-man.png b/docs-old/assets/prompt_syntax/mountain1-man.png deleted file mode 100644 index b5952d02f93..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain1-man.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain2-man.png b/docs-old/assets/prompt_syntax/mountain2-man.png deleted file mode 100644 index 8ab55ff2a7e..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain2-man.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/mountain3-man.png b/docs-old/assets/prompt_syntax/mountain3-man.png deleted file mode 100644 index c1024b0a635..00000000000 Binary files a/docs-old/assets/prompt_syntax/mountain3-man.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/sdxl-prompt-concatenated.png b/docs-old/assets/prompt_syntax/sdxl-prompt-concatenated.png deleted file mode 100644 index 8d5336da3d6..00000000000 Binary files a/docs-old/assets/prompt_syntax/sdxl-prompt-concatenated.png and /dev/null differ diff --git a/docs-old/assets/prompt_syntax/sdxl-prompt.png b/docs-old/assets/prompt_syntax/sdxl-prompt.png deleted file mode 100644 index b85464c5ad8..00000000000 Binary files a/docs-old/assets/prompt_syntax/sdxl-prompt.png and /dev/null differ diff --git a/docs-old/assets/sdxl-graphs/sdxl-base-example1.json b/docs-old/assets/sdxl-graphs/sdxl-base-example1.json deleted file mode 100644 index af162ba3e91..00000000000 --- a/docs-old/assets/sdxl-graphs/sdxl-base-example1.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes":[{"width":387,"height":565,"dragHandle":".node-drag-handle","id":"800b3166-5044-4987-ba13-f839963fec96","type":"invocation","position":{"x":-169.10829044927982,"y":-272.36451106154334},"data":{"id":"800b3166-5044-4987-ba13-f839963fec96","type":"sdxl_compel_prompt","inputs":{"prompt":{"id":"8e76febd-6692-4a40-8bba-cbac54bccf1a","name":"prompt","type":"string","value":"bluebird in a sakura tree"},"style":{"id":"cd0e7434-04d8-4e36-b08b-3342d6ab4caa","name":"style","type":"string","value":""},"original_width":{"id":"cd259354-c92c-4e1c-815a-205fb38f8fec","name":"original_width","type":"integer","value":1024},"original_height":{"id":"b5cebf0e-97ce-4cd3-92be-fad37472daf2","name":"original_height","type":"integer","value":1024},"crop_top":{"id":"e6763334-676e-42fd-9fbf-0537ff002013","name":"crop_top","type":"integer","value":0},"crop_left":{"id":"00042856-acd5-4f99-a0e1-2d0b567ebf0f","name":"crop_left","type":"integer","value":0},"target_width":{"id":"800e17aa-1c15-4cdc-bceb-1f3f3ef5d244","name":"target_width","type":"integer","value":1024},"target_height":{"id":"91079f90-886b-440f-8236-236a239af747","name":"target_height","type":"integer","value":1024},"clip":{"id":"b32c84f8-b39e-4c23-8a77-fc62944ef942","name":"clip","type":"clip"},"clip2":{"id":"80db777a-3e3d-4132-ba0b-75a0b2135809","name":"clip2","type":"clip"}},"outputs":{"conditioning":{"id":"ea368e7a-bc93-4492-8fc6-e1d2fe2f5e2a","name":"conditioning","type":"conditioning"}}},"selected":false,"positionAbsolute":{"x":-169.10829044927982,"y":-272.36451106154334},"dragging":false},{"width":387,"height":565,"dragHandle":".node-drag-handle","id":"f3b94b55-bd55-4244-87b2-bf92e4ebbd2a","type":"invocation","position":{"x":-173.7213091998297,"y":346.42482955878256},"data":{"id":"f3b94b55-bd55-4244-87b2-bf92e4ebbd2a","type":"sdxl_compel_prompt","inputs":{"prompt":{"id":"d718b007-51f9-4385-bbf9-fa99756d5a4c","name":"prompt","type":"string","value":""},"style":{"id":"6476aafe-9e17-48b2-b66c-fbb8ff555926","name":"style","type":"string","value":""},"original_width":{"id":"7f4cf0bd-d5ab-460f-9484-375160f87620","name":"original_width","type":"integer","value":1024},"original_height":{"id":"8c1695a7-2b1b-44ba-8644-da207c551366","name":"original_height","type":"integer","value":1024},"crop_top":{"id":"5e7e7434-e966-4592-a422-924784192719","name":"crop_top","type":"integer","value":0},"crop_left":{"id":"cbbbc129-c481-4a28-a944-ad894dcec8b1","name":"crop_left","type":"integer","value":0},"target_width":{"id":"18bb5fb9-d488-41c3-9d8b-1b0e5b163676","name":"target_width","type":"integer","value":1024},"target_height":{"id":"44cbc645-b582-48c7-9e37-8aabd2615355","name":"target_height","type":"integer","value":1024},"clip":{"id":"65f8cad6-9aaf-4d21-91af-e9aeb9657bf2","name":"clip","type":"clip"},"clip2":{"id":"db5cdf20-9968-4932-b7de-adbc2460ca31","name":"clip2","type":"clip"}},"outputs":{"conditioning":{"id":"4f0236e9-0bf1-4410-867d-7241f1fe3e4c","name":"conditioning","type":"conditioning"}}},"selected":false,"positionAbsolute":{"x":-173.7213091998297,"y":346.42482955878256},"dragging":false},{"width":334,"height":269,"dragHandle":".node-drag-handle","id":"e29a9d29-2f63-487b-89b8-4184b1871c8f","type":"invocation","position":{"x":-652.2340297939481,"y":338.43526096363286},"data":{"id":"e29a9d29-2f63-487b-89b8-4184b1871c8f","type":"sdxl_model_loader","inputs":{"model":{"id":"4b19dda6-31fe-4317-a741-a19f8d8ec18f","name":"model","type":"model","value":{"model_name":"stable-diffusion-xl-base-0.9","base_model":"sdxl"}}},"outputs":{"unet":{"id":"d816da3a-7cb9-4d0d-8b1a-cea4e596c11e","name":"unet","type":"unet"},"clip":{"id":"35af0240-3014-4a2e-b3f2-f79ddffaca29","name":"clip","type":"clip"},"clip2":{"id":"25360ee2-ce84-4d04-95d6-a6e63db7e87c","name":"clip2","type":"clip"},"vae":{"id":"e8f15338-cbbe-45a1-8248-059b3601b058","name":"vae","type":"vae"}}},"selected":false,"positionAbsolute":{"x":-652.2340297939481,"y":338.43526096363286},"dragging":false},{"width":384,"height":519,"dragHandle":".node-drag-handle","id":"41f3e341-a2ae-4741-b0cd-dea20abde273","type":"invocation","position":{"x":796.6378578223604,"y":-192.53063049405188},"data":{"id":"41f3e341-a2ae-4741-b0cd-dea20abde273","type":"t2l_sdxl","inputs":{"positive_conditioning":{"id":"2b679eb8-3d15-4b4c-9a68-334bf43ea3d3","name":"positive_conditioning","type":"conditioning"},"negative_conditioning":{"id":"d3cfcf4b-8b31-4f88-98ed-3486dd60fa6f","name":"negative_conditioning","type":"conditioning"},"noise":{"id":"ed624337-7042-4a28-adf1-c8a6def87b4c","name":"noise","type":"latents"},"steps":{"id":"92f299da-c6ec-45fc-ad3d-d6af0f9862e6","name":"steps","type":"integer","value":10},"cfg_scale":{"id":"b129fa4e-c772-48cd-9dad-9b7223ddfcf5","name":"cfg_scale","type":"float","value":7.5},"scheduler":{"id":"d5e86735-8301-42f4-8fc4-276120e444cd","name":"scheduler","type":"enum","value":"euler"},"unet":{"id":"9affc51f-37a9-4468-90c9-bec97efda3f4","name":"unet","type":"unet"},"denoising_end":{"id":"91f41083-910e-4955-b3ae-d93ad3d1d801","name":"denoising_end","type":"float","value":1}},"outputs":{"latents":{"id":"f6b119cc-cf8d-46c0-8f43-bf712c509938","name":"latents","type":"latents"},"width":{"id":"cfcced65-62b2-40c7-b2bf-6534220eeaa1","name":"width","type":"integer"},"height":{"id":"819fbf37-8db1-4f5c-ad6b-a5cdb71e8c9a","name":"height","type":"integer"}}},"selected":false,"positionAbsolute":{"x":796.6378578223604,"y":-192.53063049405188},"dragging":false},{"width":333,"height":344,"dragHandle":".node-drag-handle","id":"357773ee-386a-423a-a712-a23812554cbb","type":"invocation","position":{"x":401.8907147451813,"y":576.7882372082732},"data":{"id":"357773ee-386a-423a-a712-a23812554cbb","type":"noise","inputs":{"seed":{"id":"9eaa6f6f-bde4-42b6-945a-a09b6cc46531","name":"seed","type":"integer","value":10},"width":{"id":"241032d2-6d05-47c1-bf9e-1a13ef95956b","name":"width","type":"integer","value":1024},"height":{"id":"71eda9a5-4460-4e7e-ba7a-2d99fa47ddf5","name":"height","type":"integer","value":1024},"use_cpu":{"id":"1dd54a91-deaa-4b83-be89-230c517d7b6b","name":"use_cpu","type":"boolean","value":true}},"outputs":{"noise":{"id":"321604ef-b33b-4529-91af-0d8d03d23f2f","name":"noise","type":"latents"},"width":{"id":"fc1f68f3-720f-4dbe-a9c9-b8a08cca768a","name":"width","type":"integer"},"height":{"id":"61022078-48f5-4faf-923e-9cddb508be30","name":"height","type":"integer"}}},"selected":false,"positionAbsolute":{"x":401.8907147451813,"y":576.7882372082732},"dragging":false},{"width":250,"height":323,"dragHandle":".node-drag-handle","id":"3e712379-88e7-465d-94d1-e06160b7cfae","type":"invocation","position":{"x":1237.2519670580534,"y":257.58346014925485},"data":{"id":"3e712379-88e7-465d-94d1-e06160b7cfae","type":"l2i","inputs":{"latents":{"id":"453d1aac-7bfe-480b-bc6f-2c3b110cd9b8","name":"latents","type":"latents"},"vae":{"id":"c7c38097-3620-4eb8-aec5-4ce1b043bb83","name":"vae","type":"vae"},"tiled":{"id":"2cab559e-fbaa-4cbf-ae9b-e5e43df38369","name":"tiled","type":"boolean","value":false},"fp32":{"id":"daf6c8df-325a-4cf6-9e86-d8962d838f17","name":"fp32","type":"boolean","value":true}},"outputs":{"image":{"id":"6ead6575-d4ad-4dd2-b6e0-5b70f9cce726","name":"image","type":"image"},"width":{"id":"734a4436-2c6f-4c57-b1cb-25a01548031d","name":"width","type":"integer"},"height":{"id":"7a03a4af-ab06-43f3-bb4d-1789b6f2c5e9","name":"height","type":"integer"}}},"selected":false,"positionAbsolute":{"x":1237.2519670580534,"y":257.58346014925485},"dragging":false},{"width":320,"height":187,"dragHandle":".node-drag-handle","id":"2423e526-ba3c-448e-8240-7442508d4609","type":"invocation","position":{"x":-33.59691285742994,"y":945.2263448868741},"data":{"id":"2423e526-ba3c-448e-8240-7442508d4609","type":"rand_int","inputs":{"low":{"id":"70ffaec8-3064-45dc-8b5c-6b4f43cc3b62","name":"low","type":"integer","value":0},"high":{"id":"82e10321-ce31-4e86-9e21-077c16a18e3d","name":"high","type":"integer","value":2147483647}},"outputs":{"a":{"id":"bba34654-8a92-40db-9810-7f75ddad8ae9","name":"a","type":"integer"}}},"selected":true,"positionAbsolute":{"x":-33.59691285742994,"y":945.2263448868741},"dragging":false}],"edges":[{"source":"41f3e341-a2ae-4741-b0cd-dea20abde273","sourceHandle":"latents","target":"3e712379-88e7-465d-94d1-e06160b7cfae","targetHandle":"latents","id":"reactflow__edge-41f3e341-a2ae-4741-b0cd-dea20abde273latents-3e712379-88e7-465d-94d1-e06160b7cfaelatents"},{"source":"800b3166-5044-4987-ba13-f839963fec96","sourceHandle":"conditioning","target":"41f3e341-a2ae-4741-b0cd-dea20abde273","targetHandle":"positive_conditioning","id":"reactflow__edge-800b3166-5044-4987-ba13-f839963fec96conditioning-41f3e341-a2ae-4741-b0cd-dea20abde273positive_conditioning"},{"source":"f3b94b55-bd55-4244-87b2-bf92e4ebbd2a","sourceHandle":"conditioning","target":"41f3e341-a2ae-4741-b0cd-dea20abde273","targetHandle":"negative_conditioning","id":"reactflow__edge-f3b94b55-bd55-4244-87b2-bf92e4ebbd2aconditioning-41f3e341-a2ae-4741-b0cd-dea20abde273negative_conditioning"},{"source":"357773ee-386a-423a-a712-a23812554cbb","sourceHandle":"noise","target":"41f3e341-a2ae-4741-b0cd-dea20abde273","targetHandle":"noise","id":"reactflow__edge-357773ee-386a-423a-a712-a23812554cbbnoise-41f3e341-a2ae-4741-b0cd-dea20abde273noise"},{"source":"e29a9d29-2f63-487b-89b8-4184b1871c8f","sourceHandle":"vae","target":"3e712379-88e7-465d-94d1-e06160b7cfae","targetHandle":"vae","id":"reactflow__edge-e29a9d29-2f63-487b-89b8-4184b1871c8fvae-3e712379-88e7-465d-94d1-e06160b7cfaevae"},{"source":"e29a9d29-2f63-487b-89b8-4184b1871c8f","sourceHandle":"clip","target":"800b3166-5044-4987-ba13-f839963fec96","targetHandle":"clip","id":"reactflow__edge-e29a9d29-2f63-487b-89b8-4184b1871c8fclip-800b3166-5044-4987-ba13-f839963fec96clip"},{"source":"e29a9d29-2f63-487b-89b8-4184b1871c8f","sourceHandle":"clip2","target":"800b3166-5044-4987-ba13-f839963fec96","targetHandle":"clip2","id":"reactflow__edge-e29a9d29-2f63-487b-89b8-4184b1871c8fclip2-800b3166-5044-4987-ba13-f839963fec96clip2"},{"source":"e29a9d29-2f63-487b-89b8-4184b1871c8f","sourceHandle":"clip","target":"f3b94b55-bd55-4244-87b2-bf92e4ebbd2a","targetHandle":"clip","id":"reactflow__edge-e29a9d29-2f63-487b-89b8-4184b1871c8fclip-f3b94b55-bd55-4244-87b2-bf92e4ebbd2aclip"},{"source":"e29a9d29-2f63-487b-89b8-4184b1871c8f","sourceHandle":"clip2","target":"f3b94b55-bd55-4244-87b2-bf92e4ebbd2a","targetHandle":"clip2","id":"reactflow__edge-e29a9d29-2f63-487b-89b8-4184b1871c8fclip2-f3b94b55-bd55-4244-87b2-bf92e4ebbd2aclip2"},{"source":"e29a9d29-2f63-487b-89b8-4184b1871c8f","sourceHandle":"unet","target":"41f3e341-a2ae-4741-b0cd-dea20abde273","targetHandle":"unet","id":"reactflow__edge-e29a9d29-2f63-487b-89b8-4184b1871c8funet-41f3e341-a2ae-4741-b0cd-dea20abde273unet"},{"source":"2423e526-ba3c-448e-8240-7442508d4609","sourceHandle":"a","target":"357773ee-386a-423a-a712-a23812554cbb","targetHandle":"seed","id":"reactflow__edge-2423e526-ba3c-448e-8240-7442508d4609a-357773ee-386a-423a-a712-a23812554cbbseed"}],"viewport":{"x":473.83885376565604,"y":374.3473116493717,"zoom":0.5904963307147653}} \ No newline at end of file diff --git a/docs-old/assets/sdxl-graphs/sdxl-base-refine-example1.json b/docs-old/assets/sdxl-graphs/sdxl-base-refine-example1.json deleted file mode 100644 index 6935c15fd0d..00000000000 --- a/docs-old/assets/sdxl-graphs/sdxl-base-refine-example1.json +++ /dev/null @@ -1 +0,0 @@ -{"nodes":[{"width":309,"height":138,"dragHandle":".node-drag-handle","id":"2daddd1d-a468-4841-9ff4-57befb3518a1","type":"invocation","position":{"x":-352.03597532489465,"y":-853.3432520441677},"data":{"id":"2daddd1d-a468-4841-9ff4-57befb3518a1","type":"param_string","inputs":{"text":{"id":"aa4e1128-b6e0-4d8b-831b-723e9879b307","name":"text","type":"string","value":"bluebird in a sakura tree"}},"outputs":{"text":{"id":"8605a8bb-ea46-402d-a0cf-658ad66aa589","name":"text","type":"string"}}},"selected":false,"positionAbsolute":{"x":-352.03597532489465,"y":-853.3432520441677},"dragging":false},{"width":309,"height":138,"dragHandle":".node-drag-handle","id":"0cee1a14-34fa-4b5e-b361-9ebfaba62844","type":"invocation","position":{"x":-350.69241780355867,"y":-671.6645885606935},"data":{"id":"0cee1a14-34fa-4b5e-b361-9ebfaba62844","type":"param_string","inputs":{"text":{"id":"55a39e14-81ad-4112-94b3-62faba370317","name":"text","type":"string","value":"classical chinese painting"}},"outputs":{"text":{"id":"60a32b20-0e81-4936-bf68-38f65c13cfab","name":"text","type":"string"}}},"selected":false,"positionAbsolute":{"x":-350.69241780355867,"y":-671.6645885606935},"dragging":false},{"width":309,"height":138,"dragHandle":".node-drag-handle","id":"3898e2dd-0efa-44cf-8d6c-0d23ef184419","type":"invocation","position":{"x":-353.4581776953401,"y":-483.26349757856553},"data":{"id":"3898e2dd-0efa-44cf-8d6c-0d23ef184419","type":"param_string","inputs":{"text":{"id":"79c9244d-b40b-45f7-8351-a0e3aad9c203","name":"text","type":"string","value":""}},"outputs":{"text":{"id":"c49253f2-57d2-4ea8-920e-2d0107834c3a","name":"text","type":"string"}}},"selected":false,"positionAbsolute":{"x":-353.4581776953401,"y":-483.26349757856553},"dragging":false},{"width":309,"height":138,"dragHandle":".node-drag-handle","id":"96935e84-c864-48a4-b6c0-c940c71c43ae","type":"invocation","position":{"x":-355.15166832006526,"y":-312.2209444813352},"data":{"id":"96935e84-c864-48a4-b6c0-c940c71c43ae","type":"param_string","inputs":{"text":{"id":"a57a792a-3621-489e-bae4-0a413510caef","name":"text","type":"string","value":""}},"outputs":{"text":{"id":"40c229fc-b044-40cd-8b31-31f0562ca7ae","name":"text","type":"string"}}},"selected":false,"positionAbsolute":{"x":-355.15166832006526,"y":-312.2209444813352},"dragging":false},{"width":334,"height":269,"dragHandle":".node-drag-handle","id":"f3cd6592-f3a4-4d4e-a833-deb36314716f","type":"invocation","position":{"x":-405.6072492884527,"y":-90.55508106907858},"data":{"id":"f3cd6592-f3a4-4d4e-a833-deb36314716f","type":"sdxl_model_loader","inputs":{"model":{"id":"e54a5f62-7fb2-46ab-bec2-e1d1de26245b","name":"model","type":"model","value":{"model_name":"stable-diffusion-xl-base-0.9","base_model":"sdxl"}}},"outputs":{"unet":{"id":"59d159bd-42e0-40ee-8c5e-0021158d2f2e","name":"unet","type":"unet"},"clip":{"id":"232c0412-142b-4f61-b3f2-673e8375452c","name":"clip","type":"clip"},"clip2":{"id":"bfaaf2ed-d5fb-419f-8f4d-1c47375b23d9","name":"clip2","type":"clip"},"vae":{"id":"b7699621-12fd-4364-b68d-27fb322fcc2b","name":"vae","type":"vae"}}},"selected":false,"positionAbsolute":{"x":-405.6072492884527,"y":-90.55508106907858},"dragging":false},{"width":387,"height":565,"dragHandle":".node-drag-handle","id":"c1c4e1e7-ae79-4fc8-9a98-246175e7c155","type":"invocation","position":{"x":81.8860645633394,"y":-680.6110236371882},"data":{"id":"c1c4e1e7-ae79-4fc8-9a98-246175e7c155","type":"sdxl_compel_prompt","inputs":{"prompt":{"id":"9df5bf37-ff52-4deb-b8b2-59670578bd74","name":"prompt","type":"string","value":""},"style":{"id":"66b685eb-2c97-4dee-be34-fd10bda699fb","name":"style","type":"string","value":""},"original_width":{"id":"0dada1d1-305f-4b46-b342-e72150cf135d","name":"original_width","type":"integer","value":1024},"original_height":{"id":"fcbc471b-982e-4fed-9882-981d5490ab24","name":"original_height","type":"integer","value":1024},"crop_top":{"id":"7b891ead-37b4-4fcf-a642-1b27589ef8b8","name":"crop_top","type":"integer","value":0},"crop_left":{"id":"3d7d7f57-62af-42a6-ac55-80a5dcc6a6a1","name":"crop_left","type":"integer","value":0},"target_width":{"id":"e51a406e-185b-4447-82a9-752118d86ded","name":"target_width","type":"integer","value":1024},"target_height":{"id":"86aac99d-8671-4cdf-9f4b-fbd9261a1a43","name":"target_height","type":"integer","value":1024},"clip":{"id":"a6c1cdc5-ea91-4aaa-8b27-d5b0ea149d2c","name":"clip","type":"clip"},"clip2":{"id":"4af4c3c4-d23a-4940-8597-e1181b54e2b1","name":"clip2","type":"clip"}},"outputs":{"conditioning":{"id":"f8a844c4-8395-4645-87b8-7b8f02057302","name":"conditioning","type":"conditioning"}}},"selected":false,"positionAbsolute":{"x":81.8860645633394,"y":-680.6110236371882},"dragging":false},{"width":387,"height":565,"dragHandle":".node-drag-handle","id":"addd5054-97d8-466c-ab3c-41f5e039af02","type":"invocation","position":{"x":82.9601624843504,"y":-78.81640327238692},"data":{"id":"addd5054-97d8-466c-ab3c-41f5e039af02","type":"sdxl_compel_prompt","inputs":{"prompt":{"id":"d6eee212-c125-4c35-8028-a8c980496ee2","name":"prompt","type":"string","value":""},"style":{"id":"88c7d42e-5868-4e75-ab37-0b05a4cfc84d","name":"style","type":"string","value":""},"original_width":{"id":"21897c23-5bb2-46c0-b0e3-9e481c6828ab","name":"original_width","type":"integer","value":1024},"original_height":{"id":"70b41031-48b6-4c3b-9683-8332b344cc88","name":"original_height","type":"integer","value":1024},"crop_top":{"id":"a1e7aad9-1088-47c9-9956-eed7ae857a15","name":"crop_top","type":"integer","value":0},"crop_left":{"id":"9a237728-23ea-42fd-98c3-1b3ee571d8de","name":"crop_left","type":"integer","value":0},"target_width":{"id":"48ee0274-c952-4a8f-8b11-17c6cbad3546","name":"target_width","type":"integer","value":1024},"target_height":{"id":"4c75e60f-fd59-46f0-95dc-825a43fb329f","name":"target_height","type":"integer","value":1024},"clip":{"id":"1cb885c0-efc8-451c-9208-765f5c436950","name":"clip","type":"clip"},"clip2":{"id":"0b98f464-3f10-4dc3-aa14-22e25ac1a301","name":"clip2","type":"clip"}},"outputs":{"conditioning":{"id":"6cdd9112-92ec-4b34-9c10-c185f66d3d2a","name":"conditioning","type":"conditioning"}}},"selected":false,"positionAbsolute":{"x":82.9601624843504,"y":-78.81640327238692},"dragging":false},{"width":394,"height":425,"dragHandle":".node-drag-handle","id":"0af8fc93-be59-4002-9956-c700fb778d4a","type":"invocation","position":{"x":1100.1437566814066,"y":-851.6600425944865},"data":{"id":"0af8fc93-be59-4002-9956-c700fb778d4a","type":"sdxl_refiner_compel_prompt","inputs":{"style":{"id":"fda33c4f-84ef-4d82-bff1-b1b4b7c0f272","name":"style","type":"string","value":""},"original_width":{"id":"daf3c09d-d0bf-441f-ab05-5e8c16c8e136","name":"original_width","type":"integer","value":1024},"original_height":{"id":"d20c06c8-db5b-48db-92d9-79ee8ccd44f8","name":"original_height","type":"integer","value":1024},"crop_top":{"id":"29ad28e3-a497-4949-8ee5-ddb9645ca38f","name":"crop_top","type":"integer","value":0},"crop_left":{"id":"c4bdccc3-2f11-420a-abbe-2b4bd66a11c6","name":"crop_left","type":"integer","value":0},"aesthetic_score":{"id":"342061a0-eb9b-43fc-ac41-707b10355256","name":"aesthetic_score","type":"float","value":6},"clip2":{"id":"9fc7860d-aa7f-47fe-b8c9-1add0aee25bc","name":"clip2","type":"clip"}},"outputs":{"conditioning":{"id":"87cc45e3-839e-4a20-b93f-7892dbd53d75","name":"conditioning","type":"conditioning"}}},"selected":false,"positionAbsolute":{"x":1100.1437566814066,"y":-851.6600425944865},"dragging":false},{"width":334,"height":236,"dragHandle":".node-drag-handle","id":"d9e4c4e6-de63-4ee1-b2e7-ad11d874bcae","type":"invocation","position":{"x":542.9067581903546,"y":-677.6764639738383},"data":{"id":"d9e4c4e6-de63-4ee1-b2e7-ad11d874bcae","type":"sdxl_refiner_model_loader","inputs":{"model":{"id":"75b59470-6fac-48c6-a2c8-957ecb24abc9","name":"model","type":"model","value":{"model_name":"stable-diffusion-xl-refiner-0.9","base_model":"sdxl-refiner"}}},"outputs":{"unet":{"id":"8c78fcbd-a23d-4434-944c-5d1798156ae4","name":"unet","type":"unet"},"clip2":{"id":"9456e151-1b09-4853-9855-ad6c9f659720","name":"clip2","type":"clip"},"vae":{"id":"a86160bc-037c-4516-8e74-c6298e4d1f15","name":"vae","type":"vae"}}},"selected":true,"positionAbsolute":{"x":542.9067581903546,"y":-677.6764639738383},"dragging":false},{"width":394,"height":425,"dragHandle":".node-drag-handle","id":"c5b420c9-82cd-4863-b416-a620009f2d36","type":"invocation","position":{"x":1090.7741649651036,"y":-368.4448651589644},"data":{"id":"c5b420c9-82cd-4863-b416-a620009f2d36","type":"sdxl_refiner_compel_prompt","inputs":{"style":{"id":"90b996b2-e9f2-4b9b-acf9-d19804fe0228","name":"style","type":"string","value":""},"original_width":{"id":"7d511718-55c7-4c78-9bd5-89c6e293c1c2","name":"original_width","type":"integer","value":1024},"original_height":{"id":"1ec9ea13-ea31-4abd-af49-32d288313291","name":"original_height","type":"integer","value":1024},"crop_top":{"id":"ea1db4fd-521c-4439-9f57-d7d4f16175e7","name":"crop_top","type":"integer","value":0},"crop_left":{"id":"6e7af04e-0281-4f68-a393-1ec5b3af54c1","name":"crop_left","type":"integer","value":0},"aesthetic_score":{"id":"6c270052-bea0-4b08-a6b3-6601457f47bc","name":"aesthetic_score","type":"float","value":6},"clip2":{"id":"813f23b9-533e-4f65-82b3-c5d6cda09a22","name":"clip2","type":"clip"}},"outputs":{"conditioning":{"id":"46e6250e-daea-4d8d-ba32-d3405670cdae","name":"conditioning","type":"conditioning"}}},"selected":false,"positionAbsolute":{"x":1090.7741649651036,"y":-368.4448651589644},"dragging":false},{"width":333,"height":344,"dragHandle":".node-drag-handle","id":"fface4ab-25e3-4714-aca9-bb6c67842cd5","type":"invocation","position":{"x":572.6787481456306,"y":464.11370246551087},"data":{"id":"fface4ab-25e3-4714-aca9-bb6c67842cd5","type":"noise","inputs":{"seed":{"id":"c820a274-fb57-408d-b2b6-a1b1e2db4c39","name":"seed","type":"integer","value":0},"width":{"id":"533435f9-a82d-4841-8a17-33e6c766cd03","name":"width","type":"integer","value":1024},"height":{"id":"8d350b62-ed7a-484c-9cf2-ffe54a96465d","name":"height","type":"integer","value":1024},"use_cpu":{"id":"2cb494a6-5550-43da-802b-4809314615d4","name":"use_cpu","type":"boolean","value":true}},"outputs":{"noise":{"id":"e5a7a90f-656b-474a-b4cb-475d904e82c7","name":"noise","type":"latents"},"width":{"id":"e5b9de92-07f1-4dc5-95de-3096eca8db30","name":"width","type":"integer"},"height":{"id":"8489a4ce-41e9-4d69-bd8d-f0b862d84a3e","name":"height","type":"integer"}}},"selected":false,"positionAbsolute":{"x":572.6787481456306,"y":464.11370246551087},"dragging":false},{"width":384,"height":519,"dragHandle":".node-drag-handle","id":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","type":"invocation","position":{"x":1596.8574685369997,"y":277.26040215989804},"data":{"id":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","type":"t2l_sdxl","inputs":{"positive_conditioning":{"id":"77c4bda4-2d88-4d7c-bfc4-a0843a850109","name":"positive_conditioning","type":"conditioning"},"negative_conditioning":{"id":"4aad4196-2211-401a-82cf-0a66dc8b8374","name":"negative_conditioning","type":"conditioning"},"noise":{"id":"854bd0c1-a480-4c3a-a57a-0bce2d862c89","name":"noise","type":"latents"},"steps":{"id":"828a4cb3-746d-4f21-ab48-ff25b4f4b9f7","name":"steps","type":"integer","value":20},"cfg_scale":{"id":"77ec37ee-775b-49e9-8c6b-96dd81a8b561","name":"cfg_scale","type":"float","value":7.5},"scheduler":{"id":"12cd8ca4-b973-4875-a3e0-e17898ec514a","name":"scheduler","type":"enum","value":"euler"},"unet":{"id":"3465ea9c-f80b-46da-aa2d-6c550dcf4312","name":"unet","type":"unet"},"denoising_end":{"id":"717c3255-3aff-4eb5-9935-2f9b6794571e","name":"denoising_end","type":"float","value":1}},"outputs":{"latents":{"id":"eab9d5f1-aa6e-4ef5-87e6-1a5240e6638a","name":"latents","type":"latents"},"width":{"id":"957af5db-5c7d-4838-ae23-b4da184e4d92","name":"width","type":"integer"},"height":{"id":"337f2d41-78e7-4692-9f9e-7db102f97f8e","name":"height","type":"integer"}}},"selected":false,"positionAbsolute":{"x":1596.8574685369997,"y":277.26040215989804},"dragging":false},{"width":391,"height":610,"dragHandle":".node-drag-handle","id":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","type":"invocation","position":{"x":2044.2621231418175,"y":-374.36499568795915},"data":{"id":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","type":"l2l_sdxl","inputs":{"positive_conditioning":{"id":"561c26bd-5f7a-4a5b-83ef-a0380248a4b6","name":"positive_conditioning","type":"conditioning"},"negative_conditioning":{"id":"9f61e901-d8a7-4d61-ae69-ff00e8dba6da","name":"negative_conditioning","type":"conditioning"},"noise":{"id":"622b98db-33d9-495b-bc60-d0c1cf268573","name":"noise","type":"latents"},"steps":{"id":"4176611a-7071-4138-9a31-6ff5a7fae44a","name":"steps","type":"integer","value":20},"cfg_scale":{"id":"5223049f-672e-4782-8b75-d80ea4d6116b","name":"cfg_scale","type":"float","value":7.5},"scheduler":{"id":"0368e8b4-eafd-4efc-9e8f-4b0587977a62","name":"scheduler","type":"enum","value":"euler"},"unet":{"id":"f3a6510b-9e5d-4fbc-bcf8-f66d6ad13508","name":"unet","type":"unet"},"latents":{"id":"5aa7d118-7b02-4efd-8010-e72f422088b7","name":"latents","type":"latents"},"denoising_start":{"id":"1e12482b-4c8b-45f2-9f03-b6018e8db6fb","name":"denoising_start","type":"float","value":0.7},"denoising_end":{"id":"59d159d3-0839-41c6-a57f-a1bd4d655634","name":"denoising_end","type":"float","value":1}},"outputs":{"latents":{"id":"990cd073-3cd6-49d2-b27b-e6d783db56e1","name":"latents","type":"latents"},"width":{"id":"310659f0-5ee9-4093-ba93-83d8abdc879f","name":"width","type":"integer"},"height":{"id":"a613cafe-fdb4-4347-a231-e77b3e2ba975","name":"height","type":"integer"}}},"selected":false,"positionAbsolute":{"x":2044.2621231418175,"y":-374.36499568795915},"dragging":false},{"width":250,"height":323,"dragHandle":".node-drag-handle","id":"d1507d22-215b-4197-a893-c070f84c1eb2","type":"invocation","position":{"x":2564.7859493061546,"y":-740.6258381907046},"data":{"id":"d1507d22-215b-4197-a893-c070f84c1eb2","type":"l2i","inputs":{"latents":{"id":"206b269c-f27c-4ab9-9d97-80a26e6b5cc1","name":"latents","type":"latents"},"vae":{"id":"44e700f0-fe33-4d2d-ab74-3512f0625159","name":"vae","type":"vae"},"tiled":{"id":"0441b8f5-c6af-4461-8d9c-39abef4045f7","name":"tiled","type":"boolean","value":true},"fp32":{"id":"dcfa7f96-9d73-44cc-9cf5-418fd796d70c","name":"fp32","type":"boolean","value":true}},"outputs":{"image":{"id":"30c13692-27f8-4560-9ee8-188c4e4be266","name":"image","type":"image"},"width":{"id":"f12d0aee-6b61-4910-a4ef-14764a1dbaa3","name":"width","type":"integer"},"height":{"id":"3851e7e3-702a-4be9-9f51-176e0697c865","name":"height","type":"integer"}}},"selected":false,"positionAbsolute":{"x":2564.7859493061546,"y":-740.6258381907046},"dragging":false},{"width":320,"height":187,"dragHandle":".node-drag-handle","id":"1d7b5526-021f-4ade-bc7b-20173dd5e6f1","type":"invocation","position":{"x":110.88284993961776,"y":580.9044019817862},"data":{"id":"1d7b5526-021f-4ade-bc7b-20173dd5e6f1","type":"rand_int","inputs":{"low":{"id":"b05a55e0-ad8f-4629-9986-6ce27802c590","name":"low","type":"integer","value":0},"high":{"id":"4797e9b7-5d12-49e3-8f2c-64d80f203d42","name":"high","type":"integer","value":2147483647}},"outputs":{"a":{"id":"45fc7618-6803-4641-a976-36629852652d","name":"a","type":"integer"}}},"selected":false,"positionAbsolute":{"x":110.88284993961776,"y":580.9044019817862},"dragging":false},{"width":331,"height":138,"dragHandle":".node-drag-handle","id":"081ba10b-1fd0-4ad6-b4cc-b5aad5fec68e","type":"invocation","position":{"x":979.9163444732269,"y":267.53735270001346},"data":{"id":"081ba10b-1fd0-4ad6-b4cc-b5aad5fec68e","type":"param_float","inputs":{"param":{"id":"50bb0487-8138-415c-b3d4-54eedd70b980","name":"param","type":"float","value":0.7}},"outputs":{"param":{"id":"d3cf0272-d0fb-4085-bb52-9094352471a2","name":"param","type":"float"}}},"selected":false,"positionAbsolute":{"x":979.9163444732269,"y":267.53735270001346},"dragging":false}],"edges":[{"source":"2daddd1d-a468-4841-9ff4-57befb3518a1","sourceHandle":"text","target":"0af8fc93-be59-4002-9956-c700fb778d4a","targetHandle":"style","id":"reactflow__edge-2daddd1d-a468-4841-9ff4-57befb3518a1text-0af8fc93-be59-4002-9956-c700fb778d4astyle"},{"source":"2daddd1d-a468-4841-9ff4-57befb3518a1","sourceHandle":"text","target":"c1c4e1e7-ae79-4fc8-9a98-246175e7c155","targetHandle":"prompt","id":"reactflow__edge-2daddd1d-a468-4841-9ff4-57befb3518a1text-c1c4e1e7-ae79-4fc8-9a98-246175e7c155prompt"},{"source":"0cee1a14-34fa-4b5e-b361-9ebfaba62844","sourceHandle":"text","target":"c1c4e1e7-ae79-4fc8-9a98-246175e7c155","targetHandle":"style","id":"reactflow__edge-0cee1a14-34fa-4b5e-b361-9ebfaba62844text-c1c4e1e7-ae79-4fc8-9a98-246175e7c155style"},{"source":"3898e2dd-0efa-44cf-8d6c-0d23ef184419","sourceHandle":"text","target":"c5b420c9-82cd-4863-b416-a620009f2d36","targetHandle":"style","id":"reactflow__edge-3898e2dd-0efa-44cf-8d6c-0d23ef184419text-c5b420c9-82cd-4863-b416-a620009f2d36style"},{"source":"3898e2dd-0efa-44cf-8d6c-0d23ef184419","sourceHandle":"text","target":"addd5054-97d8-466c-ab3c-41f5e039af02","targetHandle":"prompt","id":"reactflow__edge-3898e2dd-0efa-44cf-8d6c-0d23ef184419text-addd5054-97d8-466c-ab3c-41f5e039af02prompt"},{"source":"96935e84-c864-48a4-b6c0-c940c71c43ae","sourceHandle":"text","target":"addd5054-97d8-466c-ab3c-41f5e039af02","targetHandle":"style","id":"reactflow__edge-96935e84-c864-48a4-b6c0-c940c71c43aetext-addd5054-97d8-466c-ab3c-41f5e039af02style"},{"source":"f3cd6592-f3a4-4d4e-a833-deb36314716f","sourceHandle":"unet","target":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","targetHandle":"unet","id":"reactflow__edge-f3cd6592-f3a4-4d4e-a833-deb36314716funet-0d025980-24fa-4b38-a5e6-e38c40ba23cfunet"},{"source":"f3cd6592-f3a4-4d4e-a833-deb36314716f","sourceHandle":"clip","target":"c1c4e1e7-ae79-4fc8-9a98-246175e7c155","targetHandle":"clip","id":"reactflow__edge-f3cd6592-f3a4-4d4e-a833-deb36314716fclip-c1c4e1e7-ae79-4fc8-9a98-246175e7c155clip"},{"source":"f3cd6592-f3a4-4d4e-a833-deb36314716f","sourceHandle":"clip","target":"addd5054-97d8-466c-ab3c-41f5e039af02","targetHandle":"clip","id":"reactflow__edge-f3cd6592-f3a4-4d4e-a833-deb36314716fclip-addd5054-97d8-466c-ab3c-41f5e039af02clip"},{"source":"f3cd6592-f3a4-4d4e-a833-deb36314716f","sourceHandle":"clip2","target":"c1c4e1e7-ae79-4fc8-9a98-246175e7c155","targetHandle":"clip2","id":"reactflow__edge-f3cd6592-f3a4-4d4e-a833-deb36314716fclip2-c1c4e1e7-ae79-4fc8-9a98-246175e7c155clip2"},{"source":"f3cd6592-f3a4-4d4e-a833-deb36314716f","sourceHandle":"clip2","target":"addd5054-97d8-466c-ab3c-41f5e039af02","targetHandle":"clip2","id":"reactflow__edge-f3cd6592-f3a4-4d4e-a833-deb36314716fclip2-addd5054-97d8-466c-ab3c-41f5e039af02clip2"},{"source":"c1c4e1e7-ae79-4fc8-9a98-246175e7c155","sourceHandle":"conditioning","target":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","targetHandle":"positive_conditioning","id":"reactflow__edge-c1c4e1e7-ae79-4fc8-9a98-246175e7c155conditioning-0d025980-24fa-4b38-a5e6-e38c40ba23cfpositive_conditioning"},{"source":"addd5054-97d8-466c-ab3c-41f5e039af02","sourceHandle":"conditioning","target":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","targetHandle":"negative_conditioning","id":"reactflow__edge-addd5054-97d8-466c-ab3c-41f5e039af02conditioning-0d025980-24fa-4b38-a5e6-e38c40ba23cfnegative_conditioning"},{"source":"fface4ab-25e3-4714-aca9-bb6c67842cd5","sourceHandle":"noise","target":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","targetHandle":"noise","id":"reactflow__edge-fface4ab-25e3-4714-aca9-bb6c67842cd5noise-0d025980-24fa-4b38-a5e6-e38c40ba23cfnoise"},{"source":"1d7b5526-021f-4ade-bc7b-20173dd5e6f1","sourceHandle":"a","target":"fface4ab-25e3-4714-aca9-bb6c67842cd5","targetHandle":"seed","id":"reactflow__edge-1d7b5526-021f-4ade-bc7b-20173dd5e6f1a-fface4ab-25e3-4714-aca9-bb6c67842cd5seed"},{"source":"d9e4c4e6-de63-4ee1-b2e7-ad11d874bcae","sourceHandle":"unet","target":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","targetHandle":"unet","id":"reactflow__edge-d9e4c4e6-de63-4ee1-b2e7-ad11d874bcaeunet-64fb34f7-6e0f-4fcf-80c3-9ca436c3623funet"},{"source":"0af8fc93-be59-4002-9956-c700fb778d4a","sourceHandle":"conditioning","target":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","targetHandle":"positive_conditioning","id":"reactflow__edge-0af8fc93-be59-4002-9956-c700fb778d4aconditioning-64fb34f7-6e0f-4fcf-80c3-9ca436c3623fpositive_conditioning"},{"source":"c5b420c9-82cd-4863-b416-a620009f2d36","sourceHandle":"conditioning","target":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","targetHandle":"negative_conditioning","id":"reactflow__edge-c5b420c9-82cd-4863-b416-a620009f2d36conditioning-64fb34f7-6e0f-4fcf-80c3-9ca436c3623fnegative_conditioning"},{"source":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","sourceHandle":"latents","target":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","targetHandle":"latents","id":"reactflow__edge-0d025980-24fa-4b38-a5e6-e38c40ba23cflatents-64fb34f7-6e0f-4fcf-80c3-9ca436c3623flatents"},{"source":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","sourceHandle":"latents","target":"d1507d22-215b-4197-a893-c070f84c1eb2","targetHandle":"latents","id":"reactflow__edge-64fb34f7-6e0f-4fcf-80c3-9ca436c3623flatents-d1507d22-215b-4197-a893-c070f84c1eb2latents"},{"source":"d9e4c4e6-de63-4ee1-b2e7-ad11d874bcae","sourceHandle":"vae","target":"d1507d22-215b-4197-a893-c070f84c1eb2","targetHandle":"vae","id":"reactflow__edge-d9e4c4e6-de63-4ee1-b2e7-ad11d874bcaevae-d1507d22-215b-4197-a893-c070f84c1eb2vae"},{"source":"d9e4c4e6-de63-4ee1-b2e7-ad11d874bcae","sourceHandle":"clip2","target":"0af8fc93-be59-4002-9956-c700fb778d4a","targetHandle":"clip2","id":"reactflow__edge-d9e4c4e6-de63-4ee1-b2e7-ad11d874bcaeclip2-0af8fc93-be59-4002-9956-c700fb778d4aclip2"},{"source":"d9e4c4e6-de63-4ee1-b2e7-ad11d874bcae","sourceHandle":"clip2","target":"c5b420c9-82cd-4863-b416-a620009f2d36","targetHandle":"clip2","id":"reactflow__edge-d9e4c4e6-de63-4ee1-b2e7-ad11d874bcaeclip2-c5b420c9-82cd-4863-b416-a620009f2d36clip2"},{"source":"081ba10b-1fd0-4ad6-b4cc-b5aad5fec68e","sourceHandle":"param","target":"0d025980-24fa-4b38-a5e6-e38c40ba23cf","targetHandle":"denoising_end","id":"reactflow__edge-081ba10b-1fd0-4ad6-b4cc-b5aad5fec68eparam-0d025980-24fa-4b38-a5e6-e38c40ba23cfdenoising_end"},{"source":"081ba10b-1fd0-4ad6-b4cc-b5aad5fec68e","sourceHandle":"param","target":"64fb34f7-6e0f-4fcf-80c3-9ca436c3623f","targetHandle":"denoising_start","id":"reactflow__edge-081ba10b-1fd0-4ad6-b4cc-b5aad5fec68eparam-64fb34f7-6e0f-4fcf-80c3-9ca436c3623fdenoising_start"}],"viewport":{"x":388.5523754198109,"y":521.3299012520417,"zoom":0.5336411821766158}} \ No newline at end of file diff --git a/docs-old/assets/send-to-icon.png b/docs-old/assets/send-to-icon.png deleted file mode 100644 index 6ff1c9065b9..00000000000 Binary files a/docs-old/assets/send-to-icon.png and /dev/null differ diff --git a/docs-old/assets/stable-samples/img2img/mountains-2.png b/docs-old/assets/stable-samples/img2img/mountains-2.png deleted file mode 100644 index e9f4e708535..00000000000 Binary files a/docs-old/assets/stable-samples/img2img/mountains-2.png and /dev/null differ diff --git a/docs-old/assets/stable-samples/img2img/mountains-3.png b/docs-old/assets/stable-samples/img2img/mountains-3.png deleted file mode 100644 index 017de3012c2..00000000000 Binary files a/docs-old/assets/stable-samples/img2img/mountains-3.png and /dev/null differ diff --git a/docs-old/assets/stable-samples/img2img/sketch-mountains-input.jpg b/docs-old/assets/stable-samples/img2img/sketch-mountains-input.jpg deleted file mode 100644 index 79d652b8003..00000000000 Binary files a/docs-old/assets/stable-samples/img2img/sketch-mountains-input.jpg and /dev/null differ diff --git a/docs-old/assets/stable-samples/txt2img/merged-0005.png b/docs-old/assets/stable-samples/txt2img/merged-0005.png deleted file mode 100644 index ca0a1af2065..00000000000 Binary files a/docs-old/assets/stable-samples/txt2img/merged-0005.png and /dev/null differ diff --git a/docs-old/assets/stable-samples/txt2img/merged-0006.png b/docs-old/assets/stable-samples/txt2img/merged-0006.png deleted file mode 100644 index 999f3703230..00000000000 Binary files a/docs-old/assets/stable-samples/txt2img/merged-0006.png and /dev/null differ diff --git a/docs-old/assets/stable-samples/txt2img/merged-0007.png b/docs-old/assets/stable-samples/txt2img/merged-0007.png deleted file mode 100644 index af390acaf60..00000000000 Binary files a/docs-old/assets/stable-samples/txt2img/merged-0007.png and /dev/null differ diff --git a/docs-old/assets/step1.png b/docs-old/assets/step1.png deleted file mode 100644 index 6309f41f206..00000000000 Binary files a/docs-old/assets/step1.png and /dev/null differ diff --git a/docs-old/assets/step2.png b/docs-old/assets/step2.png deleted file mode 100644 index 06027289b2e..00000000000 Binary files a/docs-old/assets/step2.png and /dev/null differ diff --git a/docs-old/assets/step4.png b/docs-old/assets/step4.png deleted file mode 100644 index c24db6b4702..00000000000 Binary files a/docs-old/assets/step4.png and /dev/null differ diff --git a/docs-old/assets/step5.png b/docs-old/assets/step5.png deleted file mode 100644 index b4e9b50576c..00000000000 Binary files a/docs-old/assets/step5.png and /dev/null differ diff --git a/docs-old/assets/step6.png b/docs-old/assets/step6.png deleted file mode 100644 index c43140c1aab..00000000000 Binary files a/docs-old/assets/step6.png and /dev/null differ diff --git a/docs-old/assets/step7.png b/docs-old/assets/step7.png deleted file mode 100644 index a575af28b22..00000000000 Binary files a/docs-old/assets/step7.png and /dev/null differ diff --git a/docs-old/assets/still-life-inpainted.png b/docs-old/assets/still-life-inpainted.png deleted file mode 100644 index ab8c7bd69a7..00000000000 Binary files a/docs-old/assets/still-life-inpainted.png and /dev/null differ diff --git a/docs-old/assets/still-life-scaled.jpg b/docs-old/assets/still-life-scaled.jpg deleted file mode 100644 index ba9c86be009..00000000000 Binary files a/docs-old/assets/still-life-scaled.jpg and /dev/null differ diff --git a/docs-old/assets/textual-inversion/ti-frontend.png b/docs-old/assets/textual-inversion/ti-frontend.png deleted file mode 100644 index 0500e9b1329..00000000000 Binary files a/docs-old/assets/textual-inversion/ti-frontend.png and /dev/null differ diff --git a/docs-old/assets/troubleshooting/broken-dependency.png b/docs-old/assets/troubleshooting/broken-dependency.png deleted file mode 100644 index 28415089204..00000000000 Binary files a/docs-old/assets/troubleshooting/broken-dependency.png and /dev/null differ diff --git a/docs-old/assets/truncation_comparison.jpg b/docs-old/assets/truncation_comparison.jpg deleted file mode 100644 index a39a804beb7..00000000000 Binary files a/docs-old/assets/truncation_comparison.jpg and /dev/null differ diff --git a/docs-old/assets/upscaling.png b/docs-old/assets/upscaling.png deleted file mode 100644 index e58a538e5ee..00000000000 Binary files a/docs-old/assets/upscaling.png and /dev/null differ diff --git a/docs-old/assets/v1-variants-scores.jpg b/docs-old/assets/v1-variants-scores.jpg deleted file mode 100644 index 9201b985d45..00000000000 Binary files a/docs-old/assets/v1-variants-scores.jpg and /dev/null differ diff --git a/docs-old/assets/variation_walkthru/000001.3357757885.png b/docs-old/assets/variation_walkthru/000001.3357757885.png deleted file mode 100644 index b9aa4a78edf..00000000000 Binary files a/docs-old/assets/variation_walkthru/000001.3357757885.png and /dev/null differ diff --git a/docs-old/assets/variation_walkthru/000002.1614299449.png b/docs-old/assets/variation_walkthru/000002.1614299449.png deleted file mode 100644 index 0db167ae6c1..00000000000 Binary files a/docs-old/assets/variation_walkthru/000002.1614299449.png and /dev/null differ diff --git a/docs-old/assets/variation_walkthru/000002.3647897225.png b/docs-old/assets/variation_walkthru/000002.3647897225.png deleted file mode 100644 index 7fe1f29227c..00000000000 Binary files a/docs-old/assets/variation_walkthru/000002.3647897225.png and /dev/null differ diff --git a/docs-old/assets/variation_walkthru/000003.1614299449.png b/docs-old/assets/variation_walkthru/000003.1614299449.png deleted file mode 100644 index b7f6ae76139..00000000000 Binary files a/docs-old/assets/variation_walkthru/000003.1614299449.png and /dev/null differ diff --git a/docs-old/assets/variation_walkthru/000004.3747154981.png b/docs-old/assets/variation_walkthru/000004.3747154981.png deleted file mode 100644 index e6ac5f3bc98..00000000000 Binary files a/docs-old/assets/variation_walkthru/000004.3747154981.png and /dev/null differ diff --git a/docs-old/configuration.md b/docs-old/configuration.md deleted file mode 100644 index 5d8749af6f1..00000000000 --- a/docs-old/configuration.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -title: Configuration ---- - -# :material-tune-variant: InvokeAI Configuration - -## Intro - -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`. - -InvokeAI searches for the root directory in this order: - -1. The `--root ` 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.3 - -# Put user settings here - see https://invoke-ai.github.io/InvokeAI/features/CONFIGURATION/: -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. - -### 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 - -Following the table are additional explanations for certain settings. - - -::: invokeai.app.services.config.config_default.InvokeAIAppConfig - options: - show_root_heading: false - members: false - show_docstring_description: false - show_category_heading: false - - -#### 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 "HuggingFace 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, all generated images are stored in a single flat directory (`outputs/images/`). This can become unwieldy with a large number of images. The `image_subfolder_strategy` setting lets you organize images into subfolders automatically. - -```yaml -image_subfolder_strategy: flat # default value -``` - -Available strategies: - -| Strategy | Example Path | Description | -|----------|-------------|-------------| -| `flat` | `outputs/images/abc123.png` | **Default.** All images in one directory (current behavior). | -| `date` | `outputs/images/2026/03/17/abc123.png` | Organized by creation date (YYYY/MM/DD). | -| `type` | `outputs/images/general/abc123.png` | Organized by image category (`general`, `intermediate`, `mask`, `control`, etc.). | -| `hash` | `outputs/images/ab/abc123.png` | Uses first 2 characters of the UUID as subfolder. Best for filesystem performance with very large collections (~256 evenly distributed subfolders). | - -!!! tip "Switching Strategies" - - You can switch between strategies at any time. Existing images remain in their original location — only newly generated images will use the new subfolder structure. This works because each image's subfolder path is stored in the database. - -!!! example "Example: Using date-based organization" - - ```yaml - image_subfolder_strategy: date - ``` - - New images will be saved as `outputs/images/2026/03/17/abc123.png`. Thumbnails mirror the same structure under `outputs/images/thumbnails/2026/03/17/abc123.webp`. - -#### 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. - -[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]: ./features/low-vram.md diff --git a/docs-old/contributing/ARCHITECTURE.md b/docs-old/contributing/ARCHITECTURE.md deleted file mode 100644 index f8d2e30166c..00000000000 --- a/docs-old/contributing/ARCHITECTURE.md +++ /dev/null @@ -1,93 +0,0 @@ -# Invoke.AI Architecture - -```mermaid -flowchart TB - - subgraph apps[Applications] - webui[WebUI] - cli[CLI] - - subgraph webapi[Web API] - api[HTTP API] - sio[Socket.IO] - end - - end - - subgraph invoke[Invoke] - direction LR - invoker - services - sessions - invocations - end - - subgraph core[AI Core] - Generate - end - - webui --> webapi - webapi --> invoke - cli --> invoke - - invoker --> services & sessions - invocations --> services - sessions --> invocations - - services --> core - - %% Styles - classDef sg fill:#5028C8,font-weight:bold,stroke-width:2,color:#fff,stroke:#14141A - classDef default stroke-width:2px,stroke:#F6B314,color:#fff,fill:#14141A - - class apps,webapi,invoke,core sg - -``` - -## Applications - -Applications are built on top of the invoke framework. They should construct `invoker` and then interact through it. They should avoid interacting directly with core code in order to support a variety of configurations. - -### Web UI - -The Web UI is built on top of an HTTP API built with [FastAPI](https://fastapi.tiangolo.com/) and [Socket.IO](https://socket.io/). The frontend code is found in `/invokeai/frontend` and the backend code is found in `/invokeai/app/api_app.py` and `/invokeai/app/api/`. The code is further organized as such: - -| Component | Description | -| --- | --- | -| api_app.py | Sets up the API app, annotates the OpenAPI spec with additional data, and runs the API | -| dependencies | Creates all invoker services and the invoker, and provides them to the API | -| events | An eventing system that could in the future be adapted to support horizontal scale-out | -| sockets | The Socket.IO interface - handles listening to and emitting session events (events are defined in the events service module) | -| routers | API definitions for different areas of API functionality | - -### CLI - -The CLI is built automatically from invocation metadata, and also supports invocation piping and auto-linking. Code is available in `/invokeai/frontend/cli`. - -## Invoke - -The Invoke framework provides the interface to the underlying AI systems and is built with flexibility and extensibility in mind. There are four major concepts: invoker, sessions, invocations, and services. - -### Invoker - -The invoker (`/invokeai/app/services/invoker.py`) is the primary interface through which applications interact with the framework. Its primary purpose is to create, manage, and invoke sessions. It also maintains two sets of services: -- **invocation services**, which are used by invocations to interact with core functionality. -- **invoker services**, which are used by the invoker to manage sessions and manage the invocation queue. - -### Sessions - -Invocations and links between them form a graph, which is maintained in a session. Sessions can be queued for invocation, which will execute their graph (either the next ready invocation, or all invocations). Sessions also maintain execution history for the graph (including storage of any outputs). An invocation may be added to a session at any time, and there is capability to add and entire graph at once, as well as to automatically link new invocations to previous invocations. Invocations can not be deleted or modified once added. - -The session graph does not support looping. This is left as an application problem to prevent additional complexity in the graph. - -### Invocations - -Invocations represent individual units of execution, with inputs and outputs. All invocations are located in `/invokeai/app/invocations`, and are all automatically discovered and made available in the applications. These are the primary way to expose new functionality in Invoke.AI, and the [implementation guide](INVOCATIONS.md) explains how to add new invocations. - -### Services - -Services provide invocations access AI Core functionality and other necessary functionality (e.g. image storage). These are available in `/invokeai/app/services`. As a general rule, new services should provide an interface as an abstract base class, and may provide a lightweight local implementation by default in their module. The goal for all services should be to enable the usage of different implementations (e.g. using cloud storage for image storage), but should not load any module dependencies unless that implementation has been used (i.e. don't import anything that won't be used, especially if it's expensive to import). - -## AI Core - -The AI Core is represented by the rest of the code base (i.e. the code outside of `/invokeai/app/`). diff --git a/docs-old/contributing/CANVAS_PROJECTS/CANVAS_PROJECTS.md b/docs-old/contributing/CANVAS_PROJECTS/CANVAS_PROJECTS.md deleted file mode 100644 index a05ef29492e..00000000000 --- a/docs-old/contributing/CANVAS_PROJECTS/CANVAS_PROJECTS.md +++ /dev/null @@ -1,205 +0,0 @@ -# Canvas Projects — Technical Documentation - -## Overview - -Canvas Projects provide a save/load mechanism for the entire canvas state. The feature serializes all canvas entities, generation parameters, reference images, and their associated image files into a ZIP-based `.invk` file. On load, it restores the full state, handling image deduplication and re-uploading as needed. - -## File Format - -The `.invk` file is a standard ZIP archive with the following structure: - -``` -project.invk -├── manifest.json -├── canvas_state.json -├── params.json -├── ref_images.json -├── loras.json -└── images/ - ├── {image_name_1}.png - ├── {image_name_2}.png - └── ... -``` - -### manifest.json - -Schema version and metadata. Validated on load with Zod. - -```json -{ - "version": 1, - "appVersion": "5.12.0", - "createdAt": "2026-02-26T12:00:00.000Z", - "name": "My Canvas Project" -} -``` - -| Field | Type | Description | -|---|---|---| -| `version` | `number` | Schema version, currently `1`. Used for migration logic on load. | -| `appVersion` | `string` | InvokeAI version that created the file. Informational only. | -| `createdAt` | `string` | ISO 8601 timestamp. | -| `name` | `string` | User-provided project name. Also used as the download filename. | - -### canvas_state.json - -The serialized canvas entity tree. Type: `CanvasProjectState`. - -```typescript -type CanvasProjectState = { - rasterLayers: CanvasRasterLayerState[]; - controlLayers: CanvasControlLayerState[]; - inpaintMasks: CanvasInpaintMaskState[]; - regionalGuidance: CanvasRegionalGuidanceState[]; - bbox: CanvasState['bbox']; - selectedEntityIdentifier: CanvasState['selectedEntityIdentifier']; - bookmarkedEntityIdentifier: CanvasState['bookmarkedEntityIdentifier']; -}; -``` - -Each entity contains its full state including all canvas objects (brush lines, eraser lines, rect shapes, images). Image objects reference files by `image_name` which correspond to files in the `images/` folder. - -### params.json - -The complete generation parameters state (`ParamsState`). Optional on load (older files may not have it). This includes all fields from the params Redux slice: - -- Prompts (positive, negative, prompt history) -- Core generation settings (seed, steps, CFG scale, guidance, scheduler, iterations) -- Model selections (main model, VAE, FLUX VAE, T5 encoder, CLIP embed models, refiner, Z-Image models, Klein models) -- Dimensions (width, height, aspect ratio) -- Img2img strength -- Infill settings (method, tile size, patchmatch downscale, color) -- Canvas coherence settings (mode, edge size, min denoise) -- Refiner parameters (steps, CFG scale, scheduler, aesthetic scores, start) -- FLUX-specific settings (scheduler, DyPE preset/scale/exponent) -- Z-Image-specific settings (scheduler, seed variance) -- Upscale settings (scheduler, CFG scale) -- Seamless tiling, mask blur, CLIP skip, VAE precision, CPU noise, color compensation - -### ref_images.json - -Global reference image entities (`RefImageState[]`). These are IP-Adapter / FLUX Redux configs with `CroppableImageWithDims` containing both original and cropped image references. Optional on load. - -### loras.json - -Array of LoRA configurations (`LoRA[]`). Each entry contains: - -```typescript -type LoRA = { - id: string; - isEnabled: boolean; - model: ModelIdentifierField; - weight: number; -}; -``` - -Optional on load. Like models, LoRA identifiers are stored as-is — if a LoRA is not installed when loading, the entry is restored but may not be usable. - -### images/ - -All image files referenced anywhere in the state. Keyed by their original `image_name`. On save, each image is fetched from the backend via `GET /api/v1/images/i/{name}/full` and stored as-is. - -## Key Source Files - -| File | Purpose | -|---|---| -| `features/controlLayers/util/canvasProjectFile.ts` | Types, constants, image name collection, remapping, existence checking | -| `features/controlLayers/hooks/useCanvasProjectSave.ts` | Save hook — collects Redux state, fetches images, builds ZIP | -| `features/controlLayers/hooks/useCanvasProjectLoad.ts` | Load hook — parses ZIP, deduplicates images, dispatches state | -| `features/controlLayers/components/SaveCanvasProjectDialog.tsx` | Save name dialog + `useSaveCanvasProjectWithDialog` hook | -| `features/controlLayers/components/LoadCanvasProjectConfirmationAlertDialog.tsx` | Load confirmation dialog + `useLoadCanvasProjectWithDialog` hook | -| `features/controlLayers/components/Toolbar/CanvasToolbarProjectMenuButton.tsx` | Toolbar dropdown UI | -| `features/controlLayers/store/canvasSlice.ts` | `canvasProjectRecalled` Redux action | - -## Save Flow - -1. User clicks "Save Canvas Project" → `SaveCanvasProjectDialog` opens asking for a project name -2. On confirm, `saveCanvasProject(name)` is called -3. Read Redux state via selectors: `selectCanvasSlice()`, `selectParamsSlice()`, `selectRefImagesSlice()`, `selectLoRAsSlice()` -4. Build `CanvasProjectState` from the canvas slice; use `paramsState` directly for params -5. Walk all entities to collect every `image_name` reference via `collectImageNames()`: - - `CanvasImageState.image.image_name` in layer/mask objects - - `CroppableImageWithDims.original.image.image_name` in global ref images - - `CroppableImageWithDims.crop.image.image_name` in cropped ref images - - `ImageWithDims.image_name` in regional guidance ref images -6. Fetch each image from the backend API -7. Build ZIP with JSZip: add `manifest.json` (including `name`), `canvas_state.json`, `params.json`, `ref_images.json`, and all images into `images/` -8. Sanitize the name for filesystem use and generate blob, trigger download as `{name}.invk` - -## Load Flow - -1. User selects `.invk` file → confirmation dialog opens -2. On confirm, parse ZIP with JSZip -3. Validate manifest version via Zod schema -4. Read `canvas_state.json`, `params.json` (optional), `ref_images.json` (optional) -5. Collect all `image_name` references from the loaded state -6. **Deduplicate images**: for each referenced image, check if it exists on the server via `getImageDTOSafe(image_name)` - - Already exists → skip (no upload) - - Missing → upload from ZIP via `uploadImage()`, record `oldName → newName` mapping -7. Remap all `image_name` values in the loaded state using the mapping (only for re-uploaded images whose names changed) -8. Dispatch Redux actions: - - `canvasProjectRecalled()` — restores all canvas entities, bbox, selected/bookmarked entity - - `refImagesRecalled()` — restores global reference images - - `paramsRecalled()` — replaces the entire params state in one action - - `loraAllDeleted()` + `loraRecalled()` — restores LoRAs -9. Show success/error toast - -## Image Name Collection & Remapping - -The `canvasProjectFile.ts` utility provides two parallel sets of functions: - -**Collection** (`collectImageNames`): Walks the entire state tree and returns a `Set` of all referenced `image_name` values. This is used by both save (to know which images to fetch) and load (to know which images to check/upload). - -**Remapping** (`remapCanvasState`, `remapRefImages`): Deep-clones state objects and replaces `image_name` values using a `Map` mapping. Only images that were re-uploaded with a different name are remapped. Images that already existed on the server are left unchanged. - -Both walk the same paths through the state tree: -- Layer/mask objects → `CanvasImageState.image.image_name` -- Regional guidance ref images → `ImageWithDims.image_name` -- Global ref images → `CroppableImageWithDims.original.image.image_name` and `.crop.image.image_name` - -## Extending the Format - -### Adding new optional data (non-breaking) - -Add a new JSON file to the ZIP. No version bump needed. - -1. **Save**: Add `zip.file('new_data.json', JSON.stringify(data))` in `useCanvasProjectSave.ts` -2. **Load**: Read with `zip.file('new_data.json')` in `useCanvasProjectLoad.ts` — check for `null` so older project files without it still load -3. **Dispatch**: Add the appropriate Redux action to restore the data - -### Adding new entity types with images - -1. Extend `CanvasProjectState` type in `canvasProjectFile.ts` -2. Add collection logic in `collectImageNames()` to walk the new entity's objects -3. Add remapping logic in `remapCanvasState()` to update image names -4. Include the new entity array in both save and load hooks -5. Handle it in the `canvasProjectRecalled` reducer in `canvasSlice.ts` - -### Breaking schema changes - -1. Bump `CANVAS_PROJECT_VERSION` in `canvasProjectFile.ts` -2. Update the Zod manifest schema: `version: z.union([z.literal(1), z.literal(2)])` -3. Add migration logic in the load hook: check version, transform v1 → v2 before dispatching - -## UI Architecture - -### Save dialog - -The save flow uses a **nanostore atom** (`$isOpen`) to control the `SaveCanvasProjectDialog`: - -1. `useSaveCanvasProjectWithDialog()` — returns a callback that sets `$isOpen` to `true` -2. `SaveCanvasProjectDialog` (singleton in `GlobalModalIsolator`) — renders an `AlertDialog` with a name input -3. On save → calls `saveCanvasProject(name)` and closes the dialog -4. On cancel → closes the dialog - -### Load dialog - -The load flow uses a **nanostore atom** (`$pendingFile`) to decouple the file dialog from the confirmation dialog: - -1. `useLoadCanvasProjectWithDialog()` — opens a programmatic file input (`document.createElement('input')`) -2. On file selection → sets `$pendingFile` atom -3. `LoadCanvasProjectConfirmationAlertDialog` (singleton in `GlobalModalIsolator`) — subscribes to `$pendingFile` via `useStore()` -4. On accept → calls `loadCanvasProject(file)` and clears the atom -5. On cancel → clears the atom - -The programmatic file input approach was chosen because the context menu component uses `isLazy: true`, which unmounts the DOM tree when the menu closes — a hidden `` element inside the menu would be destroyed before the file dialog returns. diff --git a/docs-old/contributing/DOWNLOAD_QUEUE.md b/docs-old/contributing/DOWNLOAD_QUEUE.md deleted file mode 100644 index 960180961e9..00000000000 --- a/docs-old/contributing/DOWNLOAD_QUEUE.md +++ /dev/null @@ -1,334 +0,0 @@ -# The InvokeAI Download Queue - -The DownloadQueueService provides a multithreaded parallel download -queue for arbitrary URLs, with queue prioritization, event handling, -and restart capabilities. - -## Simple Example - -``` -from invokeai.app.services.download import DownloadQueueService, TqdmProgress - -download_queue = DownloadQueueService() -for url in ['https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/assets/a-painting-of-a-fire.png?raw=true', - 'https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/assets/birdhouse.png?raw=true', - 'https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/assets/missing.png', - 'https://civitai.com/api/download/models/152309?type=Model&format=SafeTensor', - ]: - - # urls start downloading as soon as download() is called - download_queue.download(source=url, - dest='/tmp/downloads', - on_progress=TqdmProgress().update - ) - -download_queue.join() # wait for all downloads to finish -for job in download_queue.list_jobs(): - print(job.model_dump_json(exclude_none=True, indent=4),"\n") -``` - -Output: - -``` -{ - "source": "https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/assets/a-painting-of-a-fire.png?raw=true", - "dest": "/tmp/downloads", - "id": 0, - "priority": 10, - "status": "completed", - "download_path": "/tmp/downloads/a-painting-of-a-fire.png", - "job_started": "2023-12-04T05:34:41.742174", - "job_ended": "2023-12-04T05:34:42.592035", - "bytes": 666734, - "total_bytes": 666734 -} - -{ - "source": "https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/assets/birdhouse.png?raw=true", - "dest": "/tmp/downloads", - "id": 1, - "priority": 10, - "status": "completed", - "download_path": "/tmp/downloads/birdhouse.png", - "job_started": "2023-12-04T05:34:41.741975", - "job_ended": "2023-12-04T05:34:42.652841", - "bytes": 774949, - "total_bytes": 774949 -} - -{ - "source": "https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/assets/missing.png", - "dest": "/tmp/downloads", - "id": 2, - "priority": 10, - "status": "error", - "job_started": "2023-12-04T05:34:41.742079", - "job_ended": "2023-12-04T05:34:42.147625", - "bytes": 0, - "total_bytes": 0, - "error_type": "HTTPError(Not Found)", - "error": "Traceback (most recent call last):\n File \"/home/lstein/Projects/InvokeAI/invokeai/app/services/download/download_default.py\", line 182, in _download_next_item\n self._do_download(job)\n File \"/home/lstein/Projects/InvokeAI/invokeai/app/services/download/download_default.py\", line 206, in _do_download\n raise HTTPError(resp.reason)\nrequests.exceptions.HTTPError: Not Found\n" -} - -{ - "source": "https://civitai.com/api/download/models/152309?type=Model&format=SafeTensor", - "dest": "/tmp/downloads", - "id": 3, - "priority": 10, - "status": "completed", - "download_path": "/tmp/downloads/xl_more_art-full_v1.safetensors", - "job_started": "2023-12-04T05:34:42.147645", - "job_ended": "2023-12-04T05:34:43.735990", - "bytes": 719020768, - "total_bytes": 719020768 -} -``` - -## The API - -The default download queue is `DownloadQueueService`, an -implementation of ABC `DownloadQueueServiceBase`. It juggles multiple -background download requests and provides facilities for interrogating -and cancelling the requests. Access to a current or past download task -is mediated via `DownloadJob` objects which report the current status -of a job request - -### The Queue Object - -A default download queue is located in -`ApiDependencies.invoker.services.download_queue`. However, you can -create additional instances if you need to isolate your queue from the -main one. - -``` -queue = DownloadQueueService(event_bus=events) -``` - -`DownloadQueueService()` takes three optional arguments: - -| **Argument** | **Type** | **Default** | **Description** | -|----------------|-----------------|---------------|-----------------| -| `max_parallel_dl` | int | 5 | Maximum number of simultaneous downloads allowed | -| `event_bus` | EventServiceBase | None | System-wide FastAPI event bus for reporting download events | -| `requests_session` | requests.sessions.Session | None | An alternative requests Session object to use for the download | - -`max_parallel_dl` specifies how many download jobs are allowed to run -simultaneously. Each will run in a different thread of execution. - -`event_bus` is an EventServiceBase, typically the one created at -InvokeAI startup. If present, download events are periodically emitted -on this bus to allow clients to follow download progress. - -`requests_session` is a url library requests Session object. It is -used for testing. - -### The Job object - -The queue operates on a series of download job objects. These objects -specify the source and destination of the download, and keep track of -the progress of the download. - -Two job types are defined. `DownloadJob` and -`MultiFileDownloadJob`. The former is a pydantic object with the -following fields: - -| **Field** | **Type** | **Default** | **Description** | -|----------------|-----------------|---------------|-----------------| -| _Fields passed in at job creation time_ | -| `source` | AnyHttpUrl | | Where to download from | -| `dest` | Path | | Where to download to | -| `access_token` | str | | [optional] string containing authentication token for access | -| `on_start` | Callable | | [optional] callback when the download starts | -| `on_progress` | Callable | | [optional] callback called at intervals during download progress | -| `on_complete` | Callable | | [optional] callback called after successful download completion | -| `on_error` | Callable | | [optional] callback called after an error occurs | -| `id` | int | auto assigned | Job ID, an integer >= 0 | -| `priority` | int | 10 | Job priority. Lower priorities run before higher priorities | -| | -| _Fields updated over the course of the download task_ -| `status` | DownloadJobStatus| | Status code | -| `download_path` | Path | | Path to the location of the downloaded file | -| `job_started` | float | | Timestamp for when the job started running | -| `job_ended` | float | | Timestamp for when the job completed or errored out | -| `job_sequence` | int | | A counter that is incremented each time a model is dequeued | -| `bytes` | int | 0 | Bytes downloaded so far | -| `total_bytes` | int | 0 | Total size of the file at the remote site | -| `error_type` | str | | String version of the exception that caused an error during download | -| `error` | str | | String version of the traceback associated with an error | -| `cancelled` | bool | False | Set to true if the job was cancelled by the caller| - -When you create a job, you can assign it a `priority`. If multiple -jobs are queued, the job with the lowest priority runs first. - -Every job has a `source` and a `dest`. `source` is a pydantic.networks AnyHttpUrl object. -The `dest` is a path on the local filesystem that specifies the -destination for the downloaded object. Its semantics are -described below. - -When the job is submitted, it is assigned a numeric `id`. The id can -then be used to fetch the job object from the queue. - -The `status` field is updated by the queue to indicate where the job -is in its lifecycle. Values are defined in the string enum -`DownloadJobStatus`, a symbol available from -`invokeai.app.services.download_manager`. Possible values are: - -| **Value** | **String Value** | ** Description ** | -|--------------|---------------------|-------------------| -| `WAITING` | waiting | Job is on the queue but not yet running| -| `RUNNING` | running | The download is started | -| `COMPLETED` | completed | Job has finished its work without an error | -| `ERROR` | error | Job encountered an error and will not run again| - -`job_started` and `job_ended` indicate when the job -was started (using a python timestamp) and when it completed. - -In case of an error, the job's status will be set to `DownloadJobStatus.ERROR`, the text of the -Exception that caused the error will be placed in the `error_type` -field and the traceback that led to the error will be in `error`. - -A cancelled job will have status `DownloadJobStatus.ERROR` and an -`error_type` field of "DownloadJobCancelledException". In addition, -the job's `cancelled` property will be set to True. - -The `MultiFileDownloadJob` is used for diffusers model downloads, -which contain multiple files and directories under a common root: - -| **Field** | **Type** | **Default** | **Description** | -|----------------|-----------------|---------------|-----------------| -| _Fields passed in at job creation time_ | -| `download_parts` | Set[DownloadJob]| | Component download jobs | -| `dest` | Path | | Where to download to | -| `on_start` | Callable | | [optional] callback when the download starts | -| `on_progress` | Callable | | [optional] callback called at intervals during download progress | -| `on_complete` | Callable | | [optional] callback called after successful download completion | -| `on_error` | Callable | | [optional] callback called after an error occurs | -| `id` | int | auto assigned | Job ID, an integer >= 0 | -| _Fields updated over the course of the download task_ -| `status` | DownloadJobStatus| | Status code | -| `download_path` | Path | | Path to the root of the downloaded files | -| `bytes` | int | 0 | Bytes downloaded so far | -| `total_bytes` | int | 0 | Total size of the file at the remote site | -| `error_type` | str | | String version of the exception that caused an error during download | -| `error` | str | | String version of the traceback associated with an error | -| `cancelled` | bool | False | Set to true if the job was cancelled by the caller| - -Note that the MultiFileDownloadJob does not support the `priority`, -`job_started`, `job_ended` or `content_type` attributes. You can get -these from the individual download jobs in `download_parts`. - - -### Callbacks - -Download jobs can be associated with a series of callbacks, each with -the signature `Callable[["DownloadJob"], None]`. The callbacks are assigned -using optional arguments `on_start`, `on_progress`, `on_complete` and -`on_error`. When the corresponding event occurs, the callback wil be -invoked and passed the job. The callback will be run in a `try:` -context in the same thread as the download job. Any exceptions that -occur during execution of the callback will be caught and converted -into a log error message, thereby allowing the download to continue. - -#### `TqdmProgress` - -The `invokeai.app.services.download.download_default` module defines a -class named `TqdmProgress` which can be used as an `on_progress` -handler to display a completion bar in the console. Use as follows: - -``` -from invokeai.app.services.download import TqdmProgress - -download_queue.download(source='http://some.server.somewhere/some_file', - dest='/tmp/downloads', - on_progress=TqdmProgress().update - ) - -``` - -### Events - -If the queue was initialized with the InvokeAI event bus (the case -when using `ApiDependencies.invoker.services.download_queue`), then -download events will also be issued on the bus. The events are: - -* `download_started` -- This is issued when a job is taken off the -queue and a request is made to the remote server for the URL headers, but before any data -has been downloaded. The event payload will contain the keys `source` -and `download_path`. The latter contains the path that the URL will be -downloaded to. - -* `download_progress -- This is issued periodically as the download -runs. The payload contains the keys `source`, `download_path`, -`current_bytes` and `total_bytes`. The latter two fields can be -used to display the percent complete. - -* `download_complete` -- This is issued when the download completes -successfully. The payload contains the keys `source`, `download_path` -and `total_bytes`. - -* `download_error` -- This is issued when the download stops because -of an error condition. The payload contains the fields `error_type` -and `error`. The former is the text representation of the exception, -and the latter is a traceback showing where the error occurred. - -### Job control - -To create a job call the queue's `download()` method. You can list all -jobs using `list_jobs()`, fetch a single job by its with -`id_to_job()`, cancel a running job with `cancel_job()`, cancel all -running jobs with `cancel_all_jobs()`, and wait for all jobs to finish -with `join()`. - -#### job = queue.download(source, dest, priority, access_token, on_start, on_progress, on_complete, on_cancelled, on_error) - -Create a new download job and put it on the queue, returning the -DownloadJob object. - -#### multifile_job = queue.multifile_download(parts, dest, access_token, on_start, on_progress, on_complete, on_cancelled, on_error) - -This is similar to download(), but instead of taking a single source, -it accepts a `parts` argument consisting of a list of -`RemoteModelFile` objects. Each part corresponds to a URL/Path pair, -where the URL is the location of the remote file, and the Path is the -destination. - -`RemoteModelFile` can be imported from `invokeai.backend.model_manager.metadata`, and -consists of a url/path pair. Note that the path *must* be relative. - -The method returns a `MultiFileDownloadJob`. - - -``` -from invokeai.backend.model_manager.metadata import RemoteModelFile -remote_file_1 = RemoteModelFile(url='http://www.foo.bar/my/pytorch_model.safetensors'', - path='my_model/textencoder/pytorch_model.safetensors' - ) -remote_file_2 = RemoteModelFile(url='http://www.bar.baz/vae.ckpt', - path='my_model/vae/diffusers_model.safetensors' - ) -job = queue.multifile_download(parts=[remote_file_1, remote_file_2], - dest='/tmp/downloads', - on_progress=TqdmProgress().update) -queue.wait_for_job(job) -print(f"The files were downloaded to {job.download_path}") -``` - -#### jobs = queue.list_jobs() - -Return a list of all active and inactive `DownloadJob`s. - -#### job = queue.id_to_job(id) - -Return the job corresponding to given ID. - -Return a list of all active and inactive `DownloadJob`s. - -#### queue.prune_jobs() - -Remove inactive (complete or errored) jobs from the listing returned -by `list_jobs()`. - -#### queue.join() - -Block until all pending jobs have run to completion or errored out. - diff --git a/docs-old/contributing/HOTKEYS.md b/docs-old/contributing/HOTKEYS.md deleted file mode 100644 index fac13976b12..00000000000 --- a/docs-old/contributing/HOTKEYS.md +++ /dev/null @@ -1,295 +0,0 @@ -# Hotkeys System - -This document describes the technical implementation of the customizable hotkeys system in InvokeAI. - -> **Note:** For user-facing documentation on how to use customizable hotkeys, see [Hotkeys Feature Documentation](../features/hotkeys.md). - -## Overview - -The hotkeys system allows users to customize keyboard shortcuts throughout the application. All hotkeys are: -- Centrally defined and managed -- Customizable by users -- Persisted across sessions -- Type-safe and validated - -## Architecture - -The customizable hotkeys feature is built on top of the existing hotkey system with the following components: - -### 1. Hotkeys State Slice (`hotkeysSlice.ts`) - -Location: `invokeai/frontend/web/src/features/system/store/hotkeysSlice.ts` - -**Responsibilities:** -- Stores custom hotkey mappings in Redux state -- Persisted to IndexedDB using `redux-remember` -- Provides actions to change, reset individual, or reset all hotkeys - -**State Shape:** -```typescript -{ - _version: 1, - customHotkeys: { - 'app.invoke': ['mod+enter'], - 'canvas.undo': ['mod+z'], - // ... - } -} -``` - -**Actions:** -- `hotkeyChanged(id, hotkeys)` - Update a single hotkey -- `hotkeyReset(id)` - Reset a single hotkey to default -- `allHotkeysReset()` - Reset all hotkeys to defaults - -### 2. useHotkeyData Hook (`useHotkeyData.ts`) - -Location: `invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts` - -**Responsibilities:** -- Defines all default hotkeys -- Merges default hotkeys with custom hotkeys from the store -- Returns the effective hotkeys that should be used throughout the app -- Provides platform-specific key translations (Ctrl/Cmd, Alt/Option) - -**Key Functions:** -- `useHotkeyData()` - Returns all hotkeys organized by category -- `useRegisteredHotkeys()` - Hook to register a hotkey in a component - -### 3. HotkeyEditor Component (`HotkeyEditor.tsx`) - -Location: `invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeyEditor.tsx` - -**Features:** -- Inline editor with input field -- Modifier buttons (Mod, Ctrl, Shift, Alt) for quick insertion -- Live preview of hotkey combinations -- Validation with visual feedback -- Help tooltip with syntax examples -- Save/cancel/reset buttons - -**Smart Features:** -- Automatic `+` insertion between modifiers -- Cursor position preservation -- Validation prevents invalid combinations (e.g., modifier-only keys) - -### 4. HotkeysModal Component (`HotkeysModal.tsx`) - -Location: `invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeysModal.tsx` - -**Features:** -- View Mode / Edit Mode toggle -- Search functionality -- Category-based organization -- Shows HotkeyEditor components when in edit mode -- "Reset All to Default" button in edit mode - -## Data Flow - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 1. User opens Hotkeys Modal │ -│ 2. User clicks "Edit Mode" button │ -│ 3. User clicks edit icon next to a hotkey │ -│ 4. User enters new hotkey(s) using editor │ -│ 5. User clicks save or presses Enter │ -│ 6. Custom hotkey stored via hotkeyChanged() action │ -│ 7. Redux state persisted to IndexedDB (redux-remember) │ -│ 8. useHotkeyData() hook picks up the change │ -│ 9. All components using useRegisteredHotkeys() get update │ -└─────────────────────────────────────────────────────────────┘ -``` - -## Hotkey Format - -Hotkeys use the format from `react-hotkeys-hook` library: - -- **Modifiers:** `mod`, `ctrl`, `shift`, `alt`, `meta` -- **Keys:** Letters, numbers, function keys, special keys -- **Separator:** `+` between keys in a combination -- **Multiple hotkeys:** Comma-separated (e.g., `mod+a, ctrl+b`) - -**Examples:** -- `mod+enter` - Mod key + Enter -- `shift+x` - Shift + X -- `ctrl+shift+a` - Control + Shift + A -- `f1, f2` - F1 or F2 (alternatives) - -## Developer Guide - -### Using Hotkeys in Components - -To use a hotkey in a component: - -```tsx -import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; - -const MyComponent = () => { - const handleAction = useCallback(() => { - // Your action here - }, []); - - // This automatically uses custom hotkeys if configured - useRegisteredHotkeys({ - id: 'myAction', - category: 'app', // or 'canvas', 'viewer', 'gallery', 'workflows' - callback: handleAction, - options: { enabled: true, preventDefault: true }, - dependencies: [handleAction] - }); - - // ... -}; -``` - -**Options:** -- `enabled` - Whether the hotkey is active -- `preventDefault` - Prevent default browser behavior -- `enableOnFormTags` - Allow hotkey in form elements (default: false) - -### Adding New Hotkeys - -To add a new hotkey to the system: - -#### 1. Add Translation Strings - -In `invokeai/frontend/web/public/locales/en.json`: - -```json -{ - "hotkeys": { - "app": { - "myAction": { - "title": "My Action", - "desc": "Description of what this hotkey does" - } - } - } -} -``` - -#### 2. Register the Hotkey - -In `invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts`: - -```typescript -// Inside the appropriate category builder function -addHotkey('app', 'myAction', ['mod+k']); // Default binding -``` - -#### 3. Use the Hotkey - -In your component: - -```typescript -useRegisteredHotkeys({ - id: 'myAction', - category: 'app', - callback: handleMyAction, - options: { enabled: true }, - dependencies: [handleMyAction] -}); -``` - -### Hotkey Categories - -Current categories: -- **app** - Global application hotkeys -- **canvas** - Canvas/drawing operations -- **viewer** - Image viewer operations -- **gallery** - Gallery/image grid operations -- **workflows** - Node workflow editor - -To add a new category, update `useHotkeyData.ts` and add translations. - -## Testing - -Tests are located in `invokeai/frontend/web/src/features/system/store/hotkeysSlice.test.ts`. - -**Test Coverage:** -- Adding custom hotkeys -- Updating existing custom hotkeys -- Resetting individual hotkeys -- Resetting all hotkeys -- State persistence and migration - -Run tests with: - -```bash -cd invokeai/frontend/web -pnpm test:no-watch -``` - -## Persistence - -Custom hotkeys are persisted using the same mechanism as other app settings: - -- Stored in Redux state under the `hotkeys` slice -- Persisted to IndexedDB via `redux-remember` -- Automatically loaded when the app starts -- Survives page refreshes and browser restarts -- Includes migration support for state schema changes - -**State Location:** -- IndexedDB database: `invoke` -- Store key: `hotkeys` - -## Dependencies - -- **react-hotkeys-hook** (v4.5.0) - Core hotkey handling -- **@reduxjs/toolkit** - State management -- **redux-remember** - Persistence -- **zod** - State validation - -## Best Practices - -1. **Use `mod` instead of `ctrl`** - Automatically maps to Cmd on Mac, Ctrl elsewhere -2. **Provide descriptive translations** - Help users understand what each hotkey does -3. **Avoid conflicts** - Check existing hotkeys before adding new ones -4. **Use preventDefault** - Prevent browser default behavior when appropriate -5. **Check enabled state** - Only activate hotkeys when the action is available -6. **Use dependencies correctly** - Ensure callbacks are stable with useCallback - -## Common Patterns - -### Conditional Hotkeys - -```typescript -useRegisteredHotkeys({ - id: 'save', - category: 'app', - callback: handleSave, - options: { - enabled: hasUnsavedChanges && !isLoading, // Only when valid - preventDefault: true - }, - dependencies: [hasUnsavedChanges, isLoading, handleSave] -}); -``` - -### Multiple Hotkeys for Same Action - -```typescript -// In useHotkeyData.ts -addHotkey('canvas', 'redo', ['mod+shift+z', 'mod+y']); // Two alternatives -``` - -### Focus-Scoped Hotkeys - -```typescript -import { useFocusRegion } from 'common/hooks/focus'; - -const MyComponent = () => { - const focusRegionRef = useFocusRegion('myRegion'); - - // Hotkey only works when this region has focus - useRegisteredHotkeys({ - id: 'myAction', - category: 'app', - callback: handleAction, - options: { enabled: true } - }); - - return
...
; -}; -``` diff --git a/docs-old/contributing/INVOCATIONS.md b/docs-old/contributing/INVOCATIONS.md deleted file mode 100644 index 7fcea572a6e..00000000000 --- a/docs-old/contributing/INVOCATIONS.md +++ /dev/null @@ -1,423 +0,0 @@ -# Nodes - -Features in InvokeAI are added in the form of modular nodes systems called -**Invocations**. - -An Invocation is simply a single operation that takes in some inputs and gives -out some outputs. We can then chain multiple Invocations together to create more -complex functionality. - -## Invocations Directory - -InvokeAI Nodes can be found in the `invokeai/app/invocations` directory. These -can be used as examples to create your own nodes. - -New nodes should be added to a subfolder in `nodes` direction found at the root -level of the InvokeAI installation location. Nodes added to this folder will be -able to be used upon application startup. - -Example `nodes` subfolder structure: - -```py -├── __init__.py # Invoke-managed custom node loader -│ -├── cool_node -│ ├── __init__.py # see example below -│ └── cool_node.py -│ -└── my_node_pack - ├── __init__.py # see example below - ├── tasty_node.py - ├── bodacious_node.py - ├── utils.py - └── extra_nodes - └── fancy_node.py -``` - -Each node folder must have an `__init__.py` file that imports its nodes. Only -nodes imported in the `__init__.py` file are loaded. See the README in the nodes -folder for more examples: - -```py -from .cool_node import ResizeInvocation -``` - -## Creating A New Invocation - -In order to understand the process of creating a new Invocation, let us actually -create one. - -In our example, let us create an Invocation that will take in an image, resize -it and output the resized image. - -The first set of things we need to do when creating a new Invocation are - - -- Create a new class that derives from a predefined parent class called - `BaseInvocation`. -- Every Invocation must have a `docstring` that describes what this Invocation - does. -- While not strictly required, we suggest every invocation class name ends in - "Invocation", eg "CropImageInvocation". -- Every Invocation must use the `@invocation` decorator to provide its unique - invocation type. You may also provide its title, tags and category using the - decorator. -- Invocations are strictly typed. We make use of the native - [typing](https://docs.python.org/3/library/typing.html) library and the - installed [pydantic](https://pydantic-docs.helpmanual.io/) library for - validation. - -So let us do that. - -```python -from invokeai.invocation_api import ( - BaseInvocation, - invocation, -) - -@invocation('resize') -class ResizeInvocation(BaseInvocation): - '''Resizes an image''' -``` - -That's great. - -Now we have setup the base of our new Invocation. Let us think about what inputs -our Invocation takes. - -- We need an `image` that we are going to resize. -- We will need new `width` and `height` values to which we need to resize the - image to. - -### **Inputs** - -Every Invocation input must be defined using the `InputField` function. This is -a wrapper around the pydantic `Field` function, which handles a few extra things -and provides type hints. Like everything else, this should be strictly typed and -defined. - -So let us create these inputs for our Invocation. First up, the `image` input we -need. Generally, we can use standard variable types in Python but InvokeAI -already has a custom `ImageField` type that handles all the stuff that is needed -for image inputs. - -But what is this `ImageField` ..? It is a special class type specifically -written to handle how images are dealt with in InvokeAI. We will cover how to -create your own custom field types later in this guide. For now, let's go ahead -and use it. - -```python -from invokeai.invocation_api import ( - BaseInvocation, - ImageField, - InputField, - invocation, -) - -@invocation('resize') -class ResizeInvocation(BaseInvocation): - - # Inputs - image: ImageField = InputField(description="The input image") -``` - -Let us break down our input code. - -```python -image: ImageField = InputField(description="The input image") -``` - -| Part | Value | Description | -| --------- | ------------------------------------------- | ------------------------------------------------------------------------------- | -| Name | `image` | The variable that will hold our image | -| Type Hint | `ImageField` | The types for our field. Indicates that the image must be an `ImageField` type. | -| Field | `InputField(description="The input image")` | The image variable is an `InputField` which needs a description. | - -Great. Now let us create our other inputs for `width` and `height` - -```python -from invokeai.invocation_api import ( - BaseInvocation, - ImageField, - InputField, - invocation, -) - -@invocation('resize') -class ResizeInvocation(BaseInvocation): - '''Resizes an image''' - - image: ImageField = InputField(description="The input image") - width: int = InputField(default=512, ge=64, le=2048, description="Width of the new image") - height: int = InputField(default=512, ge=64, le=2048, description="Height of the new image") -``` - -As you might have noticed, we added two new arguments to the `InputField` -definition for `width` and `height`, called `gt` and `le`. They stand for -_greater than or equal to_ and _less than or equal to_. - -These impose constraints on those fields, and will raise an exception if the -values do not meet the constraints. Field constraints are provided by -**pydantic**, so anything you see in the **pydantic docs** will work. - -**Note:** _Any time it is possible to define constraints for our field, we -should do it so the frontend has more information on how to parse this field._ - -Perfect. We now have our inputs. Let us do something with these. - -### **Invoke Function** - -The `invoke` function is where all the magic happens. This function provides you -the `context` parameter that is of the type `InvocationContext` which will give -you access to the current context of the generation and all the other services -that are provided by it by InvokeAI. - -Let us create this function first. - -```python -from invokeai.invocation_api import ( - BaseInvocation, - ImageField, - InputField, - InvocationContext, - invocation, -) - -@invocation('resize') -class ResizeInvocation(BaseInvocation): - '''Resizes an image''' - - image: ImageField = InputField(description="The input image") - width: int = InputField(default=512, ge=64, le=2048, description="Width of the new image") - height: int = InputField(default=512, ge=64, le=2048, description="Height of the new image") - - def invoke(self, context: InvocationContext): - pass -``` - -### **Outputs** - -The output of our Invocation will be whatever is returned by this `invoke` -function. Like with our inputs, we need to strongly type and define our outputs -too. - -What is our output going to be? Another image. Normally you'd have to create a -type for this but InvokeAI already offers you an `ImageOutput` type that handles -all the necessary info related to image outputs. So let us use that. - -We will cover how to create your own output types later in this guide. - -```python -from invokeai.invocation_api import ( - BaseInvocation, - ImageField, - InputField, - InvocationContext, - invocation, -) - -from invokeai.app.invocations.image import ImageOutput - -@invocation('resize') -class ResizeInvocation(BaseInvocation): - '''Resizes an image''' - - image: ImageField = InputField(description="The input image") - width: int = InputField(default=512, ge=64, le=2048, description="Width of the new image") - height: int = InputField(default=512, ge=64, le=2048, description="Height of the new image") - - def invoke(self, context: InvocationContext) -> ImageOutput: - pass -``` - -Perfect. Now that we have our Invocation setup, let us do what we want to do. - -- We will first load the image using one of the services provided by InvokeAI to - load the image. -- We will resize the image using `PIL` to our input data. -- We will output this image in the format we set above. - -So let's do that. - -```python -from invokeai.invocation_api import ( - BaseInvocation, - ImageField, - InputField, - InvocationContext, - invocation, -) - -from invokeai.app.invocations.image import ImageOutput - -@invocation("resize") -class ResizeInvocation(BaseInvocation): - """Resizes an image""" - - image: ImageField = InputField(description="The input image") - width: int = InputField(default=512, ge=64, le=2048, description="Width of the new image") - height: int = InputField(default=512, ge=64, le=2048, description="Height of the new image") - - def invoke(self, context: InvocationContext) -> ImageOutput: - # Load the input image as a PIL image - image = context.images.get_pil(self.image.image_name) - - # Resize the image - resized_image = image.resize((self.width, self.height)) - - # Save the image - image_dto = context.images.save(image=resized_image) - - # Return an ImageOutput - return ImageOutput.build(image_dto) -``` - -**Note:** Do not be overwhelmed by the `ImageOutput` process. InvokeAI has a -certain way that the images need to be dispatched in order to be stored and read -correctly. In 99% of the cases when dealing with an image output, you can simply -copy-paste the template above. - -### Customization - -We can use the `@invocation` decorator to provide some additional info to the -UI, like a custom title, tags and category. - -We also encourage providing a version. This must be a -[semver](https://semver.org/) version string ("$MAJOR.$MINOR.$PATCH"). The UI -will let users know if their workflow is using a mismatched version of the node. - -```python -@invocation("resize", title="My Resizer", tags=["resize", "image"], category="My Invocations", version="1.0.0") -class ResizeInvocation(BaseInvocation): - """Resizes an image""" - - image: ImageField = InputField(description="The input image") - ... -``` - -That's it. You made your own **Resize Invocation**. - -## Result - -Once you make your Invocation correctly, the rest of the process is fully -automated for you. - -When you launch InvokeAI, you can go to `http://localhost:9090/docs` and see -your new Invocation show up there with all the relevant info. - -![resize invocation](../assets/contributing/resize_invocation.png) - -When you launch the frontend UI, you can go to the Node Editor tab and find your -new Invocation ready to be used. - -![resize node editor](../assets/contributing/resize_node_editor.png) - -## Contributing Nodes - -Once you've created a Node, the next step is to share it with the community! The -best way to do this is to submit a Pull Request to add the Node to the -[Community Nodes](../nodes/communityNodes.md) list. If you're not sure how to do that, -take a look a at our [contributing nodes overview](../nodes/contributingNodes.md). - -## Advanced - -### Custom Output Types - -Like with custom inputs, sometimes you might find yourself needing custom -outputs that InvokeAI does not provide. We can easily set one up. - -Now that you are familiar with Invocations and Inputs, let us use that knowledge -to create an output that has an `image` field, a `color` field and a `string` -field. - -- An invocation output is a class that derives from the parent class of - `BaseInvocationOutput`. -- All invocation outputs must use the `@invocation_output` decorator to provide - their unique output type. -- Output fields must use the provided `OutputField` function. This is very - similar to the `InputField` function described earlier - it's a wrapper around - `pydantic`'s `Field()`. -- It is not mandatory but we recommend using names ending with `Output` for - output types. -- It is not mandatory but we highly recommend adding a `docstring` to describe - what your output type is for. - -Now that we know the basic rules for creating a new output type, let us go ahead -and make it. - -```python -from .baseinvocation import BaseInvocationOutput, OutputField, invocation_output -from .primitives import ImageField, ColorField - -@invocation_output('image_color_string_output') -class ImageColorStringOutput(BaseInvocationOutput): - '''Base class for nodes that output a single image''' - - image: ImageField = OutputField(description="The image") - color: ColorField = OutputField(description="The color") - text: str = OutputField(description="The string") -``` - -That's all there is to it. - -### Custom Input Fields - -Now that you know how to create your own Invocations, let us dive into slightly -more advanced topics. - -While creating your own Invocations, you might run into a scenario where the -existing fields in InvokeAI do not meet your requirements. In such cases, you -can create your own fields. - -Let us create one as an example. Let us say we want to create a color input -field that represents a color code. But before we start on that here are some -general good practices to keep in mind. - -### Best Practices - -- There is no naming convention for input fields but we highly recommend that - you name it something appropriate like `ColorField`. -- It is not mandatory but it is heavily recommended to add a relevant - `docstring` to describe your field. -- Keep your field in the same file as the Invocation that it is made for or in - another file where it is relevant. - -All input types a class that derive from the `BaseModel` type from `pydantic`. -So let's create one. - -```python -from pydantic import BaseModel - -class ColorField(BaseModel): - '''A field that holds the rgba values of a color''' - pass -``` - -Perfect. Now let us create the properties for our field. This is similar to how -you created input fields for your Invocation. All the same rules apply. Let us -create four fields representing the _red(r)_, _blue(b)_, _green(g)_ and -_alpha(a)_ channel of the color. - -> Technically, the properties are _also_ called fields - but in this case, it -> refers to a `pydantic` field. - -```python -class ColorField(BaseModel): - '''A field that holds the rgba values of a color''' - r: int = Field(ge=0, le=255, description="The red channel") - g: int = Field(ge=0, le=255, description="The green channel") - b: int = Field(ge=0, le=255, description="The blue channel") - a: int = Field(ge=0, le=255, description="The alpha channel") -``` - -That's it. We now have a new input field type that we can use in our Invocations -like this. - -```python -color: ColorField = InputField(default=ColorField(r=0, g=0, b=0, a=0), description='Background color of an image') -``` - -### Using the custom field - -When you start the UI, your custom field will be automatically recognized. - -Custom fields only support connection inputs in the Workflow Editor. diff --git a/docs-old/contributing/LOCAL_DEVELOPMENT.md b/docs-old/contributing/LOCAL_DEVELOPMENT.md deleted file mode 100644 index 97d840186e7..00000000000 --- a/docs-old/contributing/LOCAL_DEVELOPMENT.md +++ /dev/null @@ -1,267 +0,0 @@ -# Local Development - -If you want to contribute, you will need to set up a [local development environment](./dev-environment.md). - -## Documentation - -We use [mkdocs](https://www.mkdocs.org) for our documentation with the [material theme](https://squidfunk.github.io/mkdocs-material/). Documentation is written in markdown files under the `./docs` folder and then built into a static website for hosting with GitHub Pages at [invoke-ai.github.io/InvokeAI](https://invoke-ai.github.io/InvokeAI). - -To contribute to the documentation you'll need to install the dependencies. Note -the use of `"`. - -```zsh -pip install ".[docs]" -``` - -Now, to run the documentation locally with hot-reloading for changes made. - -```zsh -mkdocs serve -``` - -You'll then be prompted to connect to `http://127.0.0.1:8080` in order to -access. - -## Backend - -The backend is contained within the `./invokeai/backend` and `./invokeai/app` directories. -To get started please install the development dependencies. - -From the root of the repository run the following command. Note the use of `"`. - -```zsh -pip install ".[dev,test]" -``` - -These are optional groups of packages which are defined within the `pyproject.toml` -and will be required for testing the changes you make to the code. - -### Tests - -See the [tests documentation](./TESTS.md) for information about running and writing tests. - -### Reloading Changes - -Experimenting with changes to the Python source code is a drag if you have to re-start the server — -and re-load those multi-gigabyte models — -after every change. - -For a faster development workflow, add the `--dev_reload` flag when starting the server. -The server will watch for changes to all the Python files in the `invokeai` directory and apply those changes to the -running server on the fly. - -This will allow you to avoid restarting the server (and reloading models) in most cases, but there are some caveats; see -the [jurigged documentation](https://github.com/breuleux/jurigged#caveats) for details. - -## Front End - - - ---8<-- "invokeai/frontend/web/README.md" - -## Developing InvokeAI in VSCode - -VSCode offers some nice tools: - -- python debugger -- automatic `venv` activation -- remote dev (e.g. run InvokeAI on a beefy linux desktop while you type in - comfort on your macbook) - -### Setup - -You'll need the -[Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) -and -[Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) -extensions installed first. - -It's also really handy to install the `Jupyter` extensions: - -- [Jupyter](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter) -- [Jupyter Cell Tags](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.vscode-jupyter-cell-tags) -- [Jupyter Notebook Renderers](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter-renderers) -- [Jupyter Slide Show](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.vscode-jupyter-slideshow) - -#### InvokeAI workspace - -Creating a VSCode workspace for working on InvokeAI is highly recommended. It -can hold InvokeAI-specific settings and configs. - -To make a workspace: - -- Open the InvokeAI repo dir in VSCode -- `File` > `Save Workspace As` > save it _outside_ the repo - -#### Default python interpreter (i.e. automatic virtual environment activation) - -- Use command palette to run command - `Preferences: Open Workspace Settings (JSON)` -- Add `python.defaultInterpreterPath` to `settings`, pointing to your `venv`'s - python - -Should look something like this: - -```jsonc -{ - // I like to have all InvokeAI-related folders in my workspace - "folders": [ - { - // repo root - "path": "InvokeAI" - }, - { - // InvokeAI root dir, where `invokeai.yaml` lives - "path": "/path/to/invokeai_root" - } - ], - "settings": { - // Where your InvokeAI `venv`'s python executable lives - "python.defaultInterpreterPath": "/path/to/invokeai_root/.venv/bin/python" - } -} -``` - -Now when you open the VSCode integrated terminal, or do anything that needs to -run python, it will automatically be in your InvokeAI virtual environment. - -Bonus: When you create a Jupyter notebook, when you run it, you'll be prompted -for the python interpreter to run in. This will default to your `venv` python, -and so you'll have access to the same python environment as the InvokeAI app. - -This is _super_ handy. - -#### Enabling Type-Checking with Pylance - -We use python's typing system in InvokeAI. PR reviews will include checking that types are present and correct. We don't enforce types with `mypy` at this time, but that is on the horizon. - -Using a code analysis tool to automatically type check your code (and types) is very important when writing with types. These tools provide immediate feedback in your editor when types are incorrect, and following their suggestions lead to fewer runtime bugs. - -Pylance, installed at the beginning of this guide, is the de-facto python LSP (language server protocol). It provides type checking in the editor (among many other features). Once installed, you do need to enable type checking manually: - -- Open a python file -- Look along the status bar in VSCode for `{ } Python` -- Click the `{ }` -- Turn type checking on - basic is fine - -You'll now see red squiggly lines where type issues are detected. Hover your cursor over the indicated symbols to see what's wrong. - -In 99% of cases when the type checker says there is a problem, there really is a problem, and you should take some time to understand and resolve what it is pointing out. - -#### Debugging configs with `launch.json` - -Debugging configs are managed in a `launch.json` file. Like most VSCode configs, -these can be scoped to a workspace or folder. - -Follow the [official guide](https://code.visualstudio.com/docs/python/debugging) -to set up your `launch.json` and try it out. - -Now we can create the InvokeAI debugging configs: - -```jsonc -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - // Run the InvokeAI backend & serve the pre-built UI - "name": "InvokeAI Web", - "type": "python", - "request": "launch", - "program": "scripts/invokeai-web.py", - "args": [ - // Your InvokeAI root dir (where `invokeai.yaml` lives) - "--root", - "/path/to/invokeai_root", - // Access the app from anywhere on your local network - "--host", - "0.0.0.0" - ], - "justMyCode": true - }, - { - // Run the nodes-based CLI - "name": "InvokeAI CLI", - "type": "python", - "request": "launch", - "program": "scripts/invokeai-cli.py", - "justMyCode": true - }, - { - // Run tests - "name": "InvokeAI Test", - "type": "python", - "request": "launch", - "module": "pytest", - "args": ["--capture=no"], - "justMyCode": true - }, - { - // Run a single test - "name": "InvokeAI Single Test", - "type": "python", - "request": "launch", - "module": "pytest", - "args": [ - // Change this to point to the specific test you are working on - "tests/nodes/test_invoker.py" - ], - "justMyCode": true - }, - { - // This is the default, useful to just run a single file - "name": "Python: File", - "type": "python", - "request": "launch", - "program": "${file}", - "justMyCode": true - } - ] -} -``` - -You'll see these configs in the debugging configs drop down. Running them will -start InvokeAI with attached debugger, in the correct environment, and work just -like the normal app. - -Enjoy debugging InvokeAI with ease (not that we have any bugs of course). - -#### Remote dev - -This is very easy to set up and provides the same very smooth experience as -local development. Environments and debugging, as set up above, just work, -though you'd need to recreate the workspace and debugging configs on the remote. - -Consult the -[official guide](https://code.visualstudio.com/docs/remote/remote-overview) to -get it set up. - -Suggest using VSCode's included settings sync so that your remote dev host has -all the same app settings and extensions automatically. - -##### One remote dev gotcha - -I've found the automatic port forwarding to be very flakey. You can disable it -in `Preferences: Open Remote Settings (ssh: hostname)`. Search for -`remote.autoForwardPorts` and untick the box. - -To forward ports very reliably, use SSH on the remote dev client (e.g. your -macbook). Here's how to forward both backend API port (`9090`) and the frontend -live dev server port (`5173`): - -```bash -ssh \ - -L 9090:localhost:9090 \ - -L 5173:localhost:5173 \ - user@remote-dev-host -``` - -The forwarding stops when you close the terminal window, so suggest to do this -_outside_ the VSCode integrated terminal in case you need to restart VSCode for -an extension update or something - -Now, on your remote dev client, you can open `localhost:9090` and access the UI, -now served from the remote dev host, just the same as if it was running on the -client. diff --git a/docs-old/contributing/MODEL_MANAGER.md b/docs-old/contributing/MODEL_MANAGER.md deleted file mode 100644 index 627a3e42ec4..00000000000 --- a/docs-old/contributing/MODEL_MANAGER.md +++ /dev/null @@ -1,1658 +0,0 @@ -# Introduction to the Model Manager V2 - -The Model Manager is responsible for organizing the various machine -learning models used by InvokeAI. It consists of a series of -interdependent services that together handle the full lifecycle of a -model. These are the: - -* _ModelRecordServiceBase_ Responsible for managing model metadata and - configuration information. Among other things, the record service - tracks the type of the model, its provenance, and where it can be - found on disk. - -* _ModelInstallServiceBase_ A service for installing models to - disk. It uses `DownloadQueueServiceBase` to download models and - their metadata, and `ModelRecordServiceBase` to store that - information. It is also responsible for managing the InvokeAI - `models` directory and its contents. - -* _DownloadQueueServiceBase_ - A multithreaded downloader responsible - for downloading models from a remote source to disk. The download - queue has special methods for downloading repo_id folders from - Hugging Face, as well as discriminating among model versions in - Civitai, but can be used for arbitrary content. - - * _ModelLoadServiceBase_ - Responsible for loading a model from disk - into RAM and VRAM and getting it ready for inference. - -## Location of the Code - -The four main services can be found in -`invokeai/app/services` in the following directories: - -* `invokeai/app/services/model_records/` -* `invokeai/app/services/model_install/` -* `invokeai/app/services/downloads/` -* `invokeai/app/services/model_load/` - -Code related to the FastAPI web API can be found in -`invokeai/app/api/routers/model_manager_v2.py`. - -*** - -## What's in a Model? The ModelRecordService - -The `ModelRecordService` manages the model's metadata. It supports a -hierarchy of pydantic metadata "config" objects, which become -increasingly specialized to support particular model types. - -### ModelConfigBase - -All model metadata classes inherit from this pydantic class. it -provides the following fields: - -| **Field Name** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `key` | str | Unique identifier for the model | -| `name` | str | Name of the model (not unique) | -| `model_type` | ModelType | The type of the model | -| `model_format` | ModelFormat | The format of the model (e.g. "diffusers"); also used as a Union discriminator | -| `base_model` | BaseModelType | The base model that the model is compatible with | -| `path` | str | Location of model on disk | -| `hash` | str | Hash of the model | -| `description` | str | Human-readable description of the model (optional) | -| `source` | str | Model's source URL or repo id (optional) | - -The `key` is a unique 32-character random ID which was generated at -install time. The `hash` field stores a hash of the model's -contents at install time obtained by sampling several parts of the -model's files using the `imohash` library. Over the course of the -model's lifetime it may be transformed in various ways, such as -changing its precision or converting it from a .safetensors to a -diffusers model. - -`ModelType`, `ModelFormat` and `BaseModelType` are string enums that -are defined in `invokeai.backend.model_manager.config`. They are also -imported by, and can be reexported from, -`invokeai.app.services.model_manager.model_records`: - -``` -from invokeai.app.services.model_records import ModelType, ModelFormat, BaseModelType -``` - -The `path` field can be absolute or relative. If relative, it is taken -to be relative to the `models_dir` setting in the user's -`invokeai.yaml` file. - -### CheckpointConfig - -This adds support for checkpoint configurations, and adds the -following field: - -| **Field Name** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `config` | str | Path to the checkpoint's config file | - -`config` is the path to the checkpoint's config file. If relative, it -is taken to be relative to the InvokeAI root directory -(e.g. `configs/stable-diffusion/v1-inference.yaml`) - -### MainConfig - -This adds support for "main" Stable Diffusion models, and adds these -fields: - -| **Field Name** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `vae` | str | Path to a VAE to use instead of the burnt-in one | -| `variant` | ModelVariantType| Model variant type, such as "inpainting" | - -`vae` can be an absolute or relative path. If relative, its base is -taken to be the `models_dir` directory. - -`variant` is an enumerated string class with values `normal`, -`inpaint` and `depth`. If needed, it can be imported if needed from -either `invokeai.app.services.model_records` or -`invokeai.backend.model_manager.config`. - -### ONNXSD2Config - -| **Field Name** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `prediction_type` | SchedulerPredictionType | Scheduler prediction type to use, e.g. "epsilon" | -| `upcast_attention` | bool | Model requires its attention module to be upcast | - -The `SchedulerPredictionType` enum can be imported from either -`invokeai.app.services.model_records` or -`invokeai.backend.model_manager.config`. - -### Other config classes - -There are a series of such classes each discriminated by their -`ModelFormat`, including `LoRAConfig`, `IPAdapterConfig`, and so -forth. These are rarely needed outside the model manager's internal -code, but available in `invokeai.backend.model_manager.config` if -needed. There is also a Union of all ModelConfig classes, called -`AnyModelConfig` that can be imported from the same file. - -### Limitations of the Data Model - -The config hierarchy has a major limitation in its handling of the -base model type. Each model can only be compatible with one base -model, which breaks down in the event of models that are compatible -with two or more base models. For example, SD-1 VAEs also work with -SD-2 models. A partial workaround is to use `BaseModelType.Any`, which -indicates that the model is compatible with any of the base -models. This works OK for some models, such as the IP Adapter image -encoders, but is an all-or-nothing proposition. - -## Reading and Writing Model Configuration Records - -The `ModelRecordService` provides the ability to retrieve model -configuration records from SQL or YAML databases, update them, and -write them back. - -A application-wide `ModelRecordService` is created during API -initialization and can be retrieved within an invocation from the -`InvocationContext` object: - -``` -store = context.services.model_manager.store -``` - -or from elsewhere in the code by accessing -`ApiDependencies.invoker.services.model_manager.store`. - -### Creating a `ModelRecordService` - -To create a new `ModelRecordService` database or open an existing one, -you can directly create either a `ModelRecordServiceSQL` or a -`ModelRecordServiceFile` object: - -``` -from invokeai.app.services.model_records import ModelRecordServiceSQL, ModelRecordServiceFile - -store = ModelRecordServiceSQL.from_connection(connection, lock) -store = ModelRecordServiceSQL.from_db_file('/path/to/sqlite_database.db') -store = ModelRecordServiceFile.from_db_file('/path/to/database.yaml') -``` - -The `from_connection()` form is only available from the -`ModelRecordServiceSQL` class, and is used to manage records in a -previously-opened SQLITE3 database using a `sqlite3.connection` object -and a `threading.lock` object. It is intended for the specific use -case of storing the record information in the main InvokeAI database, -usually `databases/invokeai.db`. - -The `from_db_file()` methods can be used to open new connections to -the named database files. If the file doesn't exist, it will be -created and initialized. - -As a convenience, `ModelRecordServiceBase` offers two methods, -`from_db_file` and `open`, which will return either a SQL or File -implementation depending on the context. The former looks at the file -extension to determine whether to open the file as a SQL database -(".db") or as a file database (".yaml"). If the file exists, but is -either the wrong type or does not contain the expected schema -metainformation, then an appropriate `AssertionError` will be raised: - -``` -store = ModelRecordServiceBase.from_db_file('/path/to/a/file.{yaml,db}') -``` - -The `ModelRecordServiceBase.open()` method is specifically designed -for use in the InvokeAI web server. Its signature is: - -``` -def open( - cls, - config: InvokeAIAppConfig, - conn: Optional[sqlite3.Connection] = None, - lock: Optional[threading.Lock] = None - ) -> Union[ModelRecordServiceSQL, ModelRecordServiceFile]: -``` - -The way it works is as follows: - -1. Retrieve the value of the `model_config_db` option from the user's - `invokeai.yaml` config file. -2. If `model_config_db` is `auto` (the default), then: - * Use the values of `conn` and `lock` to return a `ModelRecordServiceSQL` object - opened on the passed connection and lock. - * Open up a new connection to `databases/invokeai.db` if `conn` - and/or `lock` are missing (see note below). -3. If `model_config_db` is a Path, then use `from_db_file` - to return the appropriate type of ModelRecordService. -4. If `model_config_db` is None, then retrieve the legacy - `conf_path` option from `invokeai.yaml` and use the Path - indicated there. This will default to `configs/models.yaml`. - -So a typical startup pattern would be: - -``` -import sqlite3 -from invokeai.app.services.thread import lock -from invokeai.app.services.model_records import ModelRecordServiceBase -from invokeai.app.services.config import InvokeAIAppConfig - -config = InvokeAIAppConfig.get_config() -db_conn = sqlite3.connect(config.db_path.as_posix(), check_same_thread=False) -store = ModelRecordServiceBase.open(config, db_conn, lock) -``` - -### Fetching a Model's Configuration from `ModelRecordServiceBase` - -Configurations can be retrieved in several ways. - -#### get_model(key) -> AnyModelConfig - -The basic functionality is to call the record store object's -`get_model()` method with the desired model's unique key. It returns -the appropriate subclass of ModelConfigBase: - -``` -model_conf = store.get_model('f13dd932c0c35c22dcb8d6cda4203764') -print(model_conf.path) - ->> '/tmp/models/ckpts/v1-5-pruned-emaonly.safetensors' - -``` - -If the key is unrecognized, this call raises an -`UnknownModelException`. - -#### exists(key) -> AnyModelConfig - -Returns True if a model with the given key exists in the database. - -#### search_by_path(path) -> AnyModelConfig - -Returns the configuration of the model whose path is `path`. The path -is matched using a simple string comparison and won't correctly match -models referred to by different paths (e.g. using symbolic links). - -#### search_by_name(name, base, type) -> List[AnyModelConfig] - -This method searches for models that match some combination of `name`, -`BaseType` and `ModelType`. Calling without any arguments will return -all the models in the database. - -#### all_models() -> List[AnyModelConfig] - -Return all the model configs in the database. Exactly equivalent to -calling `search_by_name()` with no arguments. - -#### search_by_tag(tags) -> List[AnyModelConfig] - -`tags` is a list of strings. This method returns a list of model -configs that contain all of the given tags. Examples: - -``` -# find all models that are marked as both SFW and as generating -# background scenery -configs = store.search_by_tag(['sfw', 'scenery']) -``` - -Note that only tags are not searchable in this way. Other fields can -be searched using a filter: - -``` -commercializable_models = [x for x in store.all_models() \ - if x.license.contains('allowCommercialUse=Sell')] -``` - -#### version() -> str - -Returns the version of the database, currently at `3.2` - -#### model_info_by_name(name, base_model, model_type) -> ModelConfigBase - -This method exists to ease the transition from the previous version of -the model manager, in which `get_model()` took the three arguments -shown above. This looks for a unique model identified by name, base -model and model type and returns it. - -The method will generate a `DuplicateModelException` if there are more -than one models that share the same type, base and name. While -unlikely, it is certainly possible to have a situation in which the -user had added two models with the same name, base and type, one -located at path `/foo/my_model` and the other at `/bar/my_model`. It -is strongly recommended to search for models using `search_by_name()`, -which can return multiple results, and then to select the desired -model and pass its key to `get_model()`. - -### Writing model configs to the database - -Several methods allow you to create and update stored model config -records. - -#### add_model(key, config) -> AnyModelConfig - -Given a key and a configuration, this will add the model's -configuration record to the database. `config` can either be a subclass of -`ModelConfigBase` (i.e. any class listed in `AnyModelConfig`), or a -`dict` of key/value pairs. In the latter case, the correct -configuration class will be picked by Pydantic's discriminated union -mechanism. - -If successful, the method will return the appropriate subclass of -`ModelConfigBase`. It will raise a `DuplicateModelException` if a -model with the same key is already in the database, or an -`InvalidModelConfigException` if a dict was passed and Pydantic -experienced a parse or validation error. - -### update_model(key, config) -> AnyModelConfig - -Given a key and a configuration, this will update the model -configuration record in the database. `config` can be either a -instance of `ModelConfigBase`, or a sparse `dict` containing the -fields to be updated. This will return an `AnyModelConfig` on success, -or raise `InvalidModelConfigException` or `UnknownModelException` -exceptions on failure. - -*** - -## Model installation - -The `ModelInstallService` class implements the -`ModelInstallServiceBase` abstract base class, and provides a one-stop -shop for all your model install needs. It provides the following -functionality: - -* Registering a model config record for a model already located on the - local filesystem, without moving it or changing its path. - -* Installing a model alreadiy located on the local filesystem, by - moving it into the InvokeAI root directory under the - `models` folder (or wherever config parameter `models_dir` - specifies). - -* Probing of models to determine their type, base type and other key - information. - -* Interface with the InvokeAI event bus to provide status updates on - the download, installation and registration process. - -* Downloading a model from an arbitrary URL and installing it in - `models_dir`. - -* Special handling for HuggingFace repo_ids to recursively download - the contents of the repository, paying attention to alternative - variants such as fp16. - -* Saving tags and other metadata about the model into the invokeai database - when fetching from a repo that provides that type of information, - (currently only HuggingFace). - -### Initializing the installer - -A default installer is created at InvokeAI api startup time and stored -in `ApiDependencies.invoker.services.model_install` and can -also be retrieved from an invocation's `context` argument with -`context.services.model_install`. - -In the event you wish to create a new installer, you may use the -following initialization pattern: - -``` -from invokeai.app.services.config import get_config -from invokeai.app.services.model_records import ModelRecordServiceSQL -from invokeai.app.services.model_install import ModelInstallService -from invokeai.app.services.download import DownloadQueueService -from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase -from invokeai.backend.util.logging import InvokeAILogger - -config = get_config() - -logger = InvokeAILogger.get_logger(config=config) -db = SqliteDatabase(config.db_path, logger) -record_store = ModelRecordServiceSQL(db, logger) -queue = DownloadQueueService() -queue.start() - -installer = ModelInstallService(app_config=config, - record_store=record_store, - download_queue=queue - ) -installer.start() -``` - -The full form of `ModelInstallService()` takes the following -required parameters: - -| **Argument** | **Type** | **Description** | -|------------------|------------------------------|------------------------------| -| `app_config` | InvokeAIAppConfig | InvokeAI app configuration object | -| `record_store` | ModelRecordServiceBase | Config record storage database | -| `download_queue` | DownloadQueueServiceBase | Download queue object | -|`session` | Optional[requests.Session] | Swap in a different Session object (usually for debugging) | - -Once initialized, the installer will provide the following methods: - -#### install_job = installer.heuristic_import(source, [config], [access_token]) - -This is a simplified interface to the installer which takes a source -string, an optional model configuration dictionary and an optional -access token. - -The `source` is a string that can be any of these forms - -1. A path on the local filesystem (`C:\\users\\fred\\model.safetensors`) -2. A Url pointing to a single downloadable model file (`https://civitai.com/models/58390/detail-tweaker-lora-lora`) -3. A HuggingFace repo_id with any of the following formats: - * `model/name` -- entire model - * `model/name:fp32` -- entire model, using the fp32 variant - * `model/name:fp16:vae` -- vae submodel, using the fp16 variant - * `model/name::vae` -- vae submodel, using default precision - * `model/name:fp16:path/to/model.safetensors` -- an individual model file, fp16 variant - * `model/name::path/to/model.safetensors` -- an individual model file, default variant - -Note that by specifying a relative path to the top of the HuggingFace -repo, you can download and install arbitrary models files. - -The variant, if not provided, will be automatically filled in with -`fp32` if the user has requested full precision, and `fp16` -otherwise. If a variant that does not exist is requested, then the -method will install whatever HuggingFace returns as its default -revision. - -`config` is an optional dict of values that will override the -autoprobed values for model type, base, scheduler prediction type, and -so forth. See [Model configuration and -probing](#model-configuration-and-probing) for details. - -`access_token` is an optional access token for accessing resources -that need authentication. - -The method will return a `ModelInstallJob`. This object is discussed -at length in the following section. - -#### install_job = installer.import_model() - -The `import_model()` method is the core of the installer. The -following illustrates basic usage: - -``` -from invokeai.app.services.model_install import ( - LocalModelSource, - HFModelSource, - URLModelSource, -) - -source1 = LocalModelSource(path='/opt/models/sushi.safetensors') # a local safetensors file -source2 = LocalModelSource(path='/opt/models/sushi_diffusers') # a local diffusers folder - -source3 = HFModelSource(repo_id='runwayml/stable-diffusion-v1-5') # a repo_id -source4 = HFModelSource(repo_id='runwayml/stable-diffusion-v1-5', subfolder='vae') # a subfolder within a repo_id -source5 = HFModelSource(repo_id='runwayml/stable-diffusion-v1-5', variant='fp16') # a named variant of a HF model -source6 = HFModelSource(repo_id='runwayml/stable-diffusion-v1-5', subfolder='OrangeMix/OrangeMix1.ckpt') # path to an individual model file - -source7 = URLModelSource(url='https://civitai.com/api/download/models/63006') # model located at a URL -source8 = URLModelSource(url='https://civitai.com/api/download/models/63006', access_token='letmein') # with an access token - -for source in [source1, source2, source3, source4, source5, source6, source7]: - install_job = installer.install_model(source) - -source2job = installer.wait_for_installs(timeout=120) -for source in sources: - job = source2job[source] - if job.complete: - model_config = job.config_out - model_key = model_config.key - print(f"{source} installed as {model_key}") - elif job.errored: - print(f"{source}: {job.error_type}.\nStack trace:\n{job.error}") - -``` - -As shown here, the `import_model()` method accepts a variety of -sources, including local safetensors files, local diffusers folders, -HuggingFace repo_ids with and without a subfolder designation, -Civitai model URLs and arbitrary URLs that point to checkpoint files -(but not to folders). - -Each call to `import_model()` return a `ModelInstallJob` job, -an object which tracks the progress of the install. - -If a remote model is requested, the model's files are downloaded in -parallel across a multiple set of threads using the download -queue. During the download process, the `ModelInstallJob` is updated -to provide status and progress information. After the files (if any) -are downloaded, the remainder of the installation runs in a single -serialized background thread. These are the model probing, file -copying, and config record database update steps. - -Multiple install jobs can be queued up. You may block until all -install jobs are completed (or errored) by calling the -`wait_for_installs()` method as shown in the code -example. `wait_for_installs()` will return a `dict` that maps the -requested source to its job. This object can be interrogated -to determine its status. If the job errored out, then the error type -and details can be recovered from `job.error_type` and `job.error`. - -The full list of arguments to `import_model()` is as follows: - -| **Argument** | **Type** | **Default** | **Description** | -|------------------|------------------------------|-------------|-------------------------------------------| -| `source` | ModelSource | None | The source of the model, Path, URL or repo_id | -| `config` | Dict[str, Any] | None | Override all or a portion of model's probed attributes | - -The next few sections describe the various types of ModelSource that -can be passed to `import_model()`. - -`config` can be used to override all or a portion of the configuration -attributes returned by the model prober. See the section below for -details. - -#### LocalModelSource - -This is used for a model that is located on a locally-accessible Posix -filesystem, such as a local disk or networked fileshare. - -| **Argument** | **Type** | **Default** | **Description** | -|------------------|------------------------------|-------------|-------------------------------------------| -| `path` | str | Path | None | Path to the model file or directory | -| `inplace` | bool | False | If set, the model file(s) will be left in their location; otherwise they will be copied into the InvokeAI root's `models` directory | - -#### URLModelSource - -This is used for a single-file model that is accessible via a URL. The -fields are: - -| **Argument** | **Type** | **Default** | **Description** | -|------------------|------------------------------|-------------|-------------------------------------------| -| `url` | AnyHttpUrl | None | The URL for the model file. | -| `access_token` | str | None | An access token needed to gain access to this file. | - -The `AnyHttpUrl` class can be imported from `pydantic.networks`. - -Ordinarily, no metadata is retrieved from these sources. However, -there is special-case code in the installer that looks for HuggingFace -and fetches the corresponding model metadata from the corresponding repo. - -#### HFModelSource - -HuggingFace has the most complicated `ModelSource` structure: - -| **Argument** | **Type** | **Default** | **Description** | -|------------------|------------------------------|-------------|-------------------------------------------| -| `repo_id` | str | None | The ID of the desired model. | -| `variant` | ModelRepoVariant | ModelRepoVariant('fp16') | The desired variant. | -| `subfolder` | Path | None | Look for the model in a subfolder of the repo. | -| `access_token` | str | None | An access token needed to gain access to a subscriber's-only model. | - -The `repo_id` is the repository ID, such as `stabilityai/sdxl-turbo`. - -The `variant` is one of the various diffusers formats that HuggingFace -supports and is used to pick out from the hodgepodge of files that in -a typical HuggingFace repository the particular components needed for -a complete diffusers model. `ModelRepoVariant` is an enum that can be -imported from `invokeai.backend.model_manager` and has the following -values: - -| **Name** | **String Value** | -|----------------------------|---------------------------| -| ModelRepoVariant.DEFAULT | "default" | -| ModelRepoVariant.FP16 | "fp16" | -| ModelRepoVariant.FP32 | "fp32" | -| ModelRepoVariant.ONNX | "onnx" | -| ModelRepoVariant.OPENVINO | "openvino" | -| ModelRepoVariant.FLAX | "flax" | - -You can also pass the string forms to `variant` directly. Note that -InvokeAI may not be able to load and run all variants. At the current -time, specifying `ModelRepoVariant.DEFAULT` will retrieve model files -that are unqualified, e.g. `pytorch_model.safetensors` rather than -`pytorch_model.fp16.safetensors`. These are usually the 32-bit -safetensors forms of the model. - -If `subfolder` is specified, then the requested model resides in a -subfolder of the main model repository. This is typically used to -fetch and install VAEs. - -Some models require you to be registered with HuggingFace and logged -in. To download these files, you must provide an -`access_token`. Internally, if no access token is provided, then -`HfFolder.get_token()` will be called to fill it in with the cached -one. - -#### Monitoring the install job process - -When you create an install job with `import_model()`, it launches the -download and installation process in the background and returns a -`ModelInstallJob` object for monitoring the process. - -The `ModelInstallJob` class has the following structure: - -| **Attribute** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `id` | `int` | Integer ID for this job | -| `status` | `InstallStatus` | An enum of [`waiting`, `downloading`, `running`, `completed`, `error` and `cancelled`]| -| `config_in` | `dict` | Overriding configuration values provided by the caller | -| `config_out` | `AnyModelConfig`| After successful completion, contains the configuration record written to the database | -| `inplace` | `boolean` | True if the caller asked to install the model in place using its local path | -| `source` | `ModelSource` | The local path, remote URL or repo_id of the model to be installed | -| `local_path` | `Path` | If a remote model, holds the path of the model after it is downloaded; if a local model, same as `source` | -| `error_type` | `str` | Name of the exception that led to an error status | -| `error` | `str` | Traceback of the error | - -If the `event_bus` argument was provided, events will also be -broadcast to the InvokeAI event bus. The events will appear on the bus -as an event of type `EventServiceBase.model_event`, a timestamp and -the following event names: - -##### `model_install_downloading` - -For remote models only, `model_install_downloading` events will be issued at regular -intervals as the download progresses. The event's payload contains the -following keys: - -| **Key** | **Type** | **Description** | -|----------------|-----------|------------------| -| `source` | str | String representation of the requested source | -| `local_path` | str | String representation of the path to the downloading model (usually a temporary directory) | -| `bytes` | int | How many bytes downloaded so far | -| `total_bytes` | int | Total size of all the files that make up the model | -| `parts` | List[Dict]| Information on the progress of the individual files that make up the model | - -The parts is a list of dictionaries that give information on each of -the components pieces of the download. The dictionary's keys are -`source`, `local_path`, `bytes` and `total_bytes`, and correspond to -the like-named keys in the main event. - -Note that downloading events will not be issued for local models, and -that downloading events occur _before_ the running event. - -##### `model_install_running` - -`model_install_running` is issued when all the required downloads have completed (if applicable) and the -model probing, copying and registration process has now started. - -The payload will contain the key `source`. - -##### `model_install_completed` - -`model_install_completed` is issued once at the end of a successful -installation. The payload will contain the keys `source`, -`total_bytes` and `key`, where `key` is the ID under which the model -has been registered. - -##### `model_install_error` - -`model_install_error` is emitted if the installation process fails for -some reason. The payload will contain the keys `source`, `error_type` -and `error`. `error_type` is a short message indicating the nature of -the error, and `error` is the long traceback to help debug the -problem. - -##### `model_install_cancelled` - -`model_install_cancelled` is issued if the model installation is -cancelled, or if one or more of its files' downloads are -cancelled. The payload will contain `source`. - -##### Following the model status - -You may poll the `ModelInstallJob` object returned by `import_model()` -to ascertain the state of the install. The job status can be read from -the job's `status` attribute, an `InstallStatus` enum which has the -enumerated values `WAITING`, `DOWNLOADING`, `RUNNING`, `COMPLETED`, -`ERROR` and `CANCELLED`. - -For convenience, install jobs also provided the following boolean -properties: `waiting`, `downloading`, `running`, `complete`, `errored` -and `cancelled`, as well as `in_terminal_state`. The last will return -True if the job is in the complete, errored or cancelled states. - -#### Model configuration and probing - -The install service uses the `invokeai.backend.model_manager.probe` -module during import to determine the model's type, base type, and -other configuration parameters. Among other things, it assigns a -default name and description for the model based on probed -fields. - -When downloading remote models is implemented, additional -configuration information, such as list of trigger terms, will be -retrieved from the HuggingFace and Civitai model repositories. - -The probed values can be overridden by providing a dictionary in the -optional `config` argument passed to `import_model()`. You may provide -overriding values for any of the model's configuration -attributes. Here is an example of setting the -`SchedulerPredictionType` and `name` for an sd-2 model: - -``` -install_job = installer.import_model( - source=HFModelSource(repo_id='stabilityai/stable-diffusion-2-1',variant='fp32'), - config=dict( - prediction_type=SchedulerPredictionType('v_prediction') - name='stable diffusion 2 base model', - ) - ) -``` - -### Other installer methods - -This section describes additional methods provided by the installer class. - -#### jobs = installer.wait_for_installs([timeout]) - -Block until all pending installs are completed or errored and then -returns a list of completed jobs. The optional `timeout` argument will -return from the call if jobs aren't completed in the specified -time. An argument of 0 (the default) will block indefinitely. - -#### jobs = installer.wait_for_job(job, [timeout]) - -Like `wait_for_installs()`, but block until a specific job has -completed or errored, and then return the job. The optional `timeout` -argument will return from the call if the job doesn't complete in the -specified time. An argument of 0 (the default) will block -indefinitely. - -#### jobs = installer.list_jobs() - -Return a list of all active and complete `ModelInstallJobs`. - -#### jobs = installer.get_job_by_source(source) - -Return a list of `ModelInstallJob` corresponding to the indicated -model source. - -#### jobs = installer.get_job_by_id(id) - -Return a list of `ModelInstallJob` corresponding to the indicated -model id. - -#### jobs = installer.cancel_job(job) - -Cancel the indicated job. - -#### installer.prune_jobs - -Remove jobs that are in a terminal state (i.e. complete, errored or -cancelled) from the job list returned by `list_jobs()` and -`get_job()`. - -#### installer.app_config, installer.record_store, installer.event_bus - -Properties that provide access to the installer's `InvokeAIAppConfig`, -`ModelRecordServiceBase` and `EventServiceBase` objects. - -#### key = installer.register_path(model_path, config), key = installer.install_path(model_path, config) - -These methods bypass the download queue and directly register or -install the model at the indicated path, returning the unique ID for -the installed model. - -Both methods accept a Path object corresponding to a checkpoint or -diffusers folder, and an optional dict of config attributes to use to -override the values derived from model probing. - -The difference between `register_path()` and `install_path()` is that -the former creates a model configuration record without changing the -location of the model in the filesystem. The latter makes a copy of -the model inside the InvokeAI models directory before registering -it. - -#### installer.unregister(key) - -This will remove the model config record for the model at key, and is -equivalent to `installer.record_store.del_model(key)` - -#### installer.delete(key) - -This is similar to `unregister()` but has the additional effect of -conditionally deleting the underlying model file(s) if they reside -within the InvokeAI models directory - -#### installer.unconditionally_delete(key) - -This method is similar to `unregister()`, but also unconditionally -deletes the corresponding model weights file(s), regardless of whether -they are inside or outside the InvokeAI models hierarchy. - -#### path = installer.download_and_cache(remote_source, [access_token], [timeout]) - -This utility routine will download the model file located at source, -cache it, and return the path to the cached file. It does not attempt -to determine the model type, probe its configuration values, or -register it with the models database. - -You may provide an access token if the remote source requires -authorization. The call will block indefinitely until the file is -completely downloaded, cancelled or raises an error of some sort. If -you provide a timeout (in seconds), the call will raise a -`TimeoutError` exception if the download hasn't completed in the -specified period. - -You may use this mechanism to request any type of file, not just a -model. The file will be stored in a subdirectory of -`INVOKEAI_ROOT/models/.cache`. If the requested file is found in the -cache, its path will be returned without redownloading it. - -Be aware that the models cache is cleared of infrequently-used files -and directories at regular intervals when the size of the cache -exceeds the value specified in Invoke's `convert_cache` configuration -variable. - -#### installer.start(invoker) - -The `start` method is called by the API initialization routines when -the API starts up. Its effect is to call `sync_to_config()` to -synchronize the model record store database with what's currently on -disk. - -*** - -## Get on line: The Download Queue - -InvokeAI can download arbitrary files using a multithreaded background -download queue. Internally, the download queue is used for installing -models located at remote locations. The queue is implemented by the -`DownloadQueueService` defined in -`invokeai.app.services.download_manager`. However, most of the -implementation is spread out among several files in -`invokeai/backend/model_manager/download/*` - -A default download queue is located in -`ApiDependencies.invoker.services.download_queue`. However, you can -create additional instances if you need to isolate your queue from the -main one. - -### A job for every task - -The queue operates on a series of download job objects. These objects -specify the source and destination of the download, and keep track of -the progress of the download. Jobs come in a variety of shapes and -colors as they are progressively specialized for particular download -task. - -The basic job is the `DownloadJobBase`, a pydantic object with the -following fields: - -| **Field** | **Type** | **Default** | **Description** | -|----------------|-----------------|---------------|-----------------| -| `id` | int | | Job ID, an integer >= 0 | -| `priority` | int | 10 | Job priority. Lower priorities run before higher priorities | -| `source` | str | | Where to download from (specialized types used in subclasses)| -| `destination` | Path | | Where to download to | -| `status` | DownloadJobStatus| Idle | Job's status (see below) | -| `event_handlers` | List[DownloadEventHandler]| | Event handlers (see below) | -| `job_started` | float | | Timestamp for when the job started running | -| `job_ended` | float | | Timestamp for when the job completed or errored out | -| `job_sequence` | int | | A counter that is incremented each time a model is dequeued | -| `error` | Exception | | A copy of the Exception that caused an error during download | - -When you create a job, you can assign it a `priority`. If multiple -jobs are queued, the job with the lowest priority runs first. (Don't -blame me! The Unix developers came up with this convention.) - -Every job has a `source` and a `destination`. `source` is a string in -the base class, but subclassses redefine it more specifically. - -The `destination` must be the Path to a file or directory on the local -filesystem. If the Path points to a new or existing file, then the -source will be stored under that filename. If the Path ponts to an -existing directory, then the downloaded file will be stored inside the -directory, usually using the name assigned to it at the remote site in -the `content-disposition` http field. - -When the job is submitted, it is assigned a numeric `id`. The id can -then be used to control the job, such as starting, stopping and -cancelling its download. - -The `status` field is updated by the queue to indicate where the job -is in its lifecycle. Values are defined in the string enum -`DownloadJobStatus`, a symbol available from -`invokeai.app.services.download_manager`. Possible values are: - -| **Value** | **String Value** | **Description** | -|--------------|---------------------|-------------------| -| `IDLE` | idle | Job created, but not submitted to the queue | -| `ENQUEUED` | enqueued | Job is patiently waiting on the queue | -| `RUNNING` | running | Job is running! | -| `PAUSED` | paused | Job was paused and can be restarted | -| `COMPLETED` | completed | Job has finished its work without an error | -| `ERROR` | error | Job encountered an error and will not run again| -| `CANCELLED` | cancelled | Job was cancelled and will not run (again) | - -`job_started`, `job_ended` and `job_sequence` indicate when the job -was started (using a python timestamp), when it completed, and the -order in which it was taken off the queue. These are mostly used for -debugging and performance testing. - -In case of an error, the Exception that caused the error will be -placed in the `error` field, and the job's status will be set to -`DownloadJobStatus.ERROR`. - -After an error occurs, any partially downloaded files will be deleted -from disk, unless `preserve_partial_downloads` was set to True at job -creation time (or set to True any time before the error -occurred). Note that since all InvokeAI model install operations -involve downloading files to a temporary directory that has a limited -lifetime, this flag is not used by the model installer. - -There are a series of subclasses of `DownloadJobBase` that provide -support for specific types of downloads. These are: - -#### DownloadJobPath - -This subclass redefines `source` to be a filesystem Path. It is used -to move a file or directory from the `source` to the `destination` -paths in the background using a uniform event-based infrastructure. - -#### DownloadJobRemoteSource - -This subclass adds the following fields to the job: - -| **Field** | **Type** | **Default** | **Description** | -|----------------|-----------------|---------------|-----------------| -| `bytes` | int | 0 | bytes downloaded so far | -| `total_bytes` | int | 0 | total size to download | -| `access_token` | Any | None | an authorization token to present to the remote source | - -The job will start out with 0/0 in its bytes/total_bytes fields. Once -it starts running, `total_bytes` will be populated from information -provided in the HTTP download header (if available), and the number of -bytes downloaded so far will be progressively incremented. - -#### DownloadJobURL - -This is a subclass of `DownloadJobBase`. It redefines `source` to be a -Pydantic `AnyHttpUrl` object, which enforces URL validation checking -on the field. - -Note that the installer service defines an additional subclass of -`DownloadJobRemoteSource` that accepts HuggingFace repo_ids in -addition to URLs. This is discussed later in this document. - -### Event handlers - -While a job is being downloaded, the queue will emit events at -periodic intervals. A typical series of events during a successful -download session will look like this: - -* enqueued -* running -* running -* running -* completed - -There will be a single enqueued event, followed by one or more running -events, and finally one `completed`, `error` or `cancelled` -events. - -It is possible for a caller to pause download temporarily, in which -case the events may look something like this: - -* enqueued -* running -* running -* paused -* running -* completed - -The download queue logs when downloads start and end (unless `quiet` -is set to True at initialization time) but doesn't log any progress -events. You will probably want to be alerted to events during the -download job and provide more user feedback. In order to intercept and -respond to events you may install a series of one or more event -handlers in the job. Whenever the job's status changes, the chain of -event handlers is traversed and executed in the same thread that the -download job is running in. - -Event handlers have the signature `Callable[["DownloadJobBase"], -None]`, i.e. - -``` -def handler(job: DownloadJobBase): - pass -``` - -A typical handler will examine `job.status` and decide if there's -something to be done. This can include cancelling or erroring the job, -but more typically is used to report on the job status to the user -interface or to perform certain actions on successful completion of -the job. - -Event handlers can be attached to a job at creation time. In addition, -you can create a series of default handlers that are attached to the -queue object itself. These handlers will be executed for each job -after the job's own handlers (if any) have run. - -During a download, running events are issued every time roughly 1% of -the file is transferred. This is to provide just enough granularity to -update a tqdm progress bar smoothly. - -Handlers can be added to a job after the fact using the job's -`add_event_handler` method: - -``` -job.add_event_handler(my_handler) -``` - -All handlers can be cleared using the job's `clear_event_handlers()` -method. Note that it might be a good idea to pause the job before -altering its handlers. - -### Creating a download queue object - -The `DownloadQueueService` constructor takes the following arguments: - -| **Argument** | **Type** | **Default** | **Description** | -|----------------|-----------------|---------------|-----------------| -| `event_handlers` | List[DownloadEventHandler] | [] | Event handlers | -| `max_parallel_dl` | int | 5 | Maximum number of simultaneous downloads allowed | -| `requests_session` | requests.sessions.Session | None | An alternative requests Session object to use for the download | -| `quiet` | bool | False| Do work quietly without issuing log messages | - -A typical initialization sequence will look like: - -``` -from invokeai.app.services.download_manager import DownloadQueueService - -def log_download_event(job: DownloadJobBase): - logger.info(f'job={job.id}: status={job.status}') - -queue = DownloadQueueService( - event_handlers=[log_download_event] - ) -``` - -Event handlers can be provided to the queue at initialization time as -shown in the example. These will be automatically appended to the -handler list for any job that is submitted to this queue. - -`max_parallel_dl` sets the number of simultaneous active downloads -that are allowed. The default of five has not been benchmarked in any -way, but seems to give acceptable performance. - -`requests_session` can be used to provide a `requests` module Session -object that will be used to stream remote URLs to disk. This facility -was added for use in the module's unit tests to simulate a remote web -server, but may be useful in other contexts. - -`quiet` will prevent the queue from issuing any log messages at the -INFO or higher levels. - -### Submitting a download job - -You can submit a download job to the queue either by creating the job -manually and passing it to the queue's `submit_download_job()` method, -or using the `create_download_job()` method, which will do the same -thing on your behalf. - -To use the former method, follow this example: - -``` -job = DownloadJobRemoteSource( - source='http://www.civitai.com/models/13456', - destination='/tmp/models/', - event_handlers=[my_handler1, my_handler2], # if desired - ) -queue.submit_download_job(job, start=True) -``` - -`submit_download_job()` takes just two arguments: the job to submit, -and a flag indicating whether to immediately start the job (defaulting -to True). If you choose not to start the job immediately, you can -start it later by calling the queue's `start_job()` or -`start_all_jobs()` methods, which are described later. - -To have the queue create the job for you, follow this example instead: - -``` -job = queue.create_download_job( - source='http://www.civitai.com/models/13456', - destdir='/tmp/models/', - filename='my_model.safetensors', - event_handlers=[my_handler1, my_handler2], # if desired - start=True, - ) -``` - -The `filename` argument forces the downloader to use the specified -name for the file rather than the name provided by the remote source, -and is equivalent to manually specifying a destination of -`/tmp/models/my_model.safetensors' in the submitted job. - -Here is the full list of arguments that can be provided to -`create_download_job()`: - -| **Argument** | **Type** | **Default** | **Description** | -|------------------|------------------------------|-------------|-------------------------------------------| -| `source` | Union[str, Path, AnyHttpUrl] | | Download remote or local source | -| `destdir` | Path | | Destination directory for downloaded file | -| `filename` | Path | None | Filename for downloaded file | -| `start` | bool | True | Enqueue the job immediately | -| `priority` | int | 10 | Starting priority for this job | -| `access_token` | str | None | Authorization token for this resource | -| `event_handlers` | List[DownloadEventHandler] | [] | Event handlers for this job | - -Internally, `create_download_job()` has a little bit of internal logic -that looks at the type of the source and selects the right subclass of -`DownloadJobBase` to create and enqueue. - -**TODO**: move this logic into its own method for overriding in -subclasses. - -### Job control - -Prior to completion, jobs can be controlled with a series of queue -method calls. Do not attempt to modify jobs by directly writing to -their fields, as this is likely to lead to unexpected results. - -Any method that accepts a job argument may raise an -`UnknownJobIDException` if the job has not yet been submitted to the -queue or was not created by this queue. - -#### queue.join() - -This method will block until all the active jobs in the queue have -reached a terminal state (completed, errored or cancelled). - -#### queue.wait_for_job(job, [timeout]) - -This method will block until the indicated job has reached a terminal -state (completed, errored or cancelled). If the optional timeout is -provided, the call will block for at most timeout seconds, and raise a -TimeoutError otherwise. - -#### jobs = queue.list_jobs() - -This will return a list of all jobs, including ones that have not yet -been enqueued and those that have completed or errored out. - -#### job = queue.id_to_job(int) - -This method allows you to recover a submitted job using its ID. - -#### queue.prune_jobs() - -Remove completed and errored jobs from the job list. - -#### queue.start_job(job) - -If the job was submitted with `start=False`, then it can be started -using this method. - -#### queue.pause_job(job) - -This will temporarily pause the job, if possible. It can later be -restarted and pick up where it left off using `queue.start_job()`. - -#### queue.cancel_job(job) - -This will cancel the job if possible and clean up temporary files and -other resources that it might have been using. - -#### queue.start_all_jobs(), queue.pause_all_jobs(), queue.cancel_all_jobs() - -This will start/pause/cancel all jobs that have been submitted to the -queue and have not yet reached a terminal state. - -*** - -## This Meta be Good: Model Metadata Storage - -The modules found under `invokeai.backend.model_manager.metadata` -provide a straightforward API for fetching model metadatda from online -repositories. Currently only HuggingFace is supported. However, the -modules are easily extended for additional repos, provided that they -have defined APIs for metadata access. - -Metadata comprises any descriptive information that is not essential -for getting the model to run. For example "author" is metadata, while -"type", "base" and "format" are not. The latter fields are part of the -model's config, as defined in `invokeai.backend.model_manager.config`. - -### Example Usage - -``` -from invokeai.backend.model_manager.metadata import ( - AnyModelRepoMetadata, -) -# to access the initialized sql database -from invokeai.app.api.dependencies import ApiDependencies - -hf = HuggingFaceMetadataFetch() - -# fetch the metadata -model_metadata = hf.from_id("") - -assert isinstance(model_metadata, HuggingFaceMetadata) -``` - -### Structure of the Metadata objects - -There is a short class hierarchy of Metadata objects, all of which -descend from the Pydantic `BaseModel`. - -#### `ModelMetadataBase` - -This is the common base class for metadata: - -| **Field Name** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `name` | str | Repository's name for the model | -| `author` | str | Model's author | -| `tags` | Set[str] | Model tags | - -Note that the model config record also has a `name` field. It is -intended that the config record version be locally customizable, while -the metadata version is read-only. However, enforcing this is expected -to be part of the business logic. - -Descendents of the base add additional fields. - -#### `HuggingFaceMetadata` - -This descends from `ModelMetadataBase` and adds the following fields: - -| **Field Name** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `type` | Literal["huggingface"] | Used for the discriminated union of metadata classes| -| `id` | str | HuggingFace repo_id | -| `tag_dict` | Dict[str, Any] | A dictionary of tag/value pairs provided in addition to `tags` | -| `last_modified`| datetime | Date of last commit of this model to the repo | -| `files` | List[Path] | List of the files in the model repo | - -#### `AnyModelRepoMetadata` - -This is a discriminated Union of `HuggingFaceMetadata`. - -### Fetching Metadata from Online Repos - -The `HuggingFaceMetadataFetch` class will -retrieve metadata from its corresponding repository and return -`AnyModelRepoMetadata` objects. Their base class -`ModelMetadataFetchBase` is an abstract class that defines two -methods: `from_url()` and `from_id()`. The former accepts the type of -model URLs that the user will try to cut and paste into the model -import form. The latter accepts a string ID in the format recognized -by the repository of choice. Both methods return an -`AnyModelRepoMetadata`. - -The base class also has a class method `from_json()` which will take -the JSON representation of a `ModelMetadata` object, validate it, and -return the corresponding `AnyModelRepoMetadata` object. - -When initializing one of the metadata fetching classes, you may -provide a `requests.Session` argument. This allows you to customize -the low-level HTTP fetch requests and is used, for instance, in the -testing suite to avoid hitting the internet. - -The HuggingFace fetcher subclass add additional repo-specific fetching methods: - -#### HuggingFaceMetadataFetch - -This overrides its base class `from_json()` method to return a -`HuggingFaceMetadata` object directly. - -### Metadata Storage - -The `ModelConfigBase` stores this response in the `source_api_response` field -as a JSON blob. - -*** - -## The Lowdown on the ModelLoadService - -The `ModelLoadService` is responsible for loading a named model into -memory so that it can be used for inference. Despite the fact that it -does a lot under the covers, it is very straightforward to use. - -An application-wide model loader is created at API initialization time -and stored in -`ApiDependencies.invoker.services.model_loader`. However, you can -create alternative instances if you wish. - -### Creating a ModelLoadService object - -The class is defined in -`invokeai.app.services.model_load`. It is initialized with -an InvokeAIAppConfig object, from which it gets configuration -information such as the user's desired GPU and precision, and with a -previously-created `ModelRecordServiceBase` object, from which it -loads the requested model's configuration information. - -Here is a typical initialization pattern: - -``` -from invokeai.app.services.config import InvokeAIAppConfig -from invokeai.app.services.model_load import ModelLoadService, ModelLoaderRegistry - -config = InvokeAIAppConfig.get_config() -ram_cache = ModelCache( - max_cache_size=config.ram_cache_size, max_vram_cache_size=config.vram_cache_size, logger=logger -) -convert_cache = ModelConvertCache( - cache_path=config.models_convert_cache_path, max_size=config.convert_cache_size -) -loader = ModelLoadService( - app_config=config, - ram_cache=ram_cache, - convert_cache=convert_cache, - registry=ModelLoaderRegistry -) -``` - -### load_model(model_config, [submodel_type], [context]) -> LoadedModel - -The `load_model()` method takes an `AnyModelConfig` returned by -`ModelRecordService.get_model()` and returns the corresponding loaded -model. It loads the model into memory, gets the model ready for use, -and returns a `LoadedModel` object. - -The optional second argument, `subtype` is a `SubModelType` string -enum, such as "vae". It is mandatory when used with a main model, and -is used to select which part of the main model to load. - -The optional third argument, `context` can be provided by -an invocation to trigger model load event reporting. See below for -details. - -The returned `LoadedModel` object contains a copy of the configuration -record returned by the model record `get_model()` method, as well as -the in-memory loaded model: - -| **Attribute Name** | **Type** | **Description** | -|----------------|-----------------|------------------| -| `config` | AnyModelConfig | A copy of the model's configuration record for retrieving base type, etc. | -| `model` | AnyModel | The instantiated model (details below) | - -### get_model_by_key(key, [submodel]) -> LoadedModel - -The `get_model_by_key()` method will retrieve the model using its -unique database key. For example: - -loaded_model = loader.get_model_by_key('f13dd932c0c35c22dcb8d6cda4203764', SubModelType('vae')) - -`get_model_by_key()` may raise any of the following exceptions: - -* `UnknownModelException` -- key not in database -* `ModelNotFoundException` -- key in database but model not found at path -* `NotImplementedException` -- the loader doesn't know how to load this type of model - -### Using the Loaded Model in Inference - -`LoadedModel` acts as a context manager. The context loads the model -into the execution device (e.g. VRAM on CUDA systems), locks the model -in the execution device for the duration of the context, and returns -the model. Use it like this: - -``` -loaded_model_= loader.get_model_by_key('f13dd932c0c35c22dcb8d6cda4203764', SubModelType('vae')) -with loaded_model as vae: - image = vae.decode(latents)[0] -``` - -The object returned by the LoadedModel context manager is an -`AnyModel`, which is a Union of `ModelMixin`, `torch.nn.Module`, -`IAIOnnxRuntimeModel`, `IPAdapter`, `IPAdapterPlus`, and -`EmbeddingModelRaw`. `ModelMixin` is the base class of all diffusers -models, `EmbeddingModelRaw` is used for LoRA and TextualInversion -models. The others are obvious. - -In addition, you may call `LoadedModel.model_on_device()`, a context -manager that returns a tuple of the model's state dict in CPU and the -model itself in VRAM. It is used to optimize the LoRA patching and -unpatching process: - -``` -loaded_model_= loader.get_model_by_key('f13dd932c0c35c22dcb8d6cda4203764', SubModelType('vae')) -with loaded_model.model_on_device() as (state_dict, vae): - image = vae.decode(latents)[0] -``` - -Since not all models have state dicts, the `state_dict` return value -can be None. - - -### Emitting model loading events - -When the `context` argument is passed to `load_model_*()`, it will -retrieve the invocation event bus from the passed `InvocationContext` -object to emit events on the invocation bus. The two events are -"model_load_started" and "model_load_completed". Both carry the -following payload: - -``` -payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - model_key=model_key, - submodel_type=submodel, - hash=model_info.hash, - location=str(model_info.location), - precision=str(model_info.precision), -) -``` - -### Adding Model Loaders - -Model loaders are small classes that inherit from the `ModelLoader` -base class. They typically implement one method `_load_model()` whose -signature is: - -``` -def _load_model( - self, - model_path: Path, - model_variant: Optional[ModelRepoVariant] = None, - submodel_type: Optional[SubModelType] = None, -) -> AnyModel: -``` - -`_load_model()` will be passed the path to the model on disk, an -optional repository variant (used by the diffusers loaders to select, -e.g. the `fp16` variant, and an optional submodel_type for main and -onnx models. - -To install a new loader, place it in -`invokeai/backend/model_manager/load/model_loaders`. Inherit from -`ModelLoader` and use the `@ModelLoaderRegistry.register()` decorator to -indicate what type of models the loader can handle. - -Here is a complete example from `generic_diffusers.py`, which is able -to load several different diffusers types: - -``` -from pathlib import Path -from typing import Optional - -from invokeai.backend.model_manager import ( - AnyModel, - BaseModelType, - ModelFormat, - ModelRepoVariant, - ModelType, - SubModelType, -) -from .. import ModelLoader, ModelLoaderRegistry - - -@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.CLIPVision, format=ModelFormat.Diffusers) -@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.T2IAdapter, format=ModelFormat.Diffusers) -class GenericDiffusersLoader(ModelLoader): - """Class to load simple diffusers models.""" - - def _load_model( - self, - model_path: Path, - model_variant: Optional[ModelRepoVariant] = None, - submodel_type: Optional[SubModelType] = None, - ) -> AnyModel: - model_class = self._get_hf_load_class(model_path) - if submodel_type is not None: - raise Exception(f"There are no submodels in models of type {model_class}") - variant = model_variant.value if model_variant else None - result: AnyModel = model_class.from_pretrained(model_path, torch_dtype=self._torch_dtype, variant=variant) # type: ignore - return result -``` - -Note that a loader can register itself to handle several different -model types. An exception will be raised if more than one loader tries -to register the same model type. - -#### Conversion - -Some models require conversion to diffusers format before they can be -loaded. These loaders should override two additional methods: - -``` -_needs_conversion(self, config: AnyModelConfig, model_path: Path, dest_path: Path) -> bool -_convert_model(self, config: AnyModelConfig, model_path: Path, output_path: Path) -> Path: -``` - -The first method accepts the model configuration, the path to where -the unmodified model is currently installed, and a proposed -destination for the converted model. This method returns True if the -model needs to be converted. It typically does this by comparing the -last modification time of the original model file to the modification -time of the converted model. In some cases you will also want to check -the modification date of the configuration record, in the event that -the user has changed something like the scheduler prediction type that -will require the model to be re-converted. See `controlnet.py` for an -example of this logic. - -The second method accepts the model configuration, the path to the -original model on disk, and the desired output path for the converted -model. It does whatever it needs to do to get the model into diffusers -format, and returns the Path of the resulting model. (The path should -ordinarily be the same as `output_path`.) - -## The ModelManagerService object - -For convenience, the API provides a `ModelManagerService` object which -gives a single point of access to the major model manager -services. This object is created at initialization time and can be -found in the global `ApiDependencies.invoker.services.model_manager` -object, or in `context.services.model_manager` from within an -invocation. - -In the examples below, we have retrieved the manager using: - -``` -mm = ApiDependencies.invoker.services.model_manager -``` - -The following properties and methods will be available: - -### mm.store - -This retrieves the `ModelRecordService` associated with the -manager. Example: - -``` -configs = mm.store.get_model_by_attr(name='stable-diffusion-v1-5') -``` - -### mm.install - -This retrieves the `ModelInstallService` associated with the manager. -Example: - -``` -job = mm.install.heuristic_import(`https://civitai.com/models/58390/detail-tweaker-lora-lora`) -``` - -### mm.load - -This retrieves the `ModelLoaderService` associated with the manager. Example: - -``` -configs = mm.store.get_model_by_attr(name='stable-diffusion-v1-5') -assert len(configs) > 0 - -loaded_model = mm.load.load_model(configs[0]) -``` - -The model manager also offers a few convenience shortcuts for loading -models: - -### mm.load_model_by_config(model_config, [submodel], [context]) -> LoadedModel - -Same as `mm.load.load_model()`. - -### mm.load_model_by_attr(model_name, base_model, model_type, [submodel], [context]) -> LoadedModel - -This accepts the combination of the model's name, type and base, which -it passes to the model record config store for retrieval. If a unique -model config is found, this method returns a `LoadedModel`. It can -raise the following exceptions: - -``` -UnknownModelException -- model with these attributes not known -NotImplementedException -- the loader doesn't know how to load this type of model -ValueError -- more than one model matches this combination of base/type/name -``` - -### mm.load_model_by_key(key, [submodel], [context]) -> LoadedModel - -This method takes a model key, looks it up using the -`ModelRecordServiceBase` object in `mm.store`, and passes the returned -model configuration to `load_model_by_config()`. It may raise a -`NotImplementedException`. - -## Invocation Context Model Manager API - -Within invocations, the following methods are available from the -`InvocationContext` object: - -### context.download_and_cache_model(source) -> Path - -This method accepts a `source` of a remote model, downloads and caches -it locally, and then returns a Path to the local model. The source can -be a direct download URL or a HuggingFace repo_id. - -In the case of HuggingFace repo_id, the following variants are -recognized: - -* stabilityai/stable-diffusion-v4 -- default model -* stabilityai/stable-diffusion-v4:fp16 -- fp16 variant -* stabilityai/stable-diffusion-v4:fp16:vae -- the fp16 vae subfolder -* stabilityai/stable-diffusion-v4:onnx:vae -- the onnx variant vae subfolder - -You can also point at an arbitrary individual file within a repo_id -directory using this syntax: - -* stabilityai/stable-diffusion-v4::/checkpoints/sd4.safetensors - -### context.load_local_model(model_path, [loader]) -> LoadedModel - -This method loads a local model from the indicated path, returning a -`LoadedModel`. The optional loader is a Callable that accepts a Path -to the object, and returns a `AnyModel` object. If no loader is -provided, then the method will use `torch.load()` for a .ckpt or .bin -checkpoint file, `safetensors.torch.load_file()` for a safetensors -checkpoint file, or `cls.from_pretrained()` for a directory that looks -like a diffusers directory. - -### context.load_remote_model(source, [loader]) -> LoadedModel - -This method accepts a `source` of a remote model, downloads and caches -it locally, loads it, and returns a `LoadedModel`. The source can be a -direct download URL or a HuggingFace repo_id. - -In the case of HuggingFace repo_id, the following variants are -recognized: - -* stabilityai/stable-diffusion-v4 -- default model -* stabilityai/stable-diffusion-v4:fp16 -- fp16 variant -* stabilityai/stable-diffusion-v4:fp16:vae -- the fp16 vae subfolder -* stabilityai/stable-diffusion-v4:onnx:vae -- the onnx variant vae subfolder - -You can also point at an arbitrary individual file within a repo_id -directory using this syntax: - -* stabilityai/stable-diffusion-v4::/checkpoints/sd4.safetensors - - - diff --git a/docs-old/contributing/NEW_MODEL_INTEGRATION.md b/docs-old/contributing/NEW_MODEL_INTEGRATION.md deleted file mode 100644 index 54fc52fcd77..00000000000 --- a/docs-old/contributing/NEW_MODEL_INTEGRATION.md +++ /dev/null @@ -1,1254 +0,0 @@ -# InvokeAI - New Model Type Integration Checklist - -This documentation describes all the steps required to integrate a new model type into InvokeAI. The implementations of FLUX.1, FLUX.2 Klein, SD3, SDXL, and Z-Image serve as references. - ---- - -## Table of Contents - -1. [Backend: Model Manager](#1-backend-model-manager) -2. [Backend: Model Configs](#2-backend-model-configs) -3. [Backend: Model Loader](#3-backend-model-loader) -4. [Backend: Invocations](#4-backend-invocations) -5. [Backend: Sampling and Denoise](#5-backend-sampling-and-denoise) -6. [Frontend: Graph Building](#6-frontend-graph-building) -7. [Frontend: State Management](#7-frontend-state-management) -8. [Frontend: Parameter Recall](#8-frontend-parameter-recall) -9. [Metadata and Generation Modes](#9-metadata-and-generation-modes) -10. [Starter Models](#10-starter-models) -11. [Optional Features](#11-optional-features) - ---- - -## 1. Backend: Model Manager - -### 1.1 Add BaseModelType - -**File:** `invokeai/backend/model_manager/taxonomy.py` - -```python -class BaseModelType(str, Enum): - # Existing types - StableDiffusion1 = "sd-1" - StableDiffusion2 = "sd-2" - StableDiffusionXL = "sdxl" - Flux = "flux" - Flux2 = "flux2" # FLUX.2 Klein - SD3 = "sd-3" - ZImage = "z-image" - # NEW: - NewModel = "newmodel" -``` - -### 1.2 Add Variant Type (if needed) - -**File:** `invokeai/backend/model_manager/taxonomy.py` - -```python -# Examples of existing variants: -class FluxVariantType(str, Enum): - Schnell = "schnell" - Dev = "dev" - DevFill = "dev_fill" - -class Flux2VariantType(str, Enum): - Klein4B = "klein_4b" # Qwen3 4B encoder - Klein9B = "klein_9b" # Qwen3 8B distilled - Klein9BBase = "klein_9b_base" - -# NEW (if needed): -class NewModelVariantType(str, Enum): - VariantA = "variant_a" - VariantB = "variant_b" -``` - -### 1.3 Define Default Settings - -**File:** `invokeai/backend/model_manager/configs/main.py` - -```python -class MainModelDefaultSettings: - @staticmethod - def from_base(base: BaseModelType, variant: AnyVariant | None = None): - match base: - case BaseModelType.Flux2: - if variant == Flux2VariantType.Klein9BBase: - return MainModelDefaultSettings(steps=28, cfg_scale=1.0, ...) - return MainModelDefaultSettings(steps=4, cfg_scale=1.0, ...) - # NEW: - case BaseModelType.NewModel: - return MainModelDefaultSettings(steps=20, cfg_scale=7.0, ...) -``` - -### Backend Model Manager Checklist - -- [ ] Extend `BaseModelType` enum (`taxonomy.py`) -- [ ] Create variant enum if needed (`taxonomy.py`) -- [ ] Update `AnyVariant` union (`taxonomy.py`) -- [ ] Add default settings in `from_base()` (`configs/main.py`) - ---- - -## 2. Backend: Model Configs - -### 2.1 Create Main Model Config - -**File:** `invokeai/backend/model_manager/configs/main.py` - -```python -# Checkpoint Format -@ModelConfigFactory.register -class Main_Checkpoint_NewModel_Config(Checkpoint_Config_Base): - type: Literal[ModelType.Main] = ModelType.Main - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel - format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint - variant: NewModelVariantType = NewModelVariantType.VariantA - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict) -> Self: - if not cls._validate_is_newmodel(mod): - raise NotAMatchError("Not a NewModel") - variant = cls._get_variant_or_raise(mod) - return cls(..., variant=variant) - -# Diffusers Format -@ModelConfigFactory.register -class Main_Diffusers_NewModel_Config(Diffusers_Config_Base): - type: Literal[ModelType.Main] = ModelType.Main - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers -``` - -### 2.2 Detection Helper Functions - -**File:** `invokeai/backend/model_manager/configs/main.py` - -```python -def _is_newmodel(state_dict: dict) -> bool: - """Detect if state dict belongs to NewModel architecture.""" - # Example FLUX.2 Klein detection: - # - context_embedder.weight shape[1] > 4096 (Qwen3 vs T5) - # - img_in.weight shape[1] == 128 (32 latent channels × 4) - required_keys = ["transformer_blocks.0.attn.to_q.weight", ...] - return all(key in state_dict for key in required_keys) - -def _get_newmodel_variant(state_dict: dict) -> NewModelVariantType: - """Determine variant from state dict.""" - # Example FLUX.2: context_in_dim distinguishes Klein 4B/9B - context_dim = state_dict["context_embedder.weight"].shape[1] - if context_dim == 7680: - return NewModelVariantType.VariantA - return NewModelVariantType.VariantB -``` - -### 2.3 VAE Config (if custom VAE) - -**File:** `invokeai/backend/model_manager/configs/vae.py` - -```python -@ModelConfigFactory.register -class VAE_Checkpoint_NewModel_Config(VAE_Checkpoint_Base): - type: Literal[ModelType.VAE] = ModelType.VAE - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, ...) -> Self: - if not _is_newmodel_vae(mod.state_dict): - raise NotAMatchError() - return cls(...) - -def _is_newmodel_vae(state_dict: dict) -> bool: - # Example FLUX.2: Check for BN layers (bn.running_mean) - return "encoder.bn.running_mean" in state_dict -``` - -### 2.4 Text Encoder Config (if custom encoder) - -**File:** `invokeai/backend/model_manager/configs/[encoder_type].py` - -```python -def _has_newmodel_encoder_keys(state_dict: dict) -> bool: - """Check if state dict contains NewModel encoder keys.""" - required_keys = ["model.layers.0.", "model.embed_tokens.weight"] - return any( - key.startswith(indicator) or key == indicator - for key in state_dict.keys() - for indicator in required_keys - if isinstance(key, str) - ) - -@ModelConfigFactory.register -class NewModelEncoder_Checkpoint_Config(Checkpoint_Config_Base): - """Configuration for single-file NewModel Encoder models.""" - - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.NewModelEncoder] = Field(default=ModelType.NewModelEncoder) - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict) -> Self: - raise_if_not_file(mod) - raise_for_override_fields(cls, override_fields) - - if not _has_newmodel_encoder_keys(mod.load_state_dict()): - raise NotAMatchError("state dict does not look like a NewModel encoder") - - return cls(**override_fields) - -@ModelConfigFactory.register -class NewModelEncoder_Diffusers_Config(Config_Base): - """Configuration for NewModel Encoder in diffusers directory format.""" - - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.NewModelEncoder] = Field(default=ModelType.NewModelEncoder) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict) -> Self: - raise_if_not_dir(mod) - raise_for_override_fields(cls, override_fields) - - # Check for text_encoder config - config_path = mod.path / "text_encoder" / "config.json" - if not config_path.exists(): - raise NotAMatchError(f"config file not found: {config_path}") - - raise_for_class_name(config_path, {"NewModelForCausalLM"}) - - return cls(**override_fields) -``` - -Examples of existing implementations: -- `t5_encoder.py` - T5 Encoder for FLUX.1, SD3 -- `qwen3_encoder.py` - Qwen3 Encoder for FLUX.2 Klein, Z-Image -- `clip_embed.py` - CLIP Encoder for SDXL, SD3 - -### 2.5 Update AnyModelConfig Union - -**File:** `invokeai/backend/model_manager/configs/factory.py` - -```python -AnyModelConfig = Annotated[ - # ... existing configs - Main_Checkpoint_NewModel_Config | - Main_Diffusers_NewModel_Config | - VAE_Checkpoint_NewModel_Config, - Discriminator(...) -] -``` - -### Backend Model Configs Checklist - -- [ ] Create main checkpoint config (`configs/main.py`) -- [ ] Create main diffusers config (`configs/main.py`) -- [ ] Create detection helper functions (`_is_newmodel()`, `_get_variant()`) -- [ ] Create VAE config if custom VAE (`configs/vae.py`) -- [ ] Create text encoder config if custom encoder -- [ ] Update `AnyModelConfig` union (`configs/factory.py`) - ---- - -## 3. Backend: Model Loader - -### 3.1 Create Model Loader - -**File:** `invokeai/backend/model_manager/load/model_loaders/[newmodel].py` - -```python -@ModelLoaderRegistry.register( - base=BaseModelType.NewModel, - type=ModelType.Main, - format=ModelFormat.Checkpoint -) -class NewModelLoader(ModelLoader): - def _load_model(self, config: AnyModelConfig, submodel_type: SubModelType | None) -> AnyModel: - # Load and convert state dict - state_dict = self._load_state_dict(config.path) - - # If format conversion needed (e.g., BFL → Diffusers): - if self._is_bfl_format(state_dict): - state_dict = self._convert_bfl_to_diffusers(state_dict) - - # Instantiate model - model = NewModelTransformer(config=model_config) - model.load_state_dict(state_dict) - return model -``` - -### 3.2 VAE Loader (if custom VAE) - -**File:** `invokeai/backend/model_manager/load/model_loaders/[newmodel].py` - -```python -@ModelLoaderRegistry.register( - base=BaseModelType.NewModel, - type=ModelType.VAE, - format=ModelFormat.Checkpoint -) -class NewModelVAELoader(ModelLoader): - def _load_model(self, config, submodel_type) -> AnyModel: - # Example FLUX.2: AutoencoderKLFlux2 with BN layers - from diffusers import AutoencoderKLFlux2 - vae = AutoencoderKLFlux2.from_single_file(config.path) - return vae -``` - -### 3.3 Text Encoder Loader (if custom encoder) - -**File:** `invokeai/backend/model_manager/load/model_loaders/[newmodel].py` - -```python -@ModelLoaderRegistry.register( - base=BaseModelType.Any, - type=ModelType.NewModelEncoder, - format=ModelFormat.Checkpoint -) -class NewModelEncoderLoader(ModelLoader): - """Load single-file NewModel Encoder models.""" - - def _load_model(self, config: AnyModelConfig, submodel_type: SubModelType | None) -> AnyModel: - match submodel_type: - case SubModelType.TextEncoder: - return self._load_text_encoder(config) - case SubModelType.Tokenizer: - # Load tokenizer from HuggingFace or local path - return AutoTokenizer.from_pretrained("org/newmodel-base") - - raise ValueError(f"Unsupported submodel: {submodel_type}") - - def _load_text_encoder(self, config: AnyModelConfig) -> AnyModel: - from safetensors.torch import load_file - from transformers import NewModelConfig, NewModelForCausalLM - - # Load state dict and determine model configuration - sd = load_file(config.path) - - # Detect model architecture from weights - layer_count = self._count_layers(sd) - hidden_size = sd["model.embed_tokens.weight"].shape[1] - - # Create model with detected configuration - model_config = NewModelConfig( - hidden_size=hidden_size, - num_hidden_layers=layer_count, - # ... other config parameters - ) - - with accelerate.init_empty_weights(): - model = NewModelForCausalLM(model_config) - - model.load_state_dict(sd, assign=True) - return model -``` - -### Backend Model Loader Checklist - -- [ ] Create and register main model loader -- [ ] Create VAE loader if custom VAE -- [ ] Create text encoder loader if custom encoder -- [ ] Implement state dict conversion if needed (different formats) -- [ ] Implement submodel loading (Diffusers format) - ---- - -## 4. Backend: Invocations - -### 4.1 Model Loader Invocation - -**File:** `invokeai/app/invocations/[newmodel]_model_loader.py` - -```python -@invocation("newmodel_model_loader", title="NewModel Loader", ...) -class NewModelModelLoaderInvocation(BaseInvocation): - model: ModelIdentifierField = InputField(description="Main model") - vae_model: ModelIdentifierField | None = InputField(default=None) - encoder_model: ModelIdentifierField | None = InputField(default=None) - - def invoke(self, context: InvocationContext) -> NewModelLoaderOutput: - # Load transformer - transformer = self.model.model_copy( - update={"submodel_type": SubModelType.Transformer} - ) - # Load VAE (from main model or separately) - if self.vae_model: - vae = self.vae_model.model_copy(...) - else: - vae = self.model.model_copy( - update={"submodel_type": SubModelType.VAE} - ) - return NewModelLoaderOutput(transformer=transformer, vae=vae, ...) -``` - -### 4.2 Text Encoder Invocation - -**File:** `invokeai/app/invocations/[newmodel]_text_encoder.py` - -```python -@invocation("newmodel_text_encode", title="NewModel Text Encoder", ...) -class NewModelTextEncoderInvocation(BaseInvocation): - prompt: str = InputField() - encoder: EncoderField = InputField() - - def invoke(self, context: InvocationContext) -> ConditioningOutput: - # 1. Tokenize the prompt - with context.models.load(self.encoder.tokenizer) as tokenizer: - input_ids = tokenizer( - self.prompt, - return_tensors="pt", - padding="max_length", - max_length=256, - truncation=True - ).input_ids - - # 2. Run encoder and extract hidden states - # Example FLUX.2 Klein/Z-Image: Extract specific layers and stack them - # Different models use different layer extraction strategies: - # - Some use the final hidden state only - # - Others stack multiple intermediate layers for richer representations - with context.models.load(self.encoder.text_encoder) as encoder: - outputs = encoder(input_ids, output_hidden_states=True) - hidden_states = outputs.hidden_states - - # Stack layers 9, 18, 27 to create combined text embedding - # This captures features at different abstraction levels - # Shape: (batch, seq_len, hidden_size) -> (batch, seq_len, hidden_size * 3) - stacked_embeddings = torch.cat([ - hidden_states[9], - hidden_states[18], - hidden_states[27] - ], dim=-1) - - # 3. Create conditioning data structure - # The stacked embeddings become the text conditioning that guides denoising - conditioning_data = ConditioningFieldData( - conditionings=[ - BasicConditioningInfo(embeds=stacked_embeddings) - ] - ) - - # 4. Save conditioning to context and return reference - conditioning_name = context.conditioning.save(conditioning_data) - return ConditioningOutput( - conditioning=ConditioningField(conditioning_name=conditioning_name) - ) -``` - -### 4.3 Denoise Invocation - -**File:** `invokeai/app/invocations/[newmodel]_denoise.py` - -```python -@invocation("newmodel_denoise", title="NewModel Denoise", ...) -class NewModelDenoiseInvocation(BaseInvocation): - # Standard Fields - latents: LatentsField | None = InputField(default=None) - positive_conditioning: ConditioningField = InputField() - negative_conditioning: ConditioningField | None = InputField(default=None) - - # Model Fields - transformer: TransformerField = InputField() - - # Denoise Parameters - denoising_start: float = InputField(default=0.0, ge=0, le=1) - denoising_end: float = InputField(default=1.0, ge=0, le=1) - steps: int = InputField(default=20, ge=1) - cfg_scale: float = InputField(default=7.0) - - # Image-to-Image / Inpainting - denoise_mask: DenoiseMaskField | None = InputField(default=None) - - # Scheduler (if model-specific) - scheduler: Literal["euler", "heun", "lcm"] = InputField(default="euler") - - def invoke(self, context: InvocationContext) -> LatentsOutput: - # 1. Generate noise - noise = get_noise_newmodel(seed, height, width, ...) - - # 2. Pack latents (if needed) - x = pack_newmodel(latents) - - # 3. Compute schedule - timesteps = get_schedule_newmodel(num_steps, denoising_start, denoising_end) - - # 4. Denoising loop - x = denoise( - model=transformer, - x=x, - timesteps=timesteps, - conditioning=conditioning, - cfg_scale=self.cfg_scale, - inpaint_extension=inpaint_extension, # For inpainting - ) - - # 5. Unpack latents - latents = unpack_newmodel(x) - - return LatentsOutput(latents=latents) -``` - -### 4.4 VAE Encode Invocation - -**File:** `invokeai/app/invocations/[newmodel]_vae_encode.py` - -```python -@invocation("newmodel_vae_encode", title="Image to Latents - NewModel", ...) -class NewModelVaeEncodeInvocation(BaseInvocation): - image: ImageField = InputField() - vae: VAEField = InputField() - - def invoke(self, context: InvocationContext) -> LatentsOutput: - image = context.images.get_pil(self.image.image_name) - image_tensor = image_resized_to_grid_as_tensor(image.convert("RGB")) - - with context.models.load(self.vae.vae) as vae: - latent_dist = vae.encode(image_tensor) - latents = latent_dist.mode() # Deterministic - - return LatentsOutput(latents=latents) -``` - -### 4.5 VAE Decode Invocation - -**File:** `invokeai/app/invocations/[newmodel]_vae_decode.py` - -```python -@invocation("newmodel_vae_decode", title="Latents to Image - NewModel", ...) -class NewModelVaeDecodeInvocation(BaseInvocation): - latents: LatentsField = InputField() - vae: VAEField = InputField() - - def invoke(self, context: InvocationContext) -> ImageOutput: - latents = context.tensors.load(self.latents.latents_name) - - with context.models.load(self.vae.vae) as vae: - # Example FLUX.2: BN denormalization before decode - if hasattr(vae, "bn"): - latents = self._bn_denormalize(latents, vae) - - image = vae.decode(latents).sample - image = (image / 2 + 0.5).clamp(0, 1) - - return ImageOutput(image=image) -``` - -### Backend Invocations Checklist - -- [ ] Model loader invocation (`[newmodel]_model_loader.py`) -- [ ] Text encoder invocation (`[newmodel]_text_encoder.py`) -- [ ] Denoise invocation (`[newmodel]_denoise.py`) -- [ ] VAE encode invocation (`[newmodel]_vae_encode.py`) -- [ ] VAE decode invocation (`[newmodel]_vae_decode.py`) -- [ ] Define output classes (e.g., `NewModelLoaderOutput`) -- [ ] Define field classes if needed (e.g., `NewModelEncoderField`) - ---- - -## 5. Backend: Sampling and Denoise - -### 5.1 Sampling Utilities - -**File:** `invokeai/backend/[newmodel]/sampling_utils.py` - -```python -def get_noise_newmodel( - num_samples: int, - height: int, - width: int, - seed: int, - device: torch.device, - dtype: torch.dtype, -) -> torch.Tensor: - """Generate noise for NewModel. - - Example FLUX.2: 32 latent channels (vs 16 for FLUX.1) - """ - latent_channels = 32 # Model-specific - latent_h = height // 8 - latent_w = width // 8 - - generator = torch.Generator(device=device).manual_seed(seed) - return torch.randn( - (num_samples, latent_channels, latent_h, latent_w), - generator=generator, - device=device, - dtype=dtype, - ) - -def pack_newmodel(x: torch.Tensor) -> torch.Tensor: - """Pack latents for transformer input. - - Example FLUX: 2×2 patches → (B, H/2*W/2, C*4) - """ - return rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2) - -def unpack_newmodel(x: torch.Tensor, height: int, width: int) -> torch.Tensor: - """Unpack transformer output to latents.""" - return rearrange( - x, "b (h w) (c ph pw) -> b c (h ph) (w pw)", - h=height // 16, w=width // 16, ph=2, pw=2 - ) - -def get_schedule_newmodel( - num_steps: int, - denoising_start: float = 0.0, - denoising_end: float = 1.0, -) -> list[float]: - """Create timestep schedule. - - Example FLUX.2 Klein: Linear schedule from 1.0 → 0.0 - """ - start_step = int(num_steps * denoising_start) - end_step = int(num_steps * denoising_end) - - sigmas = torch.linspace(1.0, 0.0, num_steps + 1) - return sigmas[start_step:end_step + 1].tolist() - -def generate_img_ids_newmodel(batch_size: int, height: int, width: int) -> torch.Tensor: - """Generate position IDs for transformer. - - Example FLUX.2: 4D position IDs (T, H, W, L) - """ - # Model-specific position encoding - pass -``` - -### 5.2 Denoise Function - -**File:** `invokeai/backend/[newmodel]/denoise.py` - -```python -def denoise( - model: nn.Module, - img: torch.Tensor, - img_ids: torch.Tensor, - txt: torch.Tensor, - txt_ids: torch.Tensor, - timesteps: list[float], - cfg_scale: list[float], - neg_txt: torch.Tensor | None = None, - neg_txt_ids: torch.Tensor | None = None, - scheduler: Any = None, - inpaint_extension: RectifiedFlowInpaintExtension | None = None, - step_callback: Callable | None = None, -) -> torch.Tensor: - """Main denoising loop. - - Example FLUX.2 Klein: - - No guidance_embeds (unlike FLUX.1 Dev) - - Supports Euler, Heun, LCM schedulers - - Integration with RectifiedFlowInpaintExtension - """ - total_steps = len(timesteps) - 1 - - for step_index in range(total_steps): - t_curr = timesteps[step_index] - t_prev = timesteps[step_index + 1] - - # CFG - if cfg_scale[step_index] > 1.0 and neg_txt is not None: - pred_pos = model(img, t_curr, txt, txt_ids, img_ids) - pred_neg = model(img, t_curr, neg_txt, neg_txt_ids, img_ids) - pred = pred_neg + cfg_scale[step_index] * (pred_pos - pred_neg) - else: - pred = model(img, t_curr, txt, txt_ids, img_ids) - - # Scheduler step or manual Euler - if scheduler is not None: - img = scheduler.step(pred, t_curr, img).prev_sample - else: - # Manual Euler: x = x + (t_prev - t_curr) * pred - img = img + (t_prev - t_curr) * pred - - # Inpainting merge - if inpaint_extension is not None: - img = inpaint_extension.merge_intermediate_latents_with_init_latents(img, t_prev) - - # Progress callback - if step_callback: - step_callback(PipelineIntermediateState(step=step_index + 1, ...)) - - return img -``` - -### 5.3 Scheduler (if model-specific) - -**File:** `invokeai/backend/[newmodel]/schedulers.py` or use existing - -```python -# Existing schedulers in invokeai/backend/flux/schedulers.py: -# - FlowMatchEulerDiscreteScheduler -# - FlowMatchHeunDiscreteScheduler -# - FlowMatchLCMScheduler - -NEWMODEL_SCHEDULER_MAP = { - "euler": FlowMatchEulerDiscreteScheduler, - "heun": FlowMatchHeunDiscreteScheduler, - "lcm": FlowMatchLCMScheduler, -} -``` - -### Backend Sampling and Denoise Checklist - -- [ ] Noise generation (`get_noise_newmodel()`) -- [ ] Pack/unpack functions (if transformer-based) -- [ ] Schedule generation (`get_schedule_newmodel()`) -- [ ] Position ID generation (if needed) -- [ ] Implement denoise loop -- [ ] Scheduler integration -- [ ] Inpaint extension integration -- [ ] Progress callbacks - ---- - -## 6. Frontend: Graph Building - -### 6.1 Create Graph Builder - -**File:** `invokeai/frontend/web/src/features/nodes/util/graph/generation/buildNewModelGraph.ts` - -```typescript -export const buildNewModelGraph = async (arg: GraphBuilderArg): Promise => { - const { state, manager } = arg; - const { model } = state.params; - - const g = new Graph(); - - // 1. Model Loader - const modelLoader = g.addNode({ - id: NEWMODEL_MODEL_LOADER, - type: 'newmodel_model_loader', - model: Graph.getModelMetadataField(model), - }); - - // 2. Text Encoder - const positivePrompt = g.addNode({ - id: POSITIVE_CONDITIONING, - type: 'newmodel_text_encode', - prompt: positivePromptText, - }); - g.addEdge(modelLoader, 'encoder', positivePrompt, 'encoder'); - - // 3. Denoise Node - const denoise = g.addNode({ - id: NEWMODEL_DENOISE, - type: 'newmodel_denoise', - steps, - cfg_scale: cfg, - scheduler: newmodelScheduler, - denoising_start: 0, - denoising_end: 1, - }); - g.addEdge(modelLoader, 'transformer', denoise, 'transformer'); - g.addEdge(positivePrompt, 'conditioning', denoise, 'positive_conditioning'); - - // 4. VAE Decode - const l2i = g.addNode({ - id: NEWMODEL_VAE_DECODE, - type: 'newmodel_vae_decode', - }); - g.addEdge(modelLoader, 'vae', l2i, 'vae'); - g.addEdge(denoise, 'latents', l2i, 'latents'); - - // 5. Generation Mode Handling - let canvasOutput: Invocation = l2i; - - switch (generationMode) { - case 'txt2img': - canvasOutput = addTextToImage({ g, state, denoise, l2i }); - g.upsertMetadata({ generation_mode: 'newmodel_txt2img' }); - break; - case 'img2img': - const i2l = g.addNode({ type: 'newmodel_vae_encode' }); - canvasOutput = await addImageToImage({ g, state, manager, denoise, l2i, i2l, ... }); - g.upsertMetadata({ generation_mode: 'newmodel_img2img' }); - break; - case 'inpaint': - canvasOutput = await addInpaint({ g, state, manager, denoise, l2i, i2l, ... }); - g.upsertMetadata({ generation_mode: 'newmodel_inpaint' }); - break; - case 'outpaint': - canvasOutput = await addOutpaint({ g, state, manager, denoise, l2i, i2l, ... }); - g.upsertMetadata({ generation_mode: 'newmodel_outpaint' }); - break; - } - - return { g, noise, denoise, posCond: positivePrompt, ... }; -}; -``` - -### 6.2 Register Graph Builder - -**File:** `invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts` - -```typescript -// In the buildGraph function (around line 47-64): -switch (base) { - case 'sd-1': - case 'sd-2': - case 'sdxl': - return buildSD1Graph(arg); - case 'flux': - return buildFLUXGraph(arg); - case 'flux2': - return buildFLUXGraph(arg); // FLUX.2 uses the same builder - case 'sd-3': - return buildSD3Graph(arg); - case 'z-image': - return buildZImageGraph(arg); - // NEW: - case 'newmodel': - return buildNewModelGraph(arg); -} -``` - -### 6.3 Update Type Definitions - -**File:** `invokeai/frontend/web/src/features/nodes/util/graph/types.ts` - -```typescript -// Add node types: -export type ImageOutputNodes = - | 'l2i' | 'flux_vae_decode' | 'flux2_vae_decode' - | 'sd3_l2i' | 'newmodel_vae_decode'; - -export type LatentToImageNodes = - | 'l2i' | 'flux_vae_decode' | 'flux2_vae_decode' - | 'sd3_l2i' | 'newmodel_vae_decode'; - -export type ImageToLatentsNodes = - | 'i2l' | 'flux_vae_encode' | 'flux2_vae_encode' - | 'sd3_i2l' | 'newmodel_vae_encode'; - -export type DenoiseLatentsNodes = - | 'denoise_latents' | 'flux_denoise' | 'flux2_denoise' - | 'sd3_denoise' | 'newmodel_denoise'; - -export type MainModelLoaderNodes = - | 'main_model_loader' | 'flux_model_loader' | 'flux2_klein_model_loader' - | 'sd3_model_loader' | 'newmodel_model_loader'; -``` - -### 6.4 Update Generation Mode Utilities - -**Files:** -- `invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts` -- `invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts` -- `invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts` - -```typescript -// In addImageToImage.ts - extend type check: -if ( - denoise.type === 'cogview4_denoise' || - denoise.type === 'flux_denoise' || - denoise.type === 'flux2_denoise' || - denoise.type === 'newmodel_denoise' // NEW -) { - // Rectified flow models: denoising_start instead of noise -} -``` - -### Frontend Graph Building Checklist - -- [ ] Create graph builder (`buildNewModelGraph.ts`) -- [ ] Register graph builder in useEnqueueCanvas -- [ ] Update type definitions (`types.ts`) -- [ ] Extend node type unions (ImageOutputNodes, etc.) -- [ ] Update `addImageToImage.ts` -- [ ] Update `addInpaint.ts` -- [ ] Update `addOutpaint.ts` - ---- - -## 7. Frontend: State Management - -### 7.1 Add Parameter State - -**File:** `invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts` - -```typescript -// Extend state interface: -interface ParamsState { - // Existing fields - fluxScheduler: 'euler' | 'heun' | 'lcm'; - zImageScheduler: 'euler' | 'heun' | 'lcm'; - - // NEW: NewModel specific parameters - newmodelScheduler: 'euler' | 'heun' | 'lcm'; - newmodelVaeModel: ParameterVAEModel | null; - newmodelEncoderModel: ParameterModel | null; -} - -// Initial state: -const initialState: ParamsState = { - newmodelScheduler: 'euler', - newmodelVaeModel: null, - newmodelEncoderModel: null, -}; - -// Reducers: -reducers: { - setNewmodelScheduler: (state, action: PayloadAction<'euler' | 'heun' | 'lcm'>) => { - state.newmodelScheduler = action.payload; - }, - newmodelVaeModelSelected: (state, action: PayloadAction) => { - state.newmodelVaeModel = action.payload; - }, - newmodelEncoderModelSelected: (state, action: PayloadAction) => { - state.newmodelEncoderModel = action.payload; - }, -} -``` - -### 7.2 Create Selectors - -**File:** `invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts` - -```typescript -// Selectors: -export const selectNewmodelScheduler = createSelector( - selectParamsSlice, - (params) => params.newmodelScheduler -); - -export const selectNewmodelVaeModel = createSelector( - selectParamsSlice, - (params) => params.newmodelVaeModel -); - -export const selectNewmodelEncoderModel = createSelector( - selectParamsSlice, - (params) => params.newmodelEncoderModel -); -``` - -### Frontend State Management Checklist - -- [ ] Extend state interface for model-specific parameters -- [ ] Define initial state -- [ ] Create reducer actions -- [ ] Create selectors -- [ ] Export actions - ---- - -## 8. Frontend: Parameter Recall - -### 8.1 Metadata Parsing - -**File:** `invokeai/frontend/web/src/features/metadata/parsing.tsx` - -```typescript -// Add parameter recall handlers: -const recallNewmodelScheduler = (metadata: CoreMetadata) => { - if (metadata.scheduler) { - dispatch(setNewmodelScheduler(metadata.scheduler)); - } -}; - -const recallNewmodelVaeModel = async (metadata: CoreMetadata) => { - if (metadata.vae) { - const vaeModel = await fetchModelConfig(metadata.vae); - dispatch(newmodelVaeModelSelected(vaeModel)); - } -}; - -const recallNewmodelEncoderModel = async (metadata: CoreMetadata) => { - if (metadata.encoder_model) { - const encoderModel = await fetchModelConfig(metadata.encoder_model); - dispatch(newmodelEncoderModelSelected(encoderModel)); - } -}; -``` - -### Frontend Parameter Recall Checklist - -- [ ] Recall handlers for each model-specific parameter -- [ ] Model config fetching for submodels -- [ ] Dispatch actions for state updates - ---- - -## 9. Metadata and Generation Modes - -### 9.1 Add Generation Modes - -**File:** `invokeai/app/invocations/metadata.py` - -```python -GENERATION_MODES = Literal[ - # Existing modes - "txt2img", "img2img", "inpaint", "outpaint", - "sdxl_txt2img", "sdxl_img2img", "sdxl_inpaint", "sdxl_outpaint", - "flux_txt2img", "flux_img2img", "flux_inpaint", "flux_outpaint", - "flux2_txt2img", "flux2_img2img", "flux2_inpaint", "flux2_outpaint", - "sd3_txt2img", "sd3_img2img", "sd3_inpaint", "sd3_outpaint", - # NEW: - "newmodel_txt2img", - "newmodel_img2img", - "newmodel_inpaint", - "newmodel_outpaint", -] -``` - -### 9.2 Extend CoreMetadata (if needed) - -**File:** `invokeai/app/invocations/metadata.py` - -```python -@invocation_output("core_metadata_output") -class CoreMetadataOutput(BaseInvocationOutput): - # Existing fields - model: ModelIdentifierField | None = None - steps: int | None = None - cfg_scale: float | None = None - - # NEW: Model-specific metadata fields - newmodel_encoder: ModelIdentifierField | None = None - newmodel_custom_param: float | None = None -``` - -### Metadata and Generation Modes Checklist - -- [ ] Add generation modes to `GENERATION_MODES` -- [ ] Extend CoreMetadata if model-specific fields needed -- [ ] Set metadata in graph builder (`g.upsertMetadata({...})`) - ---- - -## 10. Starter Models - -### 10.1 Define Starter Models - -**File:** `invokeai/backend/model_manager/starter_models.py` - -```python -# Main Model -newmodel_main = StarterModel( - name="NewModel Main", - base=BaseModelType.NewModel, - source="organization/newmodel-main", # HuggingFace repo - description="NewModel main transformer. ~10GB", - type=ModelType.Main, -) - -# VAE (if separate) -newmodel_vae = StarterModel( - name="NewModel VAE", - base=BaseModelType.NewModel, - source="organization/newmodel::vae", # Submodel syntax - description="NewModel VAE. ~500MB", - type=ModelType.VAE, -) - -# Text Encoder (if separate) -newmodel_encoder = StarterModel( - name="NewModel Encoder", - base=BaseModelType.Any, - source="organization/newmodel::text_encoder+tokenizer", - description="NewModel text encoder. ~5GB", - type=ModelType.TextEncoder, -) - -# Quantized variants -newmodel_fp8 = StarterModel( - name="NewModel (FP8)", - base=BaseModelType.NewModel, - source="https://huggingface.co/org/newmodel-fp8/resolve/main/model.safetensors", - description="FP8 quantized version. ~5GB", - type=ModelType.Main, - dependencies=[newmodel_vae, newmodel_encoder], # Dependencies! -) - -# Add to STARTER_MODELS list: -STARTER_MODELS: list[StarterModel] = [ - # ... existing models - newmodel_main, - newmodel_vae, - newmodel_encoder, - newmodel_fp8, -] -``` - -### Starter Models Checklist - -- [ ] Define main model StarterModel -- [ ] Define VAE StarterModel if separate -- [ ] Define text encoder StarterModel if separate -- [ ] Define quantized variants (FP8, GGUF, etc.) -- [ ] Set dependencies correctly -- [ ] Add to `STARTER_MODELS` list - ---- - -## 11. Optional Features - -### 11.1 ControlNet Support - -**Backend Config:** -**File:** `invokeai/backend/model_manager/configs/controlnet.py` - -```python -@ModelConfigFactory.register -class ControlNet_Checkpoint_NewModel_Config(ControlNet_Checkpoint_Base): - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel - - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, ...) -> Self: - if not _is_newmodel_controlnet(mod.state_dict): - raise NotAMatchError() - return cls(...) -``` - -**Backend Invocation:** -**File:** `invokeai/app/invocations/[newmodel]_controlnet.py` - -```python -@invocation("newmodel_controlnet", ...) -class NewModelControlNetInvocation(BaseInvocation): - image: ImageField = InputField() - controlnet_model: ControlNetField = InputField() - control_weight: float = InputField(default=1.0) - - def invoke(self, context) -> ControlNetOutput: - # Compute ControlNet conditioning - pass -``` - -**Frontend Graph:** -```typescript -// In buildNewModelGraph.ts: -const { controlNets } = await addControlNets({ g, manager, denoise }); -``` - -### 11.2 IP-Adapter / Reference Images - -**Backend Invocation:** -**File:** `invokeai/app/invocations/[newmodel]_ip_adapter.py` - -```python -@invocation("newmodel_ip_adapter", ...) -class NewModelIPAdapterInvocation(BaseInvocation): - image: ImageField = InputField() - ip_adapter_model: IPAdapterField = InputField() - weight: float = InputField(default=1.0) -``` - -**Frontend Graph:** -```typescript -// In buildNewModelGraph.ts: -const { ipAdapters } = await addIPAdapters({ g, manager, denoise }); -``` - -### 11.3 LoRA Support - -**Backend Config:** -**File:** `invokeai/backend/model_manager/configs/lora.py` - -```python -@ModelConfigFactory.register -class LoRA_LyCORIS_NewModel_Config(LoRA_LyCORIS_Base): - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel -``` - -**Backend Model Loader Integration:** -```python -# In newmodel_model_loader.py: -class NewModelModelLoaderOutput(BaseInvocationOutput): - transformer: TransformerField # TransformerField already contains loras: list[LoRAField] -``` - -**Frontend Graph:** -```typescript -// In buildNewModelGraph.ts: -const { loras } = await addLoRAs({ g, manager, denoise, modelLoader }); -``` - -### 11.4 Scheduler UI - -**Frontend Component:** -**File:** `invokeai/frontend/web/src/features/parameters/components/NewModelScheduler.tsx` - -```typescript -export const NewModelSchedulerSelect = () => { - const dispatch = useAppDispatch(); - const scheduler = useAppSelector(selectNewmodelScheduler); - - return ( -