mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into bugfix/log-levels
This commit is contained in:
commit
c7fa2db556
@ -120,7 +120,7 @@ Generate an image with a given prompt, record the seed of the image, and then
|
|||||||
use the `prompt2prompt` syntax to substitute words in the original prompt for
|
use the `prompt2prompt` syntax to substitute words in the original prompt for
|
||||||
words in a new prompt. This works for `img2img` as well.
|
words in a new prompt. This works for `img2img` as well.
|
||||||
|
|
||||||
For example, consider the prompt `a cat.swap(dog) playing with a ball in the forest`. Normally, because of the word words interact with each other when doing a stable diffusion image generation, these two prompts would generate different compositions:
|
For example, consider the prompt `a cat.swap(dog) playing with a ball in the forest`. Normally, because the words interact with each other when doing a stable diffusion image generation, these two prompts would generate different compositions:
|
||||||
- `a cat playing with a ball in the forest`
|
- `a cat playing with a ball in the forest`
|
||||||
- `a dog playing with a ball in the forest`
|
- `a dog playing with a ball in the forest`
|
||||||
|
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import typing
|
import typing
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from importlib.metadata import PackageNotFoundError, version
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from platform import python_version
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import torch
|
||||||
from fastapi import Body
|
from fastapi import Body
|
||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
@ -40,6 +44,24 @@ class AppVersion(BaseModel):
|
|||||||
version: str = Field(description="App version")
|
version: str = Field(description="App version")
|
||||||
|
|
||||||
|
|
||||||
|
class AppDependencyVersions(BaseModel):
|
||||||
|
"""App depencency Versions Response"""
|
||||||
|
|
||||||
|
accelerate: str = Field(description="accelerate version")
|
||||||
|
compel: str = Field(description="compel version")
|
||||||
|
cuda: Optional[str] = Field(description="CUDA version")
|
||||||
|
diffusers: str = Field(description="diffusers version")
|
||||||
|
numpy: str = Field(description="Numpy version")
|
||||||
|
opencv: str = Field(description="OpenCV version")
|
||||||
|
onnx: str = Field(description="ONNX version")
|
||||||
|
pillow: str = Field(description="Pillow (PIL) version")
|
||||||
|
python: str = Field(description="Python version")
|
||||||
|
torch: str = Field(description="PyTorch version")
|
||||||
|
torchvision: str = Field(description="PyTorch Vision version")
|
||||||
|
transformers: str = Field(description="transformers version")
|
||||||
|
xformers: Optional[str] = Field(description="xformers version")
|
||||||
|
|
||||||
|
|
||||||
class AppConfig(BaseModel):
|
class AppConfig(BaseModel):
|
||||||
"""App Config Response"""
|
"""App Config Response"""
|
||||||
|
|
||||||
@ -54,6 +76,29 @@ async def get_version() -> AppVersion:
|
|||||||
return AppVersion(version=__version__)
|
return AppVersion(version=__version__)
|
||||||
|
|
||||||
|
|
||||||
|
@app_router.get("/app_deps", operation_id="get_app_deps", status_code=200, response_model=AppDependencyVersions)
|
||||||
|
async def get_app_deps() -> AppDependencyVersions:
|
||||||
|
try:
|
||||||
|
xformers = version("xformers")
|
||||||
|
except PackageNotFoundError:
|
||||||
|
xformers = None
|
||||||
|
return AppDependencyVersions(
|
||||||
|
accelerate=version("accelerate"),
|
||||||
|
compel=version("compel"),
|
||||||
|
cuda=torch.version.cuda,
|
||||||
|
diffusers=version("diffusers"),
|
||||||
|
numpy=version("numpy"),
|
||||||
|
opencv=version("opencv-python"),
|
||||||
|
onnx=version("onnx"),
|
||||||
|
pillow=version("pillow"),
|
||||||
|
python=python_version(),
|
||||||
|
torch=torch.version.__version__,
|
||||||
|
torchvision=version("torchvision"),
|
||||||
|
transformers=version("transformers"),
|
||||||
|
xformers=xformers,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app_router.get("/config", operation_id="get_config", status_code=200, response_model=AppConfig)
|
@app_router.get("/config", operation_id="get_config", status_code=200, response_model=AppConfig)
|
||||||
async def get_config() -> AppConfig:
|
async def get_config() -> AppConfig:
|
||||||
infill_methods = ["tile", "lama", "cv2"]
|
infill_methods = ["tile", "lama", "cv2"]
|
||||||
|
@ -207,10 +207,12 @@ class IterateInvocationOutput(BaseInvocationOutput):
|
|||||||
item: Any = OutputField(
|
item: Any = OutputField(
|
||||||
description="The item being iterated over", title="Collection Item", ui_type=UIType._CollectionItem
|
description="The item being iterated over", title="Collection Item", ui_type=UIType._CollectionItem
|
||||||
)
|
)
|
||||||
|
index: int = OutputField(description="The index of the item", title="Index")
|
||||||
|
total: int = OutputField(description="The total number of items", title="Total")
|
||||||
|
|
||||||
|
|
||||||
# TODO: Fill this out and move to invocations
|
# TODO: Fill this out and move to invocations
|
||||||
@invocation("iterate", version="1.0.0")
|
@invocation("iterate", version="1.1.0")
|
||||||
class IterateInvocation(BaseInvocation):
|
class IterateInvocation(BaseInvocation):
|
||||||
"""Iterates over a list of items"""
|
"""Iterates over a list of items"""
|
||||||
|
|
||||||
@ -221,7 +223,7 @@ class IterateInvocation(BaseInvocation):
|
|||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> IterateInvocationOutput:
|
def invoke(self, context: InvocationContext) -> IterateInvocationOutput:
|
||||||
"""Produces the outputs as values"""
|
"""Produces the outputs as values"""
|
||||||
return IterateInvocationOutput(item=self.collection[self.index])
|
return IterateInvocationOutput(item=self.collection[self.index], index=self.index, total=len(self.collection))
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("collect_output")
|
@invocation_output("collect_output")
|
||||||
|
@ -3,8 +3,8 @@ import { $authToken } from 'app/store/nanostores/authToken';
|
|||||||
import { $baseUrl } from 'app/store/nanostores/baseUrl';
|
import { $baseUrl } from 'app/store/nanostores/baseUrl';
|
||||||
import { $isDebugging } from 'app/store/nanostores/isDebugging';
|
import { $isDebugging } from 'app/store/nanostores/isDebugging';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { MapStore, WritableAtom, atom, map } from 'nanostores';
|
import { MapStore, atom, map } from 'nanostores';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
ClientToServerEvents,
|
ClientToServerEvents,
|
||||||
ServerToClientEvents,
|
ServerToClientEvents,
|
||||||
@ -16,57 +16,10 @@ import { ManagerOptions, Socket, SocketOptions, io } from 'socket.io-client';
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
$socketOptions?: MapStore<Partial<ManagerOptions & SocketOptions>>;
|
$socketOptions?: MapStore<Partial<ManagerOptions & SocketOptions>>;
|
||||||
$socketUrl?: WritableAtom<string>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeSocketOptions = (): Partial<ManagerOptions & SocketOptions> => {
|
|
||||||
const socketOptions: Parameters<typeof io>[0] = {
|
|
||||||
timeout: 60000,
|
|
||||||
path: '/ws/socket.io',
|
|
||||||
autoConnect: false, // achtung! removing this breaks the dynamic middleware
|
|
||||||
forceNew: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// if building in package mode, replace socket url with open api base url minus the http protocol
|
|
||||||
if (['nodes', 'package'].includes(import.meta.env.MODE)) {
|
|
||||||
const authToken = $authToken.get();
|
|
||||||
if (authToken) {
|
|
||||||
// TODO: handle providing jwt to socket.io
|
|
||||||
socketOptions.auth = { token: authToken };
|
|
||||||
}
|
|
||||||
|
|
||||||
socketOptions.transports = ['websocket', 'polling'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return socketOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeSocketUrl = (): string => {
|
|
||||||
const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
|
||||||
let socketUrl = `${wsProtocol}://${window.location.host}`;
|
|
||||||
if (['nodes', 'package'].includes(import.meta.env.MODE)) {
|
|
||||||
const baseUrl = $baseUrl.get();
|
|
||||||
if (baseUrl) {
|
|
||||||
//eslint-disable-next-line
|
|
||||||
socketUrl = baseUrl.replace(/^https?\:\/\//i, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return socketUrl;
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeSocket = (): Socket<ServerToClientEvents, ClientToServerEvents> => {
|
|
||||||
const socketOptions = makeSocketOptions();
|
|
||||||
const socketUrl = $socketUrl.get();
|
|
||||||
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(
|
|
||||||
socketUrl,
|
|
||||||
{ ...socketOptions, ...$socketOptions.get() }
|
|
||||||
);
|
|
||||||
return socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const $socketOptions = map<Partial<ManagerOptions & SocketOptions>>({});
|
export const $socketOptions = map<Partial<ManagerOptions & SocketOptions>>({});
|
||||||
export const $socketUrl = atom<string>(makeSocketUrl());
|
|
||||||
export const $isSocketInitialized = atom<boolean>(false);
|
export const $isSocketInitialized = atom<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,23 +27,50 @@ export const $isSocketInitialized = atom<boolean>(false);
|
|||||||
*/
|
*/
|
||||||
export const useSocketIO = () => {
|
export const useSocketIO = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const socketOptions = useStore($socketOptions);
|
|
||||||
const socketUrl = useStore($socketUrl);
|
|
||||||
const baseUrl = useStore($baseUrl);
|
const baseUrl = useStore($baseUrl);
|
||||||
const authToken = useStore($authToken);
|
const authToken = useStore($authToken);
|
||||||
|
const addlSocketOptions = useStore($socketOptions);
|
||||||
|
|
||||||
|
const socketUrl = useMemo(() => {
|
||||||
|
const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
||||||
|
if (baseUrl) {
|
||||||
|
return baseUrl.replace(/^https?:\/\//i, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${wsProtocol}://${window.location.host}`;
|
||||||
|
}, [baseUrl]);
|
||||||
|
|
||||||
|
const socketOptions = useMemo(() => {
|
||||||
|
const options: Parameters<typeof io>[0] = {
|
||||||
|
timeout: 60000,
|
||||||
|
path: '/ws/socket.io',
|
||||||
|
autoConnect: false, // achtung! removing this breaks the dynamic middleware
|
||||||
|
forceNew: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (authToken) {
|
||||||
|
options.auth = { token: authToken };
|
||||||
|
options.transports = ['websocket', 'polling'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...options, ...addlSocketOptions };
|
||||||
|
}, [authToken, addlSocketOptions]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ($isSocketInitialized.get()) {
|
if ($isSocketInitialized.get()) {
|
||||||
// Singleton!
|
// Singleton!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const socket = makeSocket();
|
|
||||||
|
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(
|
||||||
|
socketUrl,
|
||||||
|
socketOptions
|
||||||
|
);
|
||||||
setEventListeners({ dispatch, socket });
|
setEventListeners({ dispatch, socket });
|
||||||
socket.connect();
|
socket.connect();
|
||||||
|
|
||||||
if ($isDebugging.get()) {
|
if ($isDebugging.get()) {
|
||||||
window.$socketOptions = $socketOptions;
|
window.$socketOptions = $socketOptions;
|
||||||
window.$socketUrl = $socketUrl;
|
|
||||||
console.log('Socket initialized', socket);
|
console.log('Socket initialized', socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,11 +79,10 @@ export const useSocketIO = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
if ($isDebugging.get()) {
|
if ($isDebugging.get()) {
|
||||||
window.$socketOptions = undefined;
|
window.$socketOptions = undefined;
|
||||||
window.$socketUrl = undefined;
|
|
||||||
console.log('Socket teardown', socket);
|
console.log('Socket teardown', socket);
|
||||||
}
|
}
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
$isSocketInitialized.set(false);
|
$isSocketInitialized.set(false);
|
||||||
};
|
};
|
||||||
}, [dispatch, socketOptions, socketUrl, baseUrl, authToken]);
|
}, [dispatch, socketOptions, socketUrl]);
|
||||||
};
|
};
|
||||||
|
@ -73,7 +73,13 @@ const BoardContextMenu = ({
|
|||||||
addToast({
|
addToast({
|
||||||
title: t('gallery.preparingDownload'),
|
title: t('gallery.preparingDownload'),
|
||||||
status: 'success',
|
status: 'success',
|
||||||
...(response.response ? { description: response.response } : {}),
|
...(response.response
|
||||||
|
? {
|
||||||
|
description: response.response,
|
||||||
|
duration: null,
|
||||||
|
isClosable: true,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -59,7 +59,13 @@ const MultipleSelectionMenuItems = () => {
|
|||||||
addToast({
|
addToast({
|
||||||
title: t('gallery.preparingDownload'),
|
title: t('gallery.preparingDownload'),
|
||||||
status: 'success',
|
status: 'success',
|
||||||
...(response.response ? { description: response.response } : {}),
|
...(response.response
|
||||||
|
? {
|
||||||
|
description: response.response,
|
||||||
|
duration: null,
|
||||||
|
isClosable: true,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
|
Loading…
Reference in New Issue
Block a user