mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-21 07:28:06 -05:00
Compare commits
21 Commits
maryhipp/e
...
v5.10.0dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d06cc71cd9 | ||
|
|
ecbc1cf85d | ||
|
|
dcad306aee | ||
|
|
bea9d037bc | ||
|
|
eed4260975 | ||
|
|
6a79b1c64c | ||
|
|
4cdfe3e30d | ||
|
|
df294db236 | ||
|
|
52e247cfe0 | ||
|
|
0a13640bf3 | ||
|
|
643b71f56c | ||
|
|
b745411866 | ||
|
|
c3ffb0feed | ||
|
|
f53ff5fa3c | ||
|
|
b2337b56bd | ||
|
|
fb777b4502 | ||
|
|
4c12f5a011 | ||
|
|
d4655ea21a | ||
|
|
752b62d0b5 | ||
|
|
9a0efb308d | ||
|
|
b4c276b50f |
@@ -1,9 +1,11 @@
|
|||||||
*
|
*
|
||||||
!invokeai
|
!invokeai
|
||||||
!pyproject.toml
|
!pyproject.toml
|
||||||
|
!uv.lock
|
||||||
!docker/docker-entrypoint.sh
|
!docker/docker-entrypoint.sh
|
||||||
!LICENSE
|
!LICENSE
|
||||||
|
|
||||||
|
**/dist
|
||||||
**/node_modules
|
**/node_modules
|
||||||
**/__pycache__
|
**/__pycache__
|
||||||
**/*.egg-info
|
**/*.egg-info
|
||||||
|
|||||||
2
.github/workflows/build-container.yml
vendored
2
.github/workflows/build-container.yml
vendored
@@ -97,6 +97,8 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
platforms: ${{ env.PLATFORMS }}
|
platforms: ${{ env.PLATFORMS }}
|
||||||
|
build-args: |
|
||||||
|
GPU_DRIVER=${{ matrix.gpu-driver }}
|
||||||
push: ${{ github.ref == 'refs/heads/main' || github.ref_type == 'tag' || github.event.inputs.push-to-registry }}
|
push: ${{ github.ref == 'refs/heads/main' || github.ref_type == 'tag' || github.event.inputs.push-to-registry }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
2
.github/workflows/build-installer.yml
vendored
2
.github/workflows/build-installer.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: setup python
|
- name: setup python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.12'
|
||||||
cache: pip
|
cache: pip
|
||||||
cache-dependency-path: pyproject.toml
|
cache-dependency-path: pyproject.toml
|
||||||
|
|
||||||
|
|||||||
@@ -1,77 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1.4
|
# syntax=docker/dockerfile:1.4
|
||||||
|
|
||||||
## Builder stage
|
#### Web UI ------------------------------------
|
||||||
|
|
||||||
FROM library/ubuntu:24.04 AS builder
|
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
|
||||||
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
|
||||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
|
||||||
apt update && apt-get install -y \
|
|
||||||
build-essential \
|
|
||||||
git
|
|
||||||
|
|
||||||
# Install `uv` for package management
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:0.6.0 /uv /uvx /bin/
|
|
||||||
|
|
||||||
ENV VIRTUAL_ENV=/opt/venv
|
|
||||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
|
||||||
ENV INVOKEAI_SRC=/opt/invokeai
|
|
||||||
ENV PYTHON_VERSION=3.11
|
|
||||||
ENV UV_PYTHON=3.11
|
|
||||||
ENV UV_COMPILE_BYTECODE=1
|
|
||||||
ENV UV_LINK_MODE=copy
|
|
||||||
ENV UV_PROJECT_ENVIRONMENT="$VIRTUAL_ENV"
|
|
||||||
ENV UV_INDEX="https://download.pytorch.org/whl/cu124"
|
|
||||||
|
|
||||||
ARG GPU_DRIVER=cuda
|
|
||||||
# unused but available
|
|
||||||
ARG BUILDPLATFORM
|
|
||||||
|
|
||||||
# Switch to the `ubuntu` user to work around dependency issues with uv-installed python
|
|
||||||
RUN mkdir -p ${VIRTUAL_ENV} && \
|
|
||||||
mkdir -p ${INVOKEAI_SRC} && \
|
|
||||||
chmod -R a+w /opt && \
|
|
||||||
mkdir ~ubuntu/.cache && chown ubuntu: ~ubuntu/.cache
|
|
||||||
USER ubuntu
|
|
||||||
|
|
||||||
# Install python
|
|
||||||
RUN --mount=type=cache,target=/home/ubuntu/.cache/uv,uid=1000,gid=1000 \
|
|
||||||
uv python install ${PYTHON_VERSION}
|
|
||||||
|
|
||||||
WORKDIR ${INVOKEAI_SRC}
|
|
||||||
|
|
||||||
# Install project's dependencies as a separate layer so they aren't rebuilt every commit.
|
|
||||||
# bind-mount instead of copy to defer adding sources to the image until next layer.
|
|
||||||
#
|
|
||||||
# NOTE: there are no pytorch builds for arm64 + cuda, only cpu
|
|
||||||
# x86_64/CUDA is the default
|
|
||||||
RUN --mount=type=cache,target=/home/ubuntu/.cache/uv,uid=1000,gid=1000 \
|
|
||||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
||||||
--mount=type=bind,source=invokeai/version,target=invokeai/version \
|
|
||||||
if [ "$TARGETPLATFORM" = "linux/arm64" ] || [ "$GPU_DRIVER" = "cpu" ]; then \
|
|
||||||
UV_INDEX="https://download.pytorch.org/whl/cpu"; \
|
|
||||||
elif [ "$GPU_DRIVER" = "rocm" ]; then \
|
|
||||||
UV_INDEX="https://download.pytorch.org/whl/rocm6.1"; \
|
|
||||||
fi && \
|
|
||||||
uv sync --no-install-project
|
|
||||||
|
|
||||||
# Now that the bulk of the dependencies have been installed, copy in the project files that change more frequently.
|
|
||||||
COPY invokeai invokeai
|
|
||||||
COPY pyproject.toml .
|
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/home/ubuntu/.cache/uv,uid=1000,gid=1000 \
|
|
||||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
||||||
if [ "$TARGETPLATFORM" = "linux/arm64" ] || [ "$GPU_DRIVER" = "cpu" ]; then \
|
|
||||||
UV_INDEX="https://download.pytorch.org/whl/cpu"; \
|
|
||||||
elif [ "$GPU_DRIVER" = "rocm" ]; then \
|
|
||||||
UV_INDEX="https://download.pytorch.org/whl/rocm6.1"; \
|
|
||||||
fi && \
|
|
||||||
uv sync
|
|
||||||
|
|
||||||
|
|
||||||
#### Build the Web UI ------------------------------------
|
|
||||||
|
|
||||||
FROM docker.io/node:22-slim AS web-builder
|
FROM docker.io/node:22-slim AS web-builder
|
||||||
ENV PNPM_HOME="/pnpm"
|
ENV PNPM_HOME="/pnpm"
|
||||||
@@ -85,69 +14,89 @@ RUN --mount=type=cache,target=/pnpm/store \
|
|||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
RUN npx vite build
|
RUN npx vite build
|
||||||
|
|
||||||
#### Runtime stage ---------------------------------------
|
## Backend ---------------------------------------
|
||||||
|
|
||||||
FROM library/ubuntu:24.04 AS runtime
|
FROM library/ubuntu:24.04
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ENV PYTHONUNBUFFERED=1
|
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1
|
RUN --mount=type=cache,target=/var/cache/apt \
|
||||||
|
--mount=type=cache,target=/var/lib/apt \
|
||||||
|
apt update && apt install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
git \
|
||||||
|
gosu \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libgl1 \
|
||||||
|
libglx-mesa0 \
|
||||||
|
build-essential \
|
||||||
|
libopencv-dev \
|
||||||
|
libstdc++-10-dev
|
||||||
|
|
||||||
RUN apt update && apt install -y --no-install-recommends \
|
ENV \
|
||||||
git \
|
PYTHONUNBUFFERED=1 \
|
||||||
curl \
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
vim \
|
VIRTUAL_ENV=/opt/venv \
|
||||||
tmux \
|
INVOKEAI_SRC=/opt/invokeai \
|
||||||
ncdu \
|
PYTHON_VERSION=3.12 \
|
||||||
iotop \
|
UV_PYTHON=3.12 \
|
||||||
bzip2 \
|
UV_COMPILE_BYTECODE=1 \
|
||||||
gosu \
|
UV_MANAGED_PYTHON=1 \
|
||||||
magic-wormhole \
|
UV_LINK_MODE=copy \
|
||||||
libglib2.0-0 \
|
UV_PROJECT_ENVIRONMENT=/opt/venv \
|
||||||
libgl1 \
|
UV_INDEX="https://download.pytorch.org/whl/cu124" \
|
||||||
libglx-mesa0 \
|
INVOKEAI_ROOT=/invokeai \
|
||||||
build-essential \
|
INVOKEAI_HOST=0.0.0.0 \
|
||||||
libopencv-dev \
|
INVOKEAI_PORT=9090 \
|
||||||
libstdc++-10-dev &&\
|
PATH="/opt/venv/bin:$PATH" \
|
||||||
apt-get clean && apt-get autoclean
|
CONTAINER_UID=${CONTAINER_UID:-1000} \
|
||||||
|
CONTAINER_GID=${CONTAINER_GID:-1000}
|
||||||
|
|
||||||
ENV INVOKEAI_SRC=/opt/invokeai
|
ARG GPU_DRIVER=cuda
|
||||||
ENV VIRTUAL_ENV=/opt/venv
|
|
||||||
ENV UV_PROJECT_ENVIRONMENT="$VIRTUAL_ENV"
|
|
||||||
ENV PYTHON_VERSION=3.11
|
|
||||||
ENV INVOKEAI_ROOT=/invokeai
|
|
||||||
ENV INVOKEAI_HOST=0.0.0.0
|
|
||||||
ENV INVOKEAI_PORT=9090
|
|
||||||
ENV PATH="$VIRTUAL_ENV/bin:$INVOKEAI_SRC:$PATH"
|
|
||||||
ENV CONTAINER_UID=${CONTAINER_UID:-1000}
|
|
||||||
ENV CONTAINER_GID=${CONTAINER_GID:-1000}
|
|
||||||
|
|
||||||
# Install `uv` for package management
|
# Install `uv` for package management
|
||||||
# and install python for the ubuntu user (expected to exist on ubuntu >=24.x)
|
COPY --from=ghcr.io/astral-sh/uv:0.6.9 /uv /uvx /bin/
|
||||||
# this is too tiny to optimize with multi-stage builds, but maybe we'll come back to it
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:0.6.0 /uv /uvx /bin/
|
|
||||||
USER ubuntu
|
|
||||||
RUN uv python install ${PYTHON_VERSION}
|
|
||||||
USER root
|
|
||||||
|
|
||||||
# --link requires buldkit w/ dockerfile syntax 1.4
|
# Install python & allow non-root user to use it by traversing the /root dir without read permissions
|
||||||
COPY --link --from=builder ${INVOKEAI_SRC} ${INVOKEAI_SRC}
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
COPY --link --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
uv python install ${PYTHON_VERSION} && \
|
||||||
COPY --link --from=web-builder /build/dist ${INVOKEAI_SRC}/invokeai/frontend/web/dist
|
# chmod --recursive a+rX /root/.local/share/uv/python
|
||||||
|
chmod 711 /root
|
||||||
# Link amdgpu.ids for ROCm builds
|
|
||||||
# contributed by https://github.com/Rubonnek
|
|
||||||
RUN mkdir -p "/opt/amdgpu/share/libdrm" &&\
|
|
||||||
ln -s "/usr/share/libdrm/amdgpu.ids" "/opt/amdgpu/share/libdrm/amdgpu.ids"
|
|
||||||
|
|
||||||
WORKDIR ${INVOKEAI_SRC}
|
WORKDIR ${INVOKEAI_SRC}
|
||||||
|
|
||||||
|
# Install project's dependencies as a separate layer so they aren't rebuilt every commit.
|
||||||
|
# bind-mount instead of copy to defer adding sources to the image until next layer.
|
||||||
|
#
|
||||||
|
# NOTE: there are no pytorch builds for arm64 + cuda, only cpu
|
||||||
|
# x86_64/CUDA is the default
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
|
# this is just to get the package manager to recognize that the project exists, without making changes to the docker layer
|
||||||
|
--mount=type=bind,source=invokeai/version,target=invokeai/version \
|
||||||
|
if [ "$TARGETPLATFORM" = "linux/arm64" ] || [ "$GPU_DRIVER" = "cpu" ]; then UV_INDEX="https://download.pytorch.org/whl/cpu"; \
|
||||||
|
elif [ "$GPU_DRIVER" = "rocm" ]; then UV_INDEX="https://download.pytorch.org/whl/rocm6.2"; \
|
||||||
|
fi && \
|
||||||
|
uv sync --frozen
|
||||||
|
|
||||||
# build patchmatch
|
# build patchmatch
|
||||||
RUN cd /usr/lib/$(uname -p)-linux-gnu/pkgconfig/ && ln -sf opencv4.pc opencv.pc
|
RUN cd /usr/lib/$(uname -p)-linux-gnu/pkgconfig/ && ln -sf opencv4.pc opencv.pc
|
||||||
RUN python -c "from patchmatch import patch_match"
|
RUN python -c "from patchmatch import patch_match"
|
||||||
|
|
||||||
|
# Link amdgpu.ids for ROCm builds
|
||||||
|
# contributed by https://github.com/Rubonnek
|
||||||
|
RUN mkdir -p "/opt/amdgpu/share/libdrm" &&\
|
||||||
|
ln -s "/usr/share/libdrm/amdgpu.ids" "/opt/amdgpu/share/libdrm/amdgpu.ids"
|
||||||
|
|
||||||
RUN mkdir -p ${INVOKEAI_ROOT} && chown -R ${CONTAINER_UID}:${CONTAINER_GID} ${INVOKEAI_ROOT}
|
RUN mkdir -p ${INVOKEAI_ROOT} && chown -R ${CONTAINER_UID}:${CONTAINER_GID} ${INVOKEAI_ROOT}
|
||||||
|
|
||||||
COPY docker/docker-entrypoint.sh ./
|
COPY docker/docker-entrypoint.sh ./
|
||||||
ENTRYPOINT ["/opt/invokeai/docker-entrypoint.sh"]
|
ENTRYPOINT ["/opt/invokeai/docker-entrypoint.sh"]
|
||||||
CMD ["invokeai-web"]
|
CMD ["invokeai-web"]
|
||||||
|
|
||||||
|
# --link requires buldkit w/ dockerfile syntax 1.4, does not work with podman
|
||||||
|
COPY --link --from=web-builder /build/dist ${INVOKEAI_SRC}/invokeai/frontend/web/dist
|
||||||
|
|
||||||
|
# add sources last to minimize image changes on code changes
|
||||||
|
COPY invokeai ${INVOKEAI_SRC}/invokeai
|
||||||
@@ -41,7 +41,7 @@ If you just want to use Invoke, you should use the [launcher][launcher link].
|
|||||||
With the modifications made, the install command should look something like this:
|
With the modifications made, the install command should look something like this:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
uv pip install -e ".[dev,test,docs,xformers]" --python 3.11 --python-preference only-managed --index=https://download.pytorch.org/whl/cu124 --reinstall
|
uv pip install -e ".[dev,test,docs,xformers]" --python 3.12 --python-preference only-managed --index=https://download.pytorch.org/whl/cu124 --reinstall
|
||||||
```
|
```
|
||||||
|
|
||||||
6. At this point, you should have Invoke installed, a venv set up and activated, and the server running. But you will see a warning in the terminal that no UI was found. If you go to the URL for the server, you won't get a UI.
|
6. At this point, you should have Invoke installed, a venv set up and activated, and the server running. But you will see a warning in the terminal that no UI was found. If you go to the URL for the server, you won't get a UI.
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ The following commands vary depending on the version of Invoke being installed a
|
|||||||
3. Create a virtual environment in that directory:
|
3. Create a virtual environment in that directory:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
uv venv --relocatable --prompt invoke --python 3.11 --python-preference only-managed .venv
|
uv venv --relocatable --prompt invoke --python 3.12 --python-preference only-managed .venv
|
||||||
```
|
```
|
||||||
|
|
||||||
This command creates a portable virtual environment at `.venv` complete with a portable python 3.11. It doesn't matter if your system has no python installed, or has a different version - `uv` will handle everything.
|
This command creates a portable virtual environment at `.venv` complete with a portable python 3.12. It doesn't matter if your system has no python installed, or has a different version - `uv` will handle everything.
|
||||||
|
|
||||||
4. Activate the virtual environment:
|
4. Activate the virtual environment:
|
||||||
|
|
||||||
@@ -88,13 +88,13 @@ The following commands vary depending on the version of Invoke being installed a
|
|||||||
8. Install the `invokeai` package. Substitute the package specifier and version.
|
8. Install the `invokeai` package. Substitute the package specifier and version.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
uv pip install <PACKAGE_SPECIFIER>==<VERSION> --python 3.11 --python-preference only-managed --force-reinstall
|
uv pip install <PACKAGE_SPECIFIER>==<VERSION> --python 3.12 --python-preference only-managed --force-reinstall
|
||||||
```
|
```
|
||||||
|
|
||||||
If you determined you needed to use a `PyPI` index URL in the previous step, you'll need to add `--index=<INDEX_URL>` like this:
|
If you determined you needed to use a `PyPI` index URL in the previous step, you'll need to add `--index=<INDEX_URL>` like this:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
uv pip install <PACKAGE_SPECIFIER>==<VERSION> --python 3.11 --python-preference only-managed --index=<INDEX_URL> --force-reinstall
|
uv pip install <PACKAGE_SPECIFIER>==<VERSION> --python 3.12 --python-preference only-managed --index=<INDEX_URL> --force-reinstall
|
||||||
```
|
```
|
||||||
|
|
||||||
9. Deactivate and reactivate your venv so that the invokeai-specific commands become available in the environment:
|
9. Deactivate and reactivate your venv so that the invokeai-specific commands become available in the environment:
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ The requirements below are rough guidelines for best performance. GPUs with less
|
|||||||
|
|
||||||
You don't need to do this if you are installing with the [Invoke Launcher](./quick_start.md).
|
You don't need to do this if you are installing with the [Invoke Launcher](./quick_start.md).
|
||||||
|
|
||||||
Invoke requires python 3.10 or 3.11. If you don't already have one of these versions installed, we suggest installing 3.11, as it will be supported for longer.
|
Invoke requires python 3.10 through 3.12. If you don't already have one of these versions installed, we suggest installing 3.12, as it will be supported for longer.
|
||||||
|
|
||||||
Check that your system has an up-to-date Python installed by running `python3 --version` in the terminal (Linux, macOS) or cmd/powershell (Windows).
|
Check that your system has an up-to-date Python installed by running `python3 --version` in the terminal (Linux, macOS) or cmd/powershell (Windows).
|
||||||
|
|
||||||
@@ -49,19 +49,19 @@ Check that your system has an up-to-date Python installed by running `python3 --
|
|||||||
|
|
||||||
=== "Windows"
|
=== "Windows"
|
||||||
|
|
||||||
- Install python 3.11 with [an official installer].
|
- Install python with [an official installer].
|
||||||
- The installer includes an option to add python to your PATH. Be sure to enable this. If you missed it, re-run the installer, choose to modify an existing installation, and tick that checkbox.
|
- The installer includes an option to add python to your PATH. Be sure to enable this. If you missed it, re-run the installer, choose to modify an existing installation, and tick that checkbox.
|
||||||
- You may need to install [Microsoft Visual C++ Redistributable].
|
- You may need to install [Microsoft Visual C++ Redistributable].
|
||||||
|
|
||||||
=== "macOS"
|
=== "macOS"
|
||||||
|
|
||||||
- Install python 3.11 with [an official installer].
|
- Install python with [an official installer].
|
||||||
- If model installs fail with a certificate error, you may need to run this command (changing the python version to match what you have installed): `/Applications/Python\ 3.10/Install\ Certificates.command`
|
- If model installs fail with a certificate error, you may need to run this command (changing the python version to match what you have installed): `/Applications/Python\ 3.10/Install\ Certificates.command`
|
||||||
- If you haven't already, you will need to install the XCode CLI Tools by running `xcode-select --install` in a terminal.
|
- If you haven't already, you will need to install the XCode CLI Tools by running `xcode-select --install` in a terminal.
|
||||||
|
|
||||||
=== "Linux"
|
=== "Linux"
|
||||||
|
|
||||||
- Installing python varies depending on your system. On Ubuntu, you can use the [deadsnakes PPA](https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa).
|
- Installing python varies depending on your system. We recommend [using `uv` to manage your python installation](https://docs.astral.sh/uv/concepts/python-versions/#installing-a-python-version).
|
||||||
- You'll need to install `libglib2.0-0` and `libgl1-mesa-glx` for OpenCV to work. For example, on a Debian system: `sudo apt update && sudo apt install -y libglib2.0-0 libgl1-mesa-glx`
|
- You'll need to install `libglib2.0-0` and `libgl1-mesa-glx` for OpenCV to work. For example, on a Debian system: `sudo apt update && sudo apt install -y libglib2.0-0 libgl1-mesa-glx`
|
||||||
|
|
||||||
## Drivers
|
## Drivers
|
||||||
|
|||||||
@@ -37,7 +37,13 @@ from invokeai.app.services.style_preset_records.style_preset_records_sqlite impo
|
|||||||
from invokeai.app.services.urls.urls_default import LocalUrlService
|
from invokeai.app.services.urls.urls_default import LocalUrlService
|
||||||
from invokeai.app.services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
|
from invokeai.app.services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
|
||||||
from invokeai.app.services.workflow_thumbnails.workflow_thumbnails_disk import WorkflowThumbnailFileStorageDisk
|
from invokeai.app.services.workflow_thumbnails.workflow_thumbnails_disk import WorkflowThumbnailFileStorageDisk
|
||||||
from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ConditioningFieldData
|
from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
|
||||||
|
BasicConditioningInfo,
|
||||||
|
ConditioningFieldData,
|
||||||
|
FLUXConditioningInfo,
|
||||||
|
SD3ConditioningInfo,
|
||||||
|
SDXLConditioningInfo,
|
||||||
|
)
|
||||||
from invokeai.backend.util.logging import InvokeAILogger
|
from invokeai.backend.util.logging import InvokeAILogger
|
||||||
from invokeai.version.invokeai_version import __version__
|
from invokeai.version.invokeai_version import __version__
|
||||||
|
|
||||||
@@ -101,10 +107,25 @@ class ApiDependencies:
|
|||||||
images = ImageService()
|
images = ImageService()
|
||||||
invocation_cache = MemoryInvocationCache(max_cache_size=config.node_cache_size)
|
invocation_cache = MemoryInvocationCache(max_cache_size=config.node_cache_size)
|
||||||
tensors = ObjectSerializerForwardCache(
|
tensors = ObjectSerializerForwardCache(
|
||||||
ObjectSerializerDisk[torch.Tensor](output_folder / "tensors", ephemeral=True)
|
ObjectSerializerDisk[torch.Tensor](
|
||||||
|
output_folder / "tensors",
|
||||||
|
safe_globals=[torch.Tensor],
|
||||||
|
ephemeral=True,
|
||||||
|
),
|
||||||
|
max_cache_size=0,
|
||||||
)
|
)
|
||||||
conditioning = ObjectSerializerForwardCache(
|
conditioning = ObjectSerializerForwardCache(
|
||||||
ObjectSerializerDisk[ConditioningFieldData](output_folder / "conditioning", ephemeral=True)
|
ObjectSerializerDisk[ConditioningFieldData](
|
||||||
|
output_folder / "conditioning",
|
||||||
|
safe_globals=[
|
||||||
|
ConditioningFieldData,
|
||||||
|
BasicConditioningInfo,
|
||||||
|
SDXLConditioningInfo,
|
||||||
|
FLUXConditioningInfo,
|
||||||
|
SD3ConditioningInfo,
|
||||||
|
],
|
||||||
|
ephemeral=True,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
download_queue_service = DownloadQueueService(app_config=configuration, event_bus=events)
|
download_queue_service = DownloadQueueService(app_config=configuration, event_bus=events)
|
||||||
model_images_service = ModelImageFileStorageDisk(model_images_folder / "model_images")
|
model_images_service = ModelImageFileStorageDisk(model_images_folder / "model_images")
|
||||||
|
|||||||
@@ -21,10 +21,16 @@ class ObjectSerializerDisk(ObjectSerializerBase[T]):
|
|||||||
"""Disk-backed storage for arbitrary python objects. Serialization is handled by `torch.save` and `torch.load`.
|
"""Disk-backed storage for arbitrary python objects. Serialization is handled by `torch.save` and `torch.load`.
|
||||||
|
|
||||||
:param output_dir: The folder where the serialized objects will be stored
|
:param output_dir: The folder where the serialized objects will be stored
|
||||||
|
:param safe_globals: A list of types to be added to the safe globals for torch serialization
|
||||||
:param ephemeral: If True, objects will be stored in a temporary directory inside the given output_dir and cleaned up on exit
|
:param ephemeral: If True, objects will be stored in a temporary directory inside the given output_dir and cleaned up on exit
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, output_dir: Path, ephemeral: bool = False):
|
def __init__(
|
||||||
|
self,
|
||||||
|
output_dir: Path,
|
||||||
|
safe_globals: list[type],
|
||||||
|
ephemeral: bool = False,
|
||||||
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._ephemeral = ephemeral
|
self._ephemeral = ephemeral
|
||||||
self._base_output_dir = output_dir
|
self._base_output_dir = output_dir
|
||||||
@@ -42,6 +48,8 @@ class ObjectSerializerDisk(ObjectSerializerBase[T]):
|
|||||||
self._output_dir = Path(self._tempdir.name) if self._tempdir else self._base_output_dir
|
self._output_dir = Path(self._tempdir.name) if self._tempdir else self._base_output_dir
|
||||||
self.__obj_class_name: Optional[str] = None
|
self.__obj_class_name: Optional[str] = None
|
||||||
|
|
||||||
|
torch.serialization.add_safe_globals(safe_globals) if safe_globals else None
|
||||||
|
|
||||||
def load(self, name: str) -> T:
|
def load(self, name: str) -> T:
|
||||||
file_path = self._get_path(name)
|
file_path = self._get_path(name)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ class SD3ConditioningInfo:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ConditioningFieldData:
|
class ConditioningFieldData:
|
||||||
|
# If you change this class, adding more types, you _must_ update the instantiation of ObjectSerializerDisk in
|
||||||
|
# invokeai/app/api/dependencies.py, adding the types to the list of safe globals. If you do not, torch will be
|
||||||
|
# unable to deserialize the object and will raise an error.
|
||||||
conditionings: (
|
conditionings: (
|
||||||
List[BasicConditioningInfo]
|
List[BasicConditioningInfo]
|
||||||
| List[SDXLConditioningInfo]
|
| List[SDXLConditioningInfo]
|
||||||
|
|||||||
@@ -162,5 +162,6 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"pnpm": "8"
|
"pnpm": "8"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@8.15.9+sha512.499434c9d8fdd1a2794ebf4552b3b25c0a633abcee5bb15e7b5de90f32f47b513aca98cd5cfd001c31f0db454bc3804edccd578501e4ca293a6816166bbd9f81"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,4 +79,4 @@ export const isInvocationOutputSchemaObject = (
|
|||||||
|
|
||||||
export const isInvocationFieldSchema = (
|
export const isInvocationFieldSchema = (
|
||||||
obj: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject
|
obj: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject
|
||||||
): obj is InvocationFieldSchema => !('$ref' in obj);
|
): obj is InvocationFieldSchema => 'field_kind' in obj;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
|||||||
__version__ = "5.9.1"
|
__version__ = "5.10.0dev1"
|
||||||
|
|||||||
14
pins.json
Normal file
14
pins.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"python": "3.11",
|
||||||
|
"torchIndexUrl": {
|
||||||
|
"win32": {
|
||||||
|
"cuda": "https://download.pytorch.org/whl/cu126"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"cpu": "https://download.pytorch.org/whl/cpu",
|
||||||
|
"rocm": "https://download.pytorch.org/whl/rocm6.2.4",
|
||||||
|
"cuda": "https://download.pytorch.org/whl/cu126"
|
||||||
|
},
|
||||||
|
"darwin": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
[project]
|
[project]
|
||||||
name = "InvokeAI"
|
name = "InvokeAI"
|
||||||
description = "An implementation of Stable Diffusion which provides various new features and options to aid the image generation process"
|
description = "An implementation of Stable Diffusion which provides various new features and options to aid the image generation process"
|
||||||
requires-python = ">=3.10, <3.12"
|
requires-python = ">=3.10, <3.13"
|
||||||
readme = { content-type = "text/markdown", file = "README.md" }
|
readme = { content-type = "text/markdown", file = "README.md" }
|
||||||
keywords = ["stable-diffusion", "AI"]
|
keywords = ["stable-diffusion", "AI"]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
@@ -33,12 +33,12 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
# Core generation dependencies, pinned for reproducible builds.
|
# Core generation dependencies, pinned for reproducible builds.
|
||||||
"accelerate==1.0.1",
|
"accelerate",
|
||||||
"bitsandbytes==0.45.0; sys_platform!='darwin'",
|
"bitsandbytes; sys_platform!='darwin'",
|
||||||
"clip_anytorch==2.6.0", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip",
|
"clip_anytorch==2.6.0", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip",
|
||||||
"compel==2.0.2",
|
"compel==2.0.2",
|
||||||
"controlnet-aux",
|
"controlnet-aux==0.0.7",
|
||||||
"diffusers[torch]==0.31.0",
|
"diffusers[torch]",
|
||||||
"gguf==0.10.0",
|
"gguf==0.10.0",
|
||||||
"invisible-watermark==0.2.0", # needed to install SDXL base and refiner using their repo_ids
|
"invisible-watermark==0.2.0", # needed to install SDXL base and refiner using their repo_ids
|
||||||
"mediapipe==0.10.14", # needed for "mediapipeface" controlnet model
|
"mediapipe==0.10.14", # needed for "mediapipeface" controlnet model
|
||||||
@@ -46,26 +46,27 @@ dependencies = [
|
|||||||
"onnx==1.16.1",
|
"onnx==1.16.1",
|
||||||
"onnxruntime==1.19.2",
|
"onnxruntime==1.19.2",
|
||||||
"opencv-python==4.9.0.80",
|
"opencv-python==4.9.0.80",
|
||||||
"pytorch-lightning==2.1.3",
|
"pytorch-lightning",
|
||||||
"safetensors==0.4.3",
|
"safetensors",
|
||||||
# sentencepiece is required to load T5TokenizerFast (used by FLUX).
|
# sentencepiece is required to load T5TokenizerFast (used by FLUX).
|
||||||
"sentencepiece==0.2.0",
|
"sentencepiece==0.2.0",
|
||||||
"spandrel==0.3.4",
|
"spandrel==0.3.4",
|
||||||
"timm~=1.0.0",
|
"timm~=1.0.0",
|
||||||
"torch<2.5.0", # torch and related dependencies are loosely pinned, will respect requirement of `diffusers[torch]`
|
"torch~=2.6.0", # torch and related dependencies are loosely pinned, will respect requirement of `diffusers[torch]`
|
||||||
"torchmetrics",
|
"torchmetrics",
|
||||||
"torchsde",
|
"torchsde",
|
||||||
"torchvision",
|
"torchvision",
|
||||||
"transformers==4.46.3",
|
"timm~=1.0.0", # controlnet-aux depends on a version of timm that breaks LLaVA. explicitly pinning `timm` here, which results in a downgrade of `controlnet-aux`. https://github.com/huggingface/controlnet_aux/pull/101. If this poses a problem, we need to decide between newer `controlnet_aux` and LLaVA, OR we can fork `controlnet_aux` and update the pin.
|
||||||
|
"transformers",
|
||||||
|
|
||||||
# Core application dependencies, pinned for reproducible builds.
|
# Core application dependencies, pinned for reproducible builds.
|
||||||
"fastapi-events==0.11.1",
|
"fastapi-events",
|
||||||
"fastapi==0.111.0",
|
"fastapi",
|
||||||
"huggingface-hub==0.26.1",
|
"huggingface-hub",
|
||||||
"pydantic-settings==2.2.1",
|
"pydantic-settings",
|
||||||
"pydantic==2.7.2",
|
"pydantic",
|
||||||
"python-socketio==5.11.1",
|
"python-socketio",
|
||||||
"uvicorn[standard]==0.28.0",
|
"uvicorn[standard]",
|
||||||
|
|
||||||
# Auxiliary dependencies, pinned only if necessary.
|
# Auxiliary dependencies, pinned only if necessary.
|
||||||
"albumentations",
|
"albumentations",
|
||||||
@@ -90,11 +91,8 @@ dependencies = [
|
|||||||
"pyreadline3",
|
"pyreadline3",
|
||||||
"python-multipart",
|
"python-multipart",
|
||||||
"requests",
|
"requests",
|
||||||
"rich~=13.3",
|
|
||||||
"scikit-image",
|
"scikit-image",
|
||||||
"semver~=3.0.1",
|
"semver~=3.0.1",
|
||||||
"test-tube",
|
|
||||||
"windows-curses; sys_platform=='win32'",
|
|
||||||
"humanize==4.12.1",
|
"humanize==4.12.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -21,16 +21,18 @@ def count_files(path: Path):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def obj_serializer(tmp_path: Path):
|
def obj_serializer(tmp_path: Path):
|
||||||
return ObjectSerializerDisk[MockDataclass](tmp_path)
|
return ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def fwd_cache(tmp_path: Path):
|
def fwd_cache(tmp_path: Path):
|
||||||
return ObjectSerializerForwardCache(ObjectSerializerDisk[MockDataclass](tmp_path), max_cache_size=2)
|
return ObjectSerializerForwardCache(
|
||||||
|
ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass]), max_cache_size=2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_obj_serializer_disk_initializes(tmp_path: Path):
|
def test_obj_serializer_disk_initializes(tmp_path: Path):
|
||||||
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path)
|
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass])
|
||||||
assert obj_serializer._output_dir == tmp_path
|
assert obj_serializer._output_dir == tmp_path
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ def test_obj_serializer_disk_deletes(obj_serializer: ObjectSerializerDisk[MockDa
|
|||||||
|
|
||||||
|
|
||||||
def test_obj_serializer_ephemeral_creates_tempdir(tmp_path: Path):
|
def test_obj_serializer_ephemeral_creates_tempdir(tmp_path: Path):
|
||||||
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, ephemeral=True)
|
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass], ephemeral=True)
|
||||||
assert isinstance(obj_serializer._tempdir, tempfile.TemporaryDirectory)
|
assert isinstance(obj_serializer._tempdir, tempfile.TemporaryDirectory)
|
||||||
assert obj_serializer._base_output_dir == tmp_path
|
assert obj_serializer._base_output_dir == tmp_path
|
||||||
assert obj_serializer._output_dir != tmp_path
|
assert obj_serializer._output_dir != tmp_path
|
||||||
@@ -78,21 +80,21 @@ def test_obj_serializer_ephemeral_creates_tempdir(tmp_path: Path):
|
|||||||
|
|
||||||
|
|
||||||
def test_obj_serializer_ephemeral_deletes_tempdir(tmp_path: Path):
|
def test_obj_serializer_ephemeral_deletes_tempdir(tmp_path: Path):
|
||||||
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, ephemeral=True)
|
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass], ephemeral=True)
|
||||||
tempdir_path = obj_serializer._output_dir
|
tempdir_path = obj_serializer._output_dir
|
||||||
del obj_serializer
|
del obj_serializer
|
||||||
assert not tempdir_path.exists()
|
assert not tempdir_path.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_obj_serializer_ephemeral_deletes_tempdir_on_stop(tmp_path: Path):
|
def test_obj_serializer_ephemeral_deletes_tempdir_on_stop(tmp_path: Path):
|
||||||
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, ephemeral=True)
|
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass], ephemeral=True)
|
||||||
tempdir_path = obj_serializer._output_dir
|
tempdir_path = obj_serializer._output_dir
|
||||||
obj_serializer.stop(None) # pyright: ignore [reportArgumentType]
|
obj_serializer.stop(None) # pyright: ignore [reportArgumentType]
|
||||||
assert not tempdir_path.exists()
|
assert not tempdir_path.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_obj_serializer_ephemeral_writes_to_tempdir(tmp_path: Path):
|
def test_obj_serializer_ephemeral_writes_to_tempdir(tmp_path: Path):
|
||||||
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, ephemeral=True)
|
obj_serializer = ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass], ephemeral=True)
|
||||||
obj_1 = MockDataclass(foo="bar")
|
obj_1 = MockDataclass(foo="bar")
|
||||||
obj_1_name = obj_serializer.save(obj_1)
|
obj_1_name = obj_serializer.save(obj_1)
|
||||||
assert Path(obj_serializer._output_dir, obj_1_name).exists()
|
assert Path(obj_serializer._output_dir, obj_1_name).exists()
|
||||||
@@ -102,19 +104,19 @@ def test_obj_serializer_ephemeral_writes_to_tempdir(tmp_path: Path):
|
|||||||
def test_obj_serializer_ephemeral_deletes_dangling_tempdirs_on_init(tmp_path: Path):
|
def test_obj_serializer_ephemeral_deletes_dangling_tempdirs_on_init(tmp_path: Path):
|
||||||
tempdir = tmp_path / "tmpdir"
|
tempdir = tmp_path / "tmpdir"
|
||||||
tempdir.mkdir()
|
tempdir.mkdir()
|
||||||
ObjectSerializerDisk[MockDataclass](tmp_path, ephemeral=True)
|
ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass], ephemeral=True)
|
||||||
assert not tempdir.exists()
|
assert not tempdir.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_obj_serializer_does_not_delete_tempdirs_on_init(tmp_path: Path):
|
def test_obj_serializer_does_not_delete_tempdirs_on_init(tmp_path: Path):
|
||||||
tempdir = tmp_path / "tmpdir"
|
tempdir = tmp_path / "tmpdir"
|
||||||
tempdir.mkdir()
|
tempdir.mkdir()
|
||||||
ObjectSerializerDisk[MockDataclass](tmp_path, ephemeral=False)
|
ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass], ephemeral=False)
|
||||||
assert tempdir.exists()
|
assert tempdir.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_obj_serializer_disk_different_types(tmp_path: Path):
|
def test_obj_serializer_disk_different_types(tmp_path: Path):
|
||||||
obj_serializer_1 = ObjectSerializerDisk[MockDataclass](tmp_path)
|
obj_serializer_1 = ObjectSerializerDisk[MockDataclass](tmp_path, safe_globals=[MockDataclass])
|
||||||
obj_1 = MockDataclass(foo="bar")
|
obj_1 = MockDataclass(foo="bar")
|
||||||
obj_1_name = obj_serializer_1.save(obj_1)
|
obj_1_name = obj_serializer_1.save(obj_1)
|
||||||
obj_1_loaded = obj_serializer_1.load(obj_1_name)
|
obj_1_loaded = obj_serializer_1.load(obj_1_name)
|
||||||
@@ -123,19 +125,19 @@ def test_obj_serializer_disk_different_types(tmp_path: Path):
|
|||||||
assert obj_1_loaded.foo == "bar"
|
assert obj_1_loaded.foo == "bar"
|
||||||
assert obj_1_name.startswith("MockDataclass_")
|
assert obj_1_name.startswith("MockDataclass_")
|
||||||
|
|
||||||
obj_serializer_2 = ObjectSerializerDisk[int](tmp_path)
|
obj_serializer_2 = ObjectSerializerDisk[int](tmp_path, safe_globals=[int])
|
||||||
obj_2_name = obj_serializer_2.save(9001)
|
obj_2_name = obj_serializer_2.save(9001)
|
||||||
assert obj_serializer_2._obj_class_name == "int"
|
assert obj_serializer_2._obj_class_name == "int"
|
||||||
assert obj_serializer_2.load(obj_2_name) == 9001
|
assert obj_serializer_2.load(obj_2_name) == 9001
|
||||||
assert obj_2_name.startswith("int_")
|
assert obj_2_name.startswith("int_")
|
||||||
|
|
||||||
obj_serializer_3 = ObjectSerializerDisk[str](tmp_path)
|
obj_serializer_3 = ObjectSerializerDisk[str](tmp_path, safe_globals=[str])
|
||||||
obj_3_name = obj_serializer_3.save("foo")
|
obj_3_name = obj_serializer_3.save("foo")
|
||||||
assert obj_serializer_3._obj_class_name == "str"
|
assert obj_serializer_3._obj_class_name == "str"
|
||||||
assert obj_serializer_3.load(obj_3_name) == "foo"
|
assert obj_serializer_3.load(obj_3_name) == "foo"
|
||||||
assert obj_3_name.startswith("str_")
|
assert obj_3_name.startswith("str_")
|
||||||
|
|
||||||
obj_serializer_4 = ObjectSerializerDisk[torch.Tensor](tmp_path)
|
obj_serializer_4 = ObjectSerializerDisk[torch.Tensor](tmp_path, safe_globals=[torch.Tensor])
|
||||||
obj_4_name = obj_serializer_4.save(torch.tensor([1, 2, 3]))
|
obj_4_name = obj_serializer_4.save(torch.tensor([1, 2, 3]))
|
||||||
obj_4_loaded = obj_serializer_4.load(obj_4_name)
|
obj_4_loaded = obj_serializer_4.load(obj_4_name)
|
||||||
assert obj_serializer_4._obj_class_name == "Tensor"
|
assert obj_serializer_4._obj_class_name == "Tensor"
|
||||||
|
|||||||
Reference in New Issue
Block a user