From 1e94d7739a59f295e958bf1bf541914fcf13cc81 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Thu, 25 May 2023 14:47:16 -0400 Subject: [PATCH 01/31] fix metadata references, add support for negative_conditioning syntax --- .../components/CurrentImageButtons.tsx | 23 +++++++++++-------- .../gallery/components/HoverableImage.tsx | 5 +++- .../ImageMetadataViewer.tsx | 12 ---------- .../graphBuilders/buildTextToImageGraph.ts | 2 +- .../parameters/hooks/useParameters.ts | 13 ++++++----- .../features/parameters/hooks/usePrompt.ts | 15 ++++-------- 6 files changed, 30 insertions(+), 40 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx index f5265b54db..c19a404a37 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx @@ -109,8 +109,9 @@ const currentImageButtonsSelector = createSelector( isLightboxOpen, shouldHidePreview, image: selectedImage, - seed: selectedImage?.metadata?.invokeai?.node?.seed, - prompt: selectedImage?.metadata?.invokeai?.node?.prompt, + seed: selectedImage?.metadata?.seed, + prompt: selectedImage?.metadata?.positive_conditioning, + negativePrompt: selectedImage?.metadata?.negative_conditioning, }; }, { @@ -245,13 +246,16 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { ); const handleUseSeed = useCallback(() => { - recallSeed(image?.metadata?.invokeai?.node?.seed); + recallSeed(image?.metadata?.seed); }, [image, recallSeed]); useHotkeys('s', handleUseSeed, [image]); const handleUsePrompt = useCallback(() => { - recallPrompt(image?.metadata?.invokeai?.node?.prompt); + recallPrompt( + image?.metadata?.positive_conditioning, + image?.metadata?.negative_conditioning + ); }, [image, recallPrompt]); useHotkeys('p', handleUsePrompt, [image]); @@ -454,7 +458,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { {t('parameters.copyImageToLink')} - + } size="sm" w="100%"> {t('parameters.downloadImage')} @@ -500,7 +504,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { icon={} tooltip={`${t('parameters.usePrompt')} (P)`} aria-label={`${t('parameters.usePrompt')} (P)`} - isDisabled={!image?.metadata?.invokeai?.node?.prompt} + isDisabled={!image?.metadata?.positive_conditioning} onClick={handleUsePrompt} /> @@ -508,7 +512,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { icon={} tooltip={`${t('parameters.useSeed')} (S)`} aria-label={`${t('parameters.useSeed')} (S)`} - isDisabled={!image?.metadata?.invokeai?.node?.seed} + isDisabled={!image?.metadata?.seed} onClick={handleUseSeed} /> @@ -517,9 +521,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { tooltip={`${t('parameters.useAll')} (A)`} aria-label={`${t('parameters.useAll')} (A)`} isDisabled={ - !['txt2img', 'img2img', 'inpaint'].includes( - String(image?.metadata?.invokeai?.node?.type) - ) + // not sure what this list should be + !['t2l', 'l2l', 'inpaint'].includes(String(image?.metadata?.type)) } onClick={handleClickUseAllParameters} /> diff --git a/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx b/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx index 04fecac463..fccfba0a70 100644 --- a/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx @@ -155,7 +155,10 @@ const HoverableImage = memo((props: HoverableImageProps) => { // Recall parameters handlers const handleRecallPrompt = useCallback(() => { - recallPrompt(image.metadata?.positive_conditioning); + recallPrompt( + image.metadata?.positive_conditioning, + image.metadata?.negative_conditioning + ); }, [image, recallPrompt]); const handleRecallSeed = useCallback(() => { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx index b4bf9a6d25..b01191105e 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer.tsx @@ -8,29 +8,20 @@ import { Text, Tooltip, } from '@chakra-ui/react'; -import * as InvokeAI from 'app/types/invokeai'; import { useAppDispatch } from 'app/store/storeHooks'; import { useGetUrl } from 'common/util/getUrl'; import promptToString from 'common/util/promptToString'; -import { seedWeightsToString } from 'common/util/seedWeightPairs'; -import useSetBothPrompts from 'features/parameters/hooks/usePrompt'; import { setCfgScale, setHeight, setImg2imgStrength, setNegativePrompt, - setPerlin, setPositivePrompt, setScheduler, - setSeamless, setSeed, - setSeedWeights, - setShouldFitToWidthHeight, setSteps, - setThreshold, setWidth, } from 'features/parameters/store/generationSlice'; -import { setHiresFix } from 'features/parameters/store/postprocessingSlice'; import { setShouldShowImageDetails } from 'features/ui/store/uiSlice'; import { memo } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -39,7 +30,6 @@ import { FaCopy } from 'react-icons/fa'; import { IoArrowUndoCircleOutline } from 'react-icons/io5'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; import { ImageDTO } from 'services/api'; -import { filter } from 'lodash-es'; import { Scheduler } from 'app/constants'; type MetadataItemProps = { @@ -126,8 +116,6 @@ const memoEqualityCheck = ( const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => { const dispatch = useAppDispatch(); - const setBothPrompts = useSetBothPrompts(); - useHotkeys('esc', () => { dispatch(setShouldShowImageDetails(false)); }); diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts index cbe16abe28..51f89e8f74 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts @@ -11,7 +11,7 @@ import { addNoiseNodes } from '../nodeBuilders/addNoiseNodes'; const POSITIVE_CONDITIONING = 'positive_conditioning'; const NEGATIVE_CONDITIONING = 'negative_conditioning'; const TEXT_TO_LATENTS = 'text_to_latents'; -const LATENTS_TO_IMAGE = 'latnets_to_image'; +const LATENTS_TO_IMAGE = 'latents_to_image'; /** * Builds the Text to Image tab graph. diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts index ad9985b5de..27ae63e5dd 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useParameters.ts @@ -21,8 +21,8 @@ export const useParameters = () => { * Sets prompt with toast */ const recallPrompt = useCallback( - (prompt: unknown) => { - if (!isString(prompt)) { + (prompt: unknown, negativePrompt?: unknown) => { + if (!isString(prompt) || !isString(negativePrompt)) { toaster({ title: t('toast.promptNotSet'), description: t('toast.promptNotSetDesc'), @@ -33,7 +33,7 @@ export const useParameters = () => { return; } - setBothPrompts(prompt); + setBothPrompts(prompt, negativePrompt); toaster({ title: t('toast.promptSet'), status: 'info', @@ -112,12 +112,13 @@ export const useParameters = () => { const recallAllParameters = useCallback( (image: ImageDTO | undefined) => { const type = image?.metadata?.type; - if (['txt2img', 'img2img', 'inpaint'].includes(String(type))) { + // not sure what this list should be + if (['t2l', 'l2l', 'inpaint'].includes(String(type))) { dispatch(allParametersSet(image)); - if (image?.metadata?.type === 'img2img') { + if (image?.metadata?.type === 'l2l') { dispatch(setActiveTab('img2img')); - } else if (image?.metadata?.type === 'txt2img') { + } else if (image?.metadata?.type === 't2l') { dispatch(setActiveTab('txt2img')); } diff --git a/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts b/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts index 2a6a832720..cb39be6e1e 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts @@ -12,16 +12,11 @@ const useSetBothPrompts = () => { const dispatch = useAppDispatch(); return useCallback( - (inputPrompt: InvokeAI.Prompt) => { - const promptString = - typeof inputPrompt === 'string' - ? inputPrompt - : promptToString(inputPrompt); - - const [prompt, negativePrompt] = getPromptAndNegative(promptString); - - dispatch(setPositivePrompt(prompt)); - dispatch(setNegativePrompt(negativePrompt)); + (inputPrompt: InvokeAI.Prompt, negativePrompt?: InvokeAI.Prompt) => { + dispatch(setPositivePrompt(inputPrompt)); + if (negativePrompt) { + dispatch(setNegativePrompt(negativePrompt)); + } }, [dispatch] ); From a4c44edf8d0393bad37e378636e6bd9f6c672dc4 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Thu, 25 May 2023 15:17:02 -0400 Subject: [PATCH 02/31] more use parameter fixes --- .../gallery/components/HoverableImage.tsx | 3 +- .../features/parameters/hooks/usePrompt.ts | 6 ++-- .../store/setAllParametersReducer.ts | 35 +++++++++++++------ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx b/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx index fccfba0a70..ed427f4984 100644 --- a/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx @@ -251,7 +251,8 @@ const HoverableImage = memo((props: HoverableImageProps) => { icon={} onClickCapture={handleUseAllParameters} isDisabled={ - !['txt2img', 'img2img', 'inpaint'].includes( + // what should these be + !['t2l', 'l2l', 'inpaint'].includes( String(image?.metadata?.type) ) } diff --git a/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts b/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts index cb39be6e1e..3fee0bcdd8 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/usePrompt.ts @@ -12,11 +12,9 @@ const useSetBothPrompts = () => { const dispatch = useAppDispatch(); return useCallback( - (inputPrompt: InvokeAI.Prompt, negativePrompt?: InvokeAI.Prompt) => { + (inputPrompt: InvokeAI.Prompt, negativePrompt: InvokeAI.Prompt) => { dispatch(setPositivePrompt(inputPrompt)); - if (negativePrompt) { - dispatch(setNegativePrompt(negativePrompt)); - } + dispatch(setNegativePrompt(negativePrompt)); }, [dispatch] ); diff --git a/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts b/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts index d6d1af0f8e..8f06c7d0ef 100644 --- a/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts +++ b/invokeai/frontend/web/src/features/parameters/store/setAllParametersReducer.ts @@ -7,19 +7,29 @@ export const setAllParametersReducer = ( state: Draft, action: PayloadAction ) => { - const node = action.payload?.metadata.invokeai?.node; + const metadata = action.payload?.metadata; - if (!node) { + if (!metadata) { return; } + // not sure what this list should be if ( - node.type === 'txt2img' || - node.type === 'img2img' || - node.type === 'inpaint' + metadata.type === 't2l' || + metadata.type === 'l2l' || + metadata.type === 'inpaint' ) { - const { cfg_scale, height, model, prompt, scheduler, seed, steps, width } = - node; + const { + cfg_scale, + height, + model, + positive_conditioning, + negative_conditioning, + scheduler, + seed, + steps, + width, + } = metadata; if (cfg_scale !== undefined) { state.cfgScale = Number(cfg_scale); @@ -30,8 +40,11 @@ export const setAllParametersReducer = ( if (model !== undefined) { state.model = String(model); } - if (prompt !== undefined) { - state.positivePrompt = String(prompt); + if (positive_conditioning !== undefined) { + state.positivePrompt = String(positive_conditioning); + } + if (negative_conditioning !== undefined) { + state.negativePrompt = String(negative_conditioning); } if (scheduler !== undefined) { const schedulerString = String(scheduler); @@ -51,8 +64,8 @@ export const setAllParametersReducer = ( } } - if (node.type === 'img2img') { - const { fit, image } = node as ImageToImageInvocation; + if (metadata.type === 'l2l') { + const { fit, image } = metadata as ImageToImageInvocation; if (fit !== undefined) { state.shouldFitToWidthHeight = Boolean(fit); From 93bb27f2c74a908d59d04ce3f4b771c09bc75e16 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Thu, 25 May 2023 16:56:39 -0400 Subject: [PATCH 03/31] fix gallery navigation --- .../src/features/gallery/components/NextPrevImageButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/NextPrevImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/NextPrevImageButtons.tsx index d0d25f8bc6..fcf8359187 100644 --- a/invokeai/frontend/web/src/features/gallery/components/NextPrevImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/NextPrevImageButtons.tsx @@ -33,7 +33,7 @@ export const nextPrevImageButtonsSelector = createSelector( } const currentImageIndex = state[currentCategory].ids.findIndex( - (i) => i === selectedImage.name + (i) => i === selectedImage.image_name ); const nextImageIndex = clamp( From d98868e524651917e21565db061f6253ff9a834d Mon Sep 17 00:00:00 2001 From: Kent Keirsey <31807370+hipsterusername@users.noreply.github.com> Date: Wed, 24 May 2023 16:42:49 -0400 Subject: [PATCH 04/31] Update generationSlice.ts to change Default Scheduler --- .../web/src/features/parameters/store/generationSlice.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index f5054f1969..849f848ff3 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -52,7 +52,7 @@ export const initialGenerationState: GenerationState = { perlin: 0, positivePrompt: '', negativePrompt: '', - scheduler: 'lms', + scheduler: 'euler', seamBlur: 16, seamSize: 96, seamSteps: 30, From d4acd49ee346a642b0111687a3250ecbd4326e17 Mon Sep 17 00:00:00 2001 From: Kent Keirsey <31807370+hipsterusername@users.noreply.github.com> Date: Wed, 24 May 2023 16:45:21 -0400 Subject: [PATCH 05/31] Update generate.py --- invokeai/app/invocations/generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/app/invocations/generate.py b/invokeai/app/invocations/generate.py index aa16243093..6af959a5bd 100644 --- a/invokeai/app/invocations/generate.py +++ b/invokeai/app/invocations/generate.py @@ -56,7 +56,7 @@ class TextToImageInvocation(BaseInvocation, SDImageInvocation): width: int = Field(default=512, multiple_of=8, gt=0, description="The width of the resulting image", ) height: int = Field(default=512, multiple_of=8, gt=0, description="The height of the resulting image", ) cfg_scale: float = Field(default=7.5, ge=1, description="The Classifier-Free Guidance, higher values may result in a result closer to the prompt", ) - scheduler: SAMPLER_NAME_VALUES = Field(default="lms", description="The scheduler to use" ) + scheduler: SAMPLER_NAME_VALUES = Field(default="euler", description="The scheduler to use" ) model: str = Field(default="", description="The model to use (currently ignored)") # fmt: on From 05fb0ac2b29026ca39664e17a683027cb99d8ea2 Mon Sep 17 00:00:00 2001 From: Kent Keirsey <31807370+hipsterusername@users.noreply.github.com> Date: Wed, 24 May 2023 16:47:47 -0400 Subject: [PATCH 06/31] Update latent.py --- invokeai/app/invocations/latent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 34da76d39a..12cebdf41d 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -167,7 +167,7 @@ class TextToLatentsInvocation(BaseInvocation): noise: Optional[LatentsField] = Field(description="The noise to use") steps: int = Field(default=10, gt=0, description="The number of steps to use to generate the image") cfg_scale: float = Field(default=7.5, gt=0, description="The Classifier-Free Guidance, higher values may result in a result closer to the prompt", ) - scheduler: SAMPLER_NAME_VALUES = Field(default="lms", description="The scheduler to use" ) + scheduler: SAMPLER_NAME_VALUES = Field(default="euler", description="The scheduler to use" ) model: str = Field(default="", description="The model to use (currently ignored)") # seamless: bool = Field(default=False, description="Whether or not to generate an image that can tile without seams", ) # seamless_axes: str = Field(default="", description="The axes to tile the image on, 'x' and/or 'y'") From d2c8a53c55faf56dd1f2e0dfb80faa960b0eb10d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 May 2023 23:47:18 +1000 Subject: [PATCH 07/31] feat(nodes): change intermediates handling - `ImageType` is now restricted to `results` and `uploads`. - Add a reserved `meta` field to nodes to hold the `is_intermediate` boolean. We can extend it in the future to support other node `meta`. - Add a `is_intermediate` column to the `images` table to hold this. (When `latents`, `conditioning` etc are added to the DB, they will also have this column.) - All nodes default to `*not* intermediate`. Nodes must explicitly be marked `intermediate` for their outputs to be `intermediate`. - When building a graph, you can set `node.meta.is_intermediate=True` and it will be handled as an intermediate. - Add a new `update()` method to the `ImageService`, and a route to call it. Updates have a strict model, currently only `session_id` and `image_category` may be updated. - Add a new `update()` method to the `ImageRecordStorageService` to update the image record using the model. --- invokeai/app/api/routers/images.py | 60 ++++++++++++++----- invokeai/app/invocations/baseinvocation.py | 8 +++ invokeai/app/invocations/cv.py | 2 +- invokeai/app/invocations/latent.py | 1 + invokeai/app/invocations/reconstruct.py | 2 +- invokeai/app/models/image.py | 1 - invokeai/app/services/image_record_storage.py | 56 ++++++++++++++++- invokeai/app/services/images.py | 35 ++++++++++- invokeai/app/services/models/image_record.py | 25 +++++++- 9 files changed, 168 insertions(+), 22 deletions(-) diff --git a/invokeai/app/api/routers/images.py b/invokeai/app/api/routers/images.py index 0615ff187e..920181ff8b 100644 --- a/invokeai/app/api/routers/images.py +++ b/invokeai/app/api/routers/images.py @@ -1,5 +1,6 @@ import io -from fastapi import HTTPException, Path, Query, Request, Response, UploadFile +from typing import Optional +from fastapi import Body, HTTPException, Path, Query, Request, Response, UploadFile from fastapi.routing import APIRouter from fastapi.responses import FileResponse from PIL import Image @@ -7,7 +8,11 @@ from invokeai.app.models.image import ( ImageCategory, ImageType, ) -from invokeai.app.services.models.image_record import ImageDTO, ImageUrlsDTO +from invokeai.app.services.models.image_record import ( + ImageDTO, + ImageRecordChanges, + ImageUrlsDTO, +) from invokeai.app.services.item_storage import PaginatedResults from ..dependencies import ApiDependencies @@ -27,10 +32,17 @@ images_router = APIRouter(prefix="/v1/images", tags=["images"]) ) async def upload_image( file: UploadFile, - image_type: ImageType, request: Request, response: Response, - image_category: ImageCategory = ImageCategory.GENERAL, + image_category: ImageCategory = Query( + default=ImageCategory.GENERAL, description="The category of the image" + ), + is_intermediate: bool = Query( + default=False, description="Whether this is an intermediate image" + ), + session_id: Optional[str] = Query( + default=None, description="The session ID associated with this upload, if any" + ), ) -> ImageDTO: """Uploads an image""" if not file.content_type.startswith("image"): @@ -46,9 +58,11 @@ async def upload_image( try: image_dto = ApiDependencies.invoker.services.images.create( - pil_image, - image_type, - image_category, + image=pil_image, + image_type=ImageType.UPLOAD, + image_category=image_category, + session_id=session_id, + is_intermediate=is_intermediate, ) response.status_code = 201 @@ -61,7 +75,7 @@ async def upload_image( @images_router.delete("/{image_type}/{image_name}", operation_id="delete_image") async def delete_image( - image_type: ImageType = Query(description="The type of image to delete"), + image_type: ImageType = Path(description="The type of image to delete"), image_name: str = Path(description="The name of the image to delete"), ) -> None: """Deletes an image""" @@ -73,6 +87,28 @@ async def delete_image( pass +@images_router.patch( + "/{image_type}/{image_name}", + operation_id="update_image", + response_model=ImageDTO, +) +async def update_image( + image_type: ImageType = Path(description="The type of image to update"), + image_name: str = Path(description="The name of the image to update"), + image_changes: ImageRecordChanges = Body( + description="The changes to apply to the image" + ), +) -> ImageDTO: + """Updates an image""" + + try: + return ApiDependencies.invoker.services.images.update( + image_type, image_name, image_changes + ) + except Exception as e: + raise HTTPException(status_code=400, detail="Failed to update image") + + @images_router.get( "/{image_type}/{image_name}/metadata", operation_id="get_image_metadata", @@ -85,9 +121,7 @@ async def get_image_metadata( """Gets an image's metadata""" try: - return ApiDependencies.invoker.services.images.get_dto( - image_type, image_name - ) + return ApiDependencies.invoker.services.images.get_dto(image_type, image_name) except Exception as e: raise HTTPException(status_code=404) @@ -113,9 +147,7 @@ async def get_image_full( """Gets a full-resolution image file""" try: - path = ApiDependencies.invoker.services.images.get_path( - image_type, image_name - ) + path = ApiDependencies.invoker.services.images.get_path(image_type, image_name) if not ApiDependencies.invoker.services.images.validate_path(path): raise HTTPException(status_code=404) diff --git a/invokeai/app/invocations/baseinvocation.py b/invokeai/app/invocations/baseinvocation.py index da61641105..1ba498c9d8 100644 --- a/invokeai/app/invocations/baseinvocation.py +++ b/invokeai/app/invocations/baseinvocation.py @@ -39,6 +39,12 @@ class BaseInvocationOutput(BaseModel): return tuple(subclasses) +class InvocationMeta(BaseModel): + is_intermediate: bool = Field( + default=False, + description="Whether this is an intermediate node. Intermediate nodes are periodically deleted." + ) + class BaseInvocation(ABC, BaseModel): """A node to process inputs and produce outputs. May use dependency injection in __init__ to receive providers. @@ -78,6 +84,8 @@ class BaseInvocation(ABC, BaseModel): #fmt: off id: str = Field(description="The id of this node. Must be unique among all nodes.") + type: str = Field(description="The type of this node. Must be unique among all nodes.") + meta: InvocationMeta = Field(default=InvocationMeta(), description="The meta properties of this node.") #fmt: on diff --git a/invokeai/app/invocations/cv.py b/invokeai/app/invocations/cv.py index 26e06a2af8..d900ecfdbf 100644 --- a/invokeai/app/invocations/cv.py +++ b/invokeai/app/invocations/cv.py @@ -57,7 +57,7 @@ class CvInpaintInvocation(BaseInvocation, CvInvocationConfig): image_dto = context.services.images.create( image=image_inpainted, - image_type=ImageType.INTERMEDIATE, + image_type=ImageType.RESULT, image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 12cebdf41d..9f78d72b77 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -370,6 +370,7 @@ class LatentsToImageInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, + is_intermediate=self.meta.is_intermediate ) return ImageOutput( diff --git a/invokeai/app/invocations/reconstruct.py b/invokeai/app/invocations/reconstruct.py index 024134cd46..a234693128 100644 --- a/invokeai/app/invocations/reconstruct.py +++ b/invokeai/app/invocations/reconstruct.py @@ -43,7 +43,7 @@ class RestoreFaceInvocation(BaseInvocation): # TODO: can this return multiple results? image_dto = context.services.images.create( image=results[0][0], - image_type=ImageType.INTERMEDIATE, + image_type=ImageType.RESULT, image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, diff --git a/invokeai/app/models/image.py b/invokeai/app/models/image.py index 544951ea34..46b50145aa 100644 --- a/invokeai/app/models/image.py +++ b/invokeai/app/models/image.py @@ -10,7 +10,6 @@ class ImageType(str, Enum, metaclass=MetaEnum): RESULT = "results" UPLOAD = "uploads" - INTERMEDIATE = "intermediates" class InvalidImageTypeException(ValueError): diff --git a/invokeai/app/services/image_record_storage.py b/invokeai/app/services/image_record_storage.py index 4e1f73978b..188a411a6b 100644 --- a/invokeai/app/services/image_record_storage.py +++ b/invokeai/app/services/image_record_storage.py @@ -12,6 +12,7 @@ from invokeai.app.models.image import ( ) from invokeai.app.services.models.image_record import ( ImageRecord, + ImageRecordChanges, deserialize_image_record, ) from invokeai.app.services.item_storage import PaginatedResults @@ -49,6 +50,16 @@ class ImageRecordStorageBase(ABC): """Gets an image record.""" pass + @abstractmethod + def update( + self, + image_name: str, + image_type: ImageType, + changes: ImageRecordChanges, + ) -> None: + """Updates an image record.""" + pass + @abstractmethod def get_many( self, @@ -78,6 +89,7 @@ class ImageRecordStorageBase(ABC): session_id: Optional[str], node_id: Optional[str], metadata: Optional[ImageMetadata], + is_intermediate: bool = False, ) -> datetime: """Saves an image record.""" pass @@ -125,6 +137,7 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): session_id TEXT, node_id TEXT, metadata TEXT, + is_intermediate BOOLEAN DEFAULT FALSE, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Updated via trigger updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -193,6 +206,42 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): return deserialize_image_record(dict(result)) + def update( + self, + image_name: str, + image_type: ImageType, + changes: ImageRecordChanges, + ) -> None: + try: + self._lock.acquire() + # Change the category of the image + if changes.image_category is not None: + self._cursor.execute( + f"""--sql + UPDATE images + SET image_category = ? + WHERE image_name = ?; + """, + (changes.image_category, image_name), + ) + + # Change the session associated with the image + if changes.session_id is not None: + self._cursor.execute( + f"""--sql + UPDATE images + SET session_id = ? + WHERE image_name = ?; + """, + (changes.session_id, image_name), + ) + self._conn.commit() + except sqlite3.Error as e: + self._conn.rollback() + raise ImageRecordSaveException from e + finally: + self._lock.release() + def get_many( self, image_type: ImageType, @@ -265,6 +314,7 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): height: int, node_id: Optional[str], metadata: Optional[ImageMetadata], + is_intermediate: bool = False, ) -> datetime: try: metadata_json = ( @@ -281,9 +331,10 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): height, node_id, session_id, - metadata + metadata, + is_intermediate ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?); + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); """, ( image_name, @@ -294,6 +345,7 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): node_id, session_id, metadata_json, + is_intermediate, ), ) self._conn.commit() diff --git a/invokeai/app/services/images.py b/invokeai/app/services/images.py index 914dd3b6d3..d0f7236fe2 100644 --- a/invokeai/app/services/images.py +++ b/invokeai/app/services/images.py @@ -20,6 +20,7 @@ from invokeai.app.services.image_record_storage import ( from invokeai.app.services.models.image_record import ( ImageRecord, ImageDTO, + ImageRecordChanges, image_record_to_dto, ) from invokeai.app.services.image_file_storage import ( @@ -31,7 +32,6 @@ from invokeai.app.services.image_file_storage import ( from invokeai.app.services.item_storage import ItemStorageABC, PaginatedResults from invokeai.app.services.metadata import MetadataServiceBase from invokeai.app.services.urls import UrlServiceBase -from invokeai.app.util.misc import get_iso_timestamp if TYPE_CHECKING: from invokeai.app.services.graph import GraphExecutionState @@ -48,11 +48,21 @@ class ImageServiceABC(ABC): image_category: ImageCategory, node_id: Optional[str] = None, session_id: Optional[str] = None, - metadata: Optional[ImageMetadata] = None, + intermediate: bool = False, ) -> ImageDTO: """Creates an image, storing the file and its metadata.""" pass + @abstractmethod + def update( + self, + image_type: ImageType, + image_name: str, + changes: ImageRecordChanges, + ) -> ImageDTO: + """Updates an image.""" + pass + @abstractmethod def get_pil_image(self, image_type: ImageType, image_name: str) -> PILImageType: """Gets an image as a PIL image.""" @@ -157,6 +167,7 @@ class ImageService(ImageServiceABC): image_category: ImageCategory, node_id: Optional[str] = None, session_id: Optional[str] = None, + is_intermediate: bool = False, ) -> ImageDTO: if image_type not in ImageType: raise InvalidImageTypeException @@ -184,6 +195,8 @@ class ImageService(ImageServiceABC): image_category=image_category, width=width, height=height, + # Meta fields + is_intermediate=is_intermediate, # Nullable fields node_id=node_id, session_id=session_id, @@ -217,6 +230,7 @@ class ImageService(ImageServiceABC): created_at=created_at, updated_at=created_at, # this is always the same as the created_at at this time deleted_at=None, + is_intermediate=is_intermediate, # Extra non-nullable fields for DTO image_url=image_url, thumbnail_url=thumbnail_url, @@ -231,6 +245,23 @@ class ImageService(ImageServiceABC): self._services.logger.error("Problem saving image record and file") raise e + def update( + self, + image_type: ImageType, + image_name: str, + changes: ImageRecordChanges, + ) -> ImageDTO: + try: + self._services.records.update(image_name, image_type, changes) + return self.get_dto(image_type, image_name) + except ImageRecordSaveException: + self._services.logger.error("Failed to update image record") + raise + except Exception as e: + self._services.logger.error("Problem updating image record") + raise e + + def get_pil_image(self, image_type: ImageType, image_name: str) -> PILImageType: try: return self._services.files.get(image_type, image_name) diff --git a/invokeai/app/services/models/image_record.py b/invokeai/app/services/models/image_record.py index c1155ff73e..26e4929be2 100644 --- a/invokeai/app/services/models/image_record.py +++ b/invokeai/app/services/models/image_record.py @@ -1,6 +1,6 @@ import datetime from typing import Optional, Union -from pydantic import BaseModel, Field +from pydantic import BaseModel, Extra, Field, StrictStr from invokeai.app.models.image import ImageCategory, ImageType from invokeai.app.models.metadata import ImageMetadata from invokeai.app.util.misc import get_iso_timestamp @@ -31,6 +31,8 @@ class ImageRecord(BaseModel): description="The deleted timestamp of the image." ) """The deleted timestamp of the image.""" + is_intermediate: bool = Field(description="Whether this is an intermediate image.") + """Whether this is an intermediate image.""" session_id: Optional[str] = Field( default=None, description="The session ID that generated this image, if it is a generated image.", @@ -48,6 +50,25 @@ class ImageRecord(BaseModel): """A limited subset of the image's generation metadata. Retrieve the image's session for full metadata.""" +class ImageRecordChanges(BaseModel, extra=Extra.forbid): + """A set of changes to apply to an image record. + + Only limited changes are valid: + - `image_category`: change the category of an image + - `session_id`: change the session associated with an image + """ + + image_category: Optional[ImageCategory] = Field( + description="The image's new category." + ) + """The image's new category.""" + session_id: Optional[StrictStr] = Field( + default=None, + description="The image's new session ID.", + ) + """The image's new session ID.""" + + class ImageUrlsDTO(BaseModel): """The URLs for an image and its thumbnail.""" @@ -95,6 +116,7 @@ def deserialize_image_record(image_dict: dict) -> ImageRecord: created_at = image_dict.get("created_at", get_iso_timestamp()) updated_at = image_dict.get("updated_at", get_iso_timestamp()) deleted_at = image_dict.get("deleted_at", get_iso_timestamp()) + is_intermediate = image_dict.get("is_intermediate", False) raw_metadata = image_dict.get("metadata") @@ -115,4 +137,5 @@ def deserialize_image_record(image_dict: dict) -> ImageRecord: created_at=created_at, updated_at=updated_at, deleted_at=deleted_at, + is_intermediate=is_intermediate, ) From 5025f84627bc52279b42ea7f2f5d587480bc5860 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 May 2023 23:47:36 +1000 Subject: [PATCH 08/31] chore(ui): regen api client --- .../frontend/web/src/services/api/index.ts | 15 +++-- .../src/services/api/models/AddInvocation.ts | 6 ++ .../services/api/models/CollectInvocation.ts | 6 ++ .../services/api/models/CompelInvocation.ts | 6 ++ .../api/models/CvInpaintInvocation.ts | 5 ++ .../services/api/models/DivideInvocation.ts | 6 ++ .../web/src/services/api/models/Graph.ts | 15 +++-- .../services/api/models/GraphInvocation.ts | 8 ++- ...urInvocation.ts => ImageBlurInvocation.ts} | 9 ++- .../src/services/api/models/ImageCategory.ts | 2 +- .../api/models/ImageChannelInvocation.ts | 30 ++++++++++ .../api/models/ImageConvertInvocation.ts | 30 ++++++++++ ...geInvocation.ts => ImageCropInvocation.ts} | 9 ++- .../web/src/services/api/models/ImageDTO.ts | 4 ++ ...ation.ts => ImageInverseLerpInvocation.ts} | 9 ++- ...rpInvocation.ts => ImageLerpInvocation.ts} | 9 ++- .../api/models/ImageMultiplyInvocation.ts | 30 ++++++++++ ...eInvocation.ts => ImagePasteInvocation.ts} | 9 ++- .../services/api/models/ImageRecordChanges.ts | 24 ++++++++ .../api/models/ImageToImageInvocation.ts | 5 ++ .../api/models/ImageToLatentsInvocation.ts | 5 ++ .../web/src/services/api/models/ImageType.ts | 2 +- .../api/models/InfillColorInvocation.ts | 5 ++ .../api/models/InfillPatchMatchInvocation.ts | 5 ++ .../api/models/InfillTileInvocation.ts | 5 ++ .../services/api/models/InpaintInvocation.ts | 5 ++ .../src/services/api/models/InvocationMeta.ts | 11 ++++ .../services/api/models/IterateInvocation.ts | 9 ++- .../api/models/LatentsToImageInvocation.ts | 5 ++ .../api/models/LatentsToLatentsInvocation.ts | 13 ++--- .../api/models/LoadImageInvocation.ts | 11 ++-- .../api/models/MaskFromAlphaInvocation.ts | 5 ++ .../services/api/models/MultiplyInvocation.ts | 6 ++ .../services/api/models/NoiseInvocation.ts | 6 ++ .../services/api/models/ParamIntInvocation.ts | 6 ++ .../api/models/RandomIntInvocation.ts | 6 ++ .../api/models/RandomRangeInvocation.ts | 6 ++ .../services/api/models/RangeInvocation.ts | 6 ++ .../api/models/RangeOfSizeInvocation.ts | 6 ++ .../api/models/ResizeLatentsInvocation.ts | 5 ++ .../api/models/RestoreFaceInvocation.ts | 5 ++ .../api/models/ScaleLatentsInvocation.ts | 5 ++ .../api/models/ShowImageInvocation.ts | 5 ++ .../services/api/models/SubtractInvocation.ts | 6 ++ .../api/models/TextToImageInvocation.ts | 6 ++ .../api/models/TextToLatentsInvocation.ts | 13 ++--- .../services/api/models/UpscaleInvocation.ts | 5 ++ .../services/api/services/ImagesService.ts | 58 ++++++++++++++++++- .../services/api/services/SessionsService.ts | 17 +++--- 49 files changed, 427 insertions(+), 58 deletions(-) rename invokeai/frontend/web/src/services/api/models/{BlurInvocation.ts => ImageBlurInvocation.ts} (68%) create mode 100644 invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts create mode 100644 invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts rename invokeai/frontend/web/src/services/api/models/{CropImageInvocation.ts => ImageCropInvocation.ts} (77%) rename invokeai/frontend/web/src/services/api/models/{InverseLerpInvocation.ts => ImageInverseLerpInvocation.ts} (69%) rename invokeai/frontend/web/src/services/api/models/{LerpInvocation.ts => ImageLerpInvocation.ts} (69%) create mode 100644 invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts rename invokeai/frontend/web/src/services/api/models/{PasteImageInvocation.ts => ImagePasteInvocation.ts} (75%) create mode 100644 invokeai/frontend/web/src/services/api/models/ImageRecordChanges.ts create mode 100644 invokeai/frontend/web/src/services/api/models/InvocationMeta.ts diff --git a/invokeai/frontend/web/src/services/api/index.ts b/invokeai/frontend/web/src/services/api/index.ts index ecf8621ed6..b7cfb476a2 100644 --- a/invokeai/frontend/web/src/services/api/index.ts +++ b/invokeai/frontend/web/src/services/api/index.ts @@ -7,7 +7,6 @@ export { OpenAPI } from './core/OpenAPI'; export type { OpenAPIConfig } from './core/OpenAPI'; export type { AddInvocation } from './models/AddInvocation'; -export type { BlurInvocation } from './models/BlurInvocation'; export type { Body_upload_image } from './models/Body_upload_image'; export type { CkptModelInfo } from './models/CkptModelInfo'; export type { CollectInvocation } from './models/CollectInvocation'; @@ -17,7 +16,6 @@ export type { CompelInvocation } from './models/CompelInvocation'; export type { CompelOutput } from './models/CompelOutput'; export type { ConditioningField } from './models/ConditioningField'; export type { CreateModelRequest } from './models/CreateModelRequest'; -export type { CropImageInvocation } from './models/CropImageInvocation'; export type { CvInpaintInvocation } from './models/CvInpaintInvocation'; export type { DiffusersModelInfo } from './models/DiffusersModelInfo'; export type { DivideInvocation } from './models/DivideInvocation'; @@ -28,11 +26,20 @@ export type { GraphExecutionState } from './models/GraphExecutionState'; export type { GraphInvocation } from './models/GraphInvocation'; export type { GraphInvocationOutput } from './models/GraphInvocationOutput'; export type { HTTPValidationError } from './models/HTTPValidationError'; +export type { ImageBlurInvocation } from './models/ImageBlurInvocation'; export type { ImageCategory } from './models/ImageCategory'; +export type { ImageChannelInvocation } from './models/ImageChannelInvocation'; +export type { ImageConvertInvocation } from './models/ImageConvertInvocation'; +export type { ImageCropInvocation } from './models/ImageCropInvocation'; export type { ImageDTO } from './models/ImageDTO'; export type { ImageField } from './models/ImageField'; +export type { ImageInverseLerpInvocation } from './models/ImageInverseLerpInvocation'; +export type { ImageLerpInvocation } from './models/ImageLerpInvocation'; export type { ImageMetadata } from './models/ImageMetadata'; +export type { ImageMultiplyInvocation } from './models/ImageMultiplyInvocation'; export type { ImageOutput } from './models/ImageOutput'; +export type { ImagePasteInvocation } from './models/ImagePasteInvocation'; +export type { ImageRecordChanges } from './models/ImageRecordChanges'; export type { ImageToImageInvocation } from './models/ImageToImageInvocation'; export type { ImageToLatentsInvocation } from './models/ImageToLatentsInvocation'; export type { ImageType } from './models/ImageType'; @@ -43,14 +50,13 @@ export type { InfillTileInvocation } from './models/InfillTileInvocation'; export type { InpaintInvocation } from './models/InpaintInvocation'; export type { IntCollectionOutput } from './models/IntCollectionOutput'; export type { IntOutput } from './models/IntOutput'; -export type { InverseLerpInvocation } from './models/InverseLerpInvocation'; +export type { InvocationMeta } from './models/InvocationMeta'; export type { IterateInvocation } from './models/IterateInvocation'; export type { IterateInvocationOutput } from './models/IterateInvocationOutput'; export type { LatentsField } from './models/LatentsField'; export type { LatentsOutput } from './models/LatentsOutput'; export type { LatentsToImageInvocation } from './models/LatentsToImageInvocation'; export type { LatentsToLatentsInvocation } from './models/LatentsToLatentsInvocation'; -export type { LerpInvocation } from './models/LerpInvocation'; export type { LoadImageInvocation } from './models/LoadImageInvocation'; export type { MaskFromAlphaInvocation } from './models/MaskFromAlphaInvocation'; export type { MaskOutput } from './models/MaskOutput'; @@ -61,7 +67,6 @@ export type { NoiseOutput } from './models/NoiseOutput'; export type { PaginatedResults_GraphExecutionState_ } from './models/PaginatedResults_GraphExecutionState_'; export type { PaginatedResults_ImageDTO_ } from './models/PaginatedResults_ImageDTO_'; export type { ParamIntInvocation } from './models/ParamIntInvocation'; -export type { PasteImageInvocation } from './models/PasteImageInvocation'; export type { PromptOutput } from './models/PromptOutput'; export type { RandomIntInvocation } from './models/RandomIntInvocation'; export type { RandomRangeInvocation } from './models/RandomRangeInvocation'; diff --git a/invokeai/frontend/web/src/services/api/models/AddInvocation.ts b/invokeai/frontend/web/src/services/api/models/AddInvocation.ts index 1ff7b010c2..52898bfd0c 100644 --- a/invokeai/frontend/web/src/services/api/models/AddInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/AddInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Adds two numbers */ @@ -11,6 +13,10 @@ export type AddInvocation = { */ id: string; type?: 'add'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts b/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts index d250ae4450..fb6c7b40e5 100644 --- a/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Collects values into a collection */ @@ -11,6 +13,10 @@ export type CollectInvocation = { */ id: string; type?: 'collect'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The item to collect (all inputs must be of the same type) */ diff --git a/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts b/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts index f03d53a841..ec483a76d5 100644 --- a/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Parse prompt using compel package to conditioning. */ @@ -11,6 +13,10 @@ export type CompelInvocation = { */ id: string; type?: 'compel'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * Prompt */ diff --git a/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts b/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts index 19342acf8f..d8fafc02d4 100644 --- a/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Simple inpaint using opencv. @@ -13,6 +14,10 @@ export type CvInpaintInvocation = { */ id: string; type?: 'cv_inpaint'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to inpaint */ diff --git a/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts b/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts index 3cb262e9af..220f635580 100644 --- a/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Divides two numbers */ @@ -11,6 +13,10 @@ export type DivideInvocation = { */ id: string; type?: 'div'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/Graph.ts b/invokeai/frontend/web/src/services/api/models/Graph.ts index 039923e585..6be925841b 100644 --- a/invokeai/frontend/web/src/services/api/models/Graph.ts +++ b/invokeai/frontend/web/src/services/api/models/Graph.ts @@ -3,31 +3,34 @@ /* eslint-disable */ import type { AddInvocation } from './AddInvocation'; -import type { BlurInvocation } from './BlurInvocation'; import type { CollectInvocation } from './CollectInvocation'; import type { CompelInvocation } from './CompelInvocation'; -import type { CropImageInvocation } from './CropImageInvocation'; import type { CvInpaintInvocation } from './CvInpaintInvocation'; import type { DivideInvocation } from './DivideInvocation'; import type { Edge } from './Edge'; import type { GraphInvocation } from './GraphInvocation'; +import type { ImageBlurInvocation } from './ImageBlurInvocation'; +import type { ImageChannelInvocation } from './ImageChannelInvocation'; +import type { ImageConvertInvocation } from './ImageConvertInvocation'; +import type { ImageCropInvocation } from './ImageCropInvocation'; +import type { ImageInverseLerpInvocation } from './ImageInverseLerpInvocation'; +import type { ImageLerpInvocation } from './ImageLerpInvocation'; +import type { ImageMultiplyInvocation } from './ImageMultiplyInvocation'; +import type { ImagePasteInvocation } from './ImagePasteInvocation'; import type { ImageToImageInvocation } from './ImageToImageInvocation'; import type { ImageToLatentsInvocation } from './ImageToLatentsInvocation'; import type { InfillColorInvocation } from './InfillColorInvocation'; import type { InfillPatchMatchInvocation } from './InfillPatchMatchInvocation'; import type { InfillTileInvocation } from './InfillTileInvocation'; import type { InpaintInvocation } from './InpaintInvocation'; -import type { InverseLerpInvocation } from './InverseLerpInvocation'; import type { IterateInvocation } from './IterateInvocation'; import type { LatentsToImageInvocation } from './LatentsToImageInvocation'; import type { LatentsToLatentsInvocation } from './LatentsToLatentsInvocation'; -import type { LerpInvocation } from './LerpInvocation'; import type { LoadImageInvocation } from './LoadImageInvocation'; import type { MaskFromAlphaInvocation } from './MaskFromAlphaInvocation'; import type { MultiplyInvocation } from './MultiplyInvocation'; import type { NoiseInvocation } from './NoiseInvocation'; import type { ParamIntInvocation } from './ParamIntInvocation'; -import type { PasteImageInvocation } from './PasteImageInvocation'; import type { RandomIntInvocation } from './RandomIntInvocation'; import type { RandomRangeInvocation } from './RandomRangeInvocation'; import type { RangeInvocation } from './RangeInvocation'; @@ -49,7 +52,7 @@ export type Graph = { /** * The nodes in this graph */ - nodes?: Record; + nodes?: Record; /** * The connections between nodes and their fields in this graph */ diff --git a/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts b/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts index 5109a49a68..b631edce62 100644 --- a/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts @@ -3,10 +3,10 @@ /* eslint-disable */ import type { Graph } from './Graph'; +import type { InvocationMeta } from './InvocationMeta'; /** - * A node to process inputs and produce outputs. - * May use dependency injection in __init__ to receive providers. + * Execute a graph */ export type GraphInvocation = { /** @@ -14,6 +14,10 @@ export type GraphInvocation = { */ id: string; type?: 'graph'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The graph to run */ diff --git a/invokeai/frontend/web/src/services/api/models/BlurInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageBlurInvocation.ts similarity index 68% rename from invokeai/frontend/web/src/services/api/models/BlurInvocation.ts rename to invokeai/frontend/web/src/services/api/models/ImageBlurInvocation.ts index 0643e4b309..caa189c0ab 100644 --- a/invokeai/frontend/web/src/services/api/models/BlurInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageBlurInvocation.ts @@ -3,16 +3,21 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Blurs an image */ -export type BlurInvocation = { +export type ImageBlurInvocation = { /** * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'blur'; + type?: 'img_blur'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to blur */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageCategory.ts b/invokeai/frontend/web/src/services/api/models/ImageCategory.ts index c4edf90fd3..6b04a0b864 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageCategory.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageCategory.ts @@ -5,4 +5,4 @@ /** * The category of an image. Use ImageCategory.OTHER for non-default categories. */ -export type ImageCategory = 'general' | 'control' | 'other'; +export type ImageCategory = 'general' | 'control' | 'mask' | 'other'; diff --git a/invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts new file mode 100644 index 0000000000..0320b3d50a --- /dev/null +++ b/invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts @@ -0,0 +1,30 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; + +/** + * Gets a channel from an image. + */ +export type ImageChannelInvocation = { + /** + * The id of this node. Must be unique among all nodes. + */ + id: string; + type?: 'img_chan'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; + /** + * The image to get the channel from + */ + image?: ImageField; + /** + * The channel to get + */ + channel?: 'A' | 'R' | 'G' | 'B'; +}; + diff --git a/invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts new file mode 100644 index 0000000000..34f0365541 --- /dev/null +++ b/invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts @@ -0,0 +1,30 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; + +/** + * Converts an image to a different mode. + */ +export type ImageConvertInvocation = { + /** + * The id of this node. Must be unique among all nodes. + */ + id: string; + type?: 'img_conv'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; + /** + * The image to convert + */ + image?: ImageField; + /** + * The mode to convert to + */ + mode?: 'L' | 'RGB' | 'RGBA' | 'CMYK' | 'YCbCr' | 'LAB' | 'HSV' | 'I' | 'F'; +}; + diff --git a/invokeai/frontend/web/src/services/api/models/CropImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageCropInvocation.ts similarity index 77% rename from invokeai/frontend/web/src/services/api/models/CropImageInvocation.ts rename to invokeai/frontend/web/src/services/api/models/ImageCropInvocation.ts index 2676f5cb87..ad672f1772 100644 --- a/invokeai/frontend/web/src/services/api/models/CropImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageCropInvocation.ts @@ -3,16 +3,21 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Crops an image to a specified box. The box can be outside of the image. */ -export type CropImageInvocation = { +export type ImageCropInvocation = { /** * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'crop'; + type?: 'img_crop'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to crop */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageDTO.ts b/invokeai/frontend/web/src/services/api/models/ImageDTO.ts index c5377b4c76..bc2f19f1b5 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageDTO.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageDTO.ts @@ -50,6 +50,10 @@ export type ImageDTO = { * The deleted timestamp of the image. */ deleted_at?: string; + /** + * Whether this is an intermediate image. + */ + is_intermediate: boolean; /** * The session ID that generated this image, if it is a generated image. */ diff --git a/invokeai/frontend/web/src/services/api/models/InverseLerpInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageInverseLerpInvocation.ts similarity index 69% rename from invokeai/frontend/web/src/services/api/models/InverseLerpInvocation.ts rename to invokeai/frontend/web/src/services/api/models/ImageInverseLerpInvocation.ts index 33c59b7bac..be88fb5d54 100644 --- a/invokeai/frontend/web/src/services/api/models/InverseLerpInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageInverseLerpInvocation.ts @@ -3,16 +3,21 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Inverse linear interpolation of all pixels of an image */ -export type InverseLerpInvocation = { +export type ImageInverseLerpInvocation = { /** * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'ilerp'; + type?: 'img_ilerp'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to lerp */ diff --git a/invokeai/frontend/web/src/services/api/models/LerpInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageLerpInvocation.ts similarity index 69% rename from invokeai/frontend/web/src/services/api/models/LerpInvocation.ts rename to invokeai/frontend/web/src/services/api/models/ImageLerpInvocation.ts index f2406c2246..fcf1ea184c 100644 --- a/invokeai/frontend/web/src/services/api/models/LerpInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageLerpInvocation.ts @@ -3,16 +3,21 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Linear interpolation of all pixels of an image */ -export type LerpInvocation = { +export type ImageLerpInvocation = { /** * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'lerp'; + type?: 'img_lerp'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to lerp */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts new file mode 100644 index 0000000000..c1b76b3864 --- /dev/null +++ b/invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts @@ -0,0 +1,30 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; + +/** + * Multiplies two images together using `PIL.ImageChops.multiply()`. + */ +export type ImageMultiplyInvocation = { + /** + * The id of this node. Must be unique among all nodes. + */ + id: string; + type?: 'img_mul'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; + /** + * The first image to multiply + */ + image1?: ImageField; + /** + * The second image to multiply + */ + image2?: ImageField; +}; + diff --git a/invokeai/frontend/web/src/services/api/models/PasteImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImagePasteInvocation.ts similarity index 75% rename from invokeai/frontend/web/src/services/api/models/PasteImageInvocation.ts rename to invokeai/frontend/web/src/services/api/models/ImagePasteInvocation.ts index 8a181ccf07..3db0c33bf9 100644 --- a/invokeai/frontend/web/src/services/api/models/PasteImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImagePasteInvocation.ts @@ -3,16 +3,21 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Pastes an image into another image. */ -export type PasteImageInvocation = { +export type ImagePasteInvocation = { /** * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'paste'; + type?: 'img_paste'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The base image */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageRecordChanges.ts b/invokeai/frontend/web/src/services/api/models/ImageRecordChanges.ts new file mode 100644 index 0000000000..51f0ee2079 --- /dev/null +++ b/invokeai/frontend/web/src/services/api/models/ImageRecordChanges.ts @@ -0,0 +1,24 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { ImageCategory } from './ImageCategory'; + +/** + * A set of changes to apply to an image record. + * + * Only limited changes are valid: + * - `image_category`: change the category of an image + * - `session_id`: change the session associated with an image + */ +export type ImageRecordChanges = { + /** + * The image's new category. + */ + image_category?: ImageCategory; + /** + * The image's new session ID. + */ + session_id?: string; +}; + diff --git a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts index fb43c76921..809403e38c 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Generates an image using img2img. @@ -13,6 +14,10 @@ export type ImageToImageInvocation = { */ id: string; type?: 'img2img'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The prompt to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts index f72d446615..fc510a2b4c 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Encodes an image into latents. @@ -13,6 +14,10 @@ export type ImageToLatentsInvocation = { */ id: string; type?: 'i2l'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to encode */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageType.ts b/invokeai/frontend/web/src/services/api/models/ImageType.ts index bba9134e63..dfc10bf455 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageType.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageType.ts @@ -5,4 +5,4 @@ /** * The type of an image. */ -export type ImageType = 'results' | 'uploads' | 'intermediates'; +export type ImageType = 'results' | 'uploads'; diff --git a/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts index 157c976e11..dbfac9cd60 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts @@ -4,6 +4,7 @@ import type { ColorField } from './ColorField'; import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Infills transparent areas of an image with a solid color @@ -14,6 +15,10 @@ export type InfillColorInvocation = { */ id: string; type?: 'infill_rgba'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to infill */ diff --git a/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts index a4c18ade5d..49d8519e13 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Infills transparent areas of an image using the PatchMatch algorithm @@ -13,6 +14,10 @@ export type InfillPatchMatchInvocation = { */ id: string; type?: 'infill_patchmatch'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to infill */ diff --git a/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts index 12113f57f5..ce318b9c29 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Infills transparent areas of an image with tiles of the image @@ -13,6 +14,10 @@ export type InfillTileInvocation = { */ id: string; type?: 'infill_tile'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to infill */ diff --git a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts index 88ead9907c..c73d001c5b 100644 --- a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts @@ -4,6 +4,7 @@ import type { ColorField } from './ColorField'; import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Generates an image using inpaint. @@ -14,6 +15,10 @@ export type InpaintInvocation = { */ id: string; type?: 'inpaint'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The prompt to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/InvocationMeta.ts b/invokeai/frontend/web/src/services/api/models/InvocationMeta.ts new file mode 100644 index 0000000000..e2973c150a --- /dev/null +++ b/invokeai/frontend/web/src/services/api/models/InvocationMeta.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type InvocationMeta = { + /** + * Whether this is an intermediate node. Intermediate nodes are periodically deleted. + */ + is_intermediate?: boolean; +}; + diff --git a/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts b/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts index 0ff7a1258d..419caa15a4 100644 --- a/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts @@ -2,9 +2,10 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** - * A node to process inputs and produce outputs. - * May use dependency injection in __init__ to receive providers. + * Iterates over a list of items */ export type IterateInvocation = { /** @@ -12,6 +13,10 @@ export type IterateInvocation = { */ id: string; type?: 'iterate'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The list of items to iterate over */ diff --git a/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts index 8acd872e28..a01877cc40 100644 --- a/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts @@ -2,6 +2,7 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -13,6 +14,10 @@ export type LatentsToImageInvocation = { */ id: string; type?: 'l2i'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The latents to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts index 29995c6ad9..b441f51b3f 100644 --- a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ConditioningField } from './ConditioningField'; +import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -14,6 +15,10 @@ export type LatentsToLatentsInvocation = { */ id: string; type?: 'l2l'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * Positive conditioning for generation */ @@ -42,14 +47,6 @@ export type LatentsToLatentsInvocation = { * The model to use (currently ignored) */ model?: string; - /** - * Whether or not to generate an image that can tile without seams - */ - seamless?: boolean; - /** - * The axes to tile the image on, 'x' and/or 'y' - */ - seamless_axes?: string; /** * The latents to use as a base image */ diff --git a/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts index 745a9b44e4..c147597806 100644 --- a/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts @@ -2,7 +2,8 @@ /* tslint:disable */ /* eslint-disable */ -import type { ImageType } from './ImageType'; +import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Load an image and provide it as output. @@ -14,12 +15,12 @@ export type LoadImageInvocation = { id: string; type?: 'load_image'; /** - * The type of the image + * The meta properties of this node. */ - image_type: ImageType; + meta?: InvocationMeta; /** - * The name of the image + * The image to load */ - image_name: string; + image?: ImageField; }; diff --git a/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts b/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts index e71b1f464b..74aee00b2f 100644 --- a/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Extracts the alpha channel of an image as a mask. @@ -13,6 +14,10 @@ export type MaskFromAlphaInvocation = { */ id: string; type?: 'tomask'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to create the mask from */ diff --git a/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts b/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts index eede8f18d7..aa6cbf6fb4 100644 --- a/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Multiplies two numbers */ @@ -11,6 +13,10 @@ export type MultiplyInvocation = { */ id: string; type?: 'mul'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts b/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts index 59e50b76f3..67c77d024e 100644 --- a/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Generates latent noise. */ @@ -11,6 +13,10 @@ export type NoiseInvocation = { */ id: string; type?: 'noise'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The seed to use */ diff --git a/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts b/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts index 7047310a87..6dc597f183 100644 --- a/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * An integer parameter */ @@ -11,6 +13,10 @@ export type ParamIntInvocation = { */ id: string; type?: 'param_int'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The integer value */ diff --git a/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts b/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts index 0a5220c31d..1cccffe93e 100644 --- a/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Outputs a single random integer. */ @@ -11,6 +13,10 @@ export type RandomIntInvocation = { */ id: string; type?: 'rand_int'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The inclusive low value */ diff --git a/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts b/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts index c1f80042a6..fbf8455226 100644 --- a/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Creates a collection of random numbers */ @@ -11,6 +13,10 @@ export type RandomRangeInvocation = { */ id: string; type?: 'random_range'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The inclusive low value */ diff --git a/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts b/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts index 1c37ca7fe3..5173ceeb7f 100644 --- a/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Creates a range of numbers from start to stop with step */ @@ -11,6 +13,10 @@ export type RangeInvocation = { */ id: string; type?: 'range'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The start of the range */ diff --git a/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts b/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts index b918f17130..2a7d5bae2a 100644 --- a/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Creates a range from start to start + size with step */ @@ -11,6 +13,10 @@ export type RangeOfSizeInvocation = { */ id: string; type?: 'range_of_size'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The start of the range */ diff --git a/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts index c0fabb4984..e0cbb5c0c7 100644 --- a/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts @@ -2,6 +2,7 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -13,6 +14,10 @@ export type ResizeLatentsInvocation = { */ id: string; type?: 'lresize'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The latents to resize */ diff --git a/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts b/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts index e03ed01c81..01de107ab9 100644 --- a/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Restores faces in an image. @@ -13,6 +14,10 @@ export type RestoreFaceInvocation = { */ id: string; type?: 'restore_face'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The input image */ diff --git a/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts index f398eaf408..08cd8de900 100644 --- a/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts @@ -2,6 +2,7 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -13,6 +14,10 @@ export type ScaleLatentsInvocation = { */ id: string; type?: 'lscale'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The latents to scale */ diff --git a/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts index 145895ad75..6934eeaa90 100644 --- a/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Displays a provided image, and passes it forward in the pipeline. @@ -13,6 +14,10 @@ export type ShowImageInvocation = { */ id: string; type?: 'show_image'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The image to show */ diff --git a/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts b/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts index 6f2da116a2..8ef305183c 100644 --- a/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Subtracts two numbers */ @@ -11,6 +13,10 @@ export type SubtractInvocation = { */ id: string; type?: 'sub'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts index 184e35693b..56dfd076f9 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts @@ -2,6 +2,8 @@ /* tslint:disable */ /* eslint-disable */ +import type { InvocationMeta } from './InvocationMeta'; + /** * Generates an image using text2img. */ @@ -11,6 +13,10 @@ export type TextToImageInvocation = { */ id: string; type?: 'txt2img'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The prompt to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts index d1ec5ed08c..865360d39b 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ConditioningField } from './ConditioningField'; +import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -14,6 +15,10 @@ export type TextToLatentsInvocation = { */ id: string; type?: 't2l'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * Positive conditioning for generation */ @@ -42,13 +47,5 @@ export type TextToLatentsInvocation = { * The model to use (currently ignored) */ model?: string; - /** - * Whether or not to generate an image that can tile without seams - */ - seamless?: boolean; - /** - * The axes to tile the image on, 'x' and/or 'y' - */ - seamless_axes?: string; }; diff --git a/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts b/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts index 8416c2454d..fe34b74678 100644 --- a/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; +import type { InvocationMeta } from './InvocationMeta'; /** * Upscales an image. @@ -13,6 +14,10 @@ export type UpscaleInvocation = { */ id: string; type?: 'upscale'; + /** + * The meta properties of this node. + */ + meta?: InvocationMeta; /** * The input image */ diff --git a/invokeai/frontend/web/src/services/api/services/ImagesService.ts b/invokeai/frontend/web/src/services/api/services/ImagesService.ts index 13b2ef836a..d01a97a45e 100644 --- a/invokeai/frontend/web/src/services/api/services/ImagesService.ts +++ b/invokeai/frontend/web/src/services/api/services/ImagesService.ts @@ -4,6 +4,7 @@ import type { Body_upload_image } from '../models/Body_upload_image'; import type { ImageCategory } from '../models/ImageCategory'; import type { ImageDTO } from '../models/ImageDTO'; +import type { ImageRecordChanges } from '../models/ImageRecordChanges'; import type { ImageType } from '../models/ImageType'; import type { ImageUrlsDTO } from '../models/ImageUrlsDTO'; import type { PaginatedResults_ImageDTO_ } from '../models/PaginatedResults_ImageDTO_'; @@ -65,20 +66,32 @@ export class ImagesService { * @throws ApiError */ public static uploadImage({ - imageType, formData, imageCategory, + isIntermediate = false, + sessionId, }: { - imageType: ImageType, formData: Body_upload_image, + /** + * The category of the image + */ imageCategory?: ImageCategory, + /** + * Whether this is an intermediate image + */ + isIntermediate?: boolean, + /** + * The session ID associated with this upload, if any + */ + sessionId?: string, }): CancelablePromise { return __request(OpenAPI, { method: 'POST', url: '/api/v1/images/', query: { - 'image_type': imageType, 'image_category': imageCategory, + 'is_intermediate': isIntermediate, + 'session_id': sessionId, }, formData: formData, mediaType: 'multipart/form-data', @@ -132,6 +145,9 @@ export class ImagesService { imageType, imageName, }: { + /** + * The type of image to delete + */ imageType: ImageType, /** * The name of the image to delete @@ -151,6 +167,42 @@ export class ImagesService { }); } + /** + * Update Image + * Updates an image + * @returns ImageDTO Successful Response + * @throws ApiError + */ + public static updateImage({ + imageType, + imageName, + requestBody, + }: { + /** + * The type of image to update + */ + imageType: ImageType, + /** + * The name of the image to update + */ + imageName: string, + requestBody: ImageRecordChanges, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'PATCH', + url: '/api/v1/images/{image_type}/{image_name}', + path: { + 'image_type': imageType, + 'image_name': imageName, + }, + body: requestBody, + mediaType: 'application/json', + errors: { + 422: `Validation Error`, + }, + }); + } + /** * Get Image Metadata * Gets an image's metadata diff --git a/invokeai/frontend/web/src/services/api/services/SessionsService.ts b/invokeai/frontend/web/src/services/api/services/SessionsService.ts index 23597c9e9e..1c55d36502 100644 --- a/invokeai/frontend/web/src/services/api/services/SessionsService.ts +++ b/invokeai/frontend/web/src/services/api/services/SessionsService.ts @@ -2,34 +2,37 @@ /* tslint:disable */ /* eslint-disable */ import type { AddInvocation } from '../models/AddInvocation'; -import type { BlurInvocation } from '../models/BlurInvocation'; import type { CollectInvocation } from '../models/CollectInvocation'; import type { CompelInvocation } from '../models/CompelInvocation'; -import type { CropImageInvocation } from '../models/CropImageInvocation'; import type { CvInpaintInvocation } from '../models/CvInpaintInvocation'; import type { DivideInvocation } from '../models/DivideInvocation'; import type { Edge } from '../models/Edge'; import type { Graph } from '../models/Graph'; import type { GraphExecutionState } from '../models/GraphExecutionState'; import type { GraphInvocation } from '../models/GraphInvocation'; +import type { ImageBlurInvocation } from '../models/ImageBlurInvocation'; +import type { ImageChannelInvocation } from '../models/ImageChannelInvocation'; +import type { ImageConvertInvocation } from '../models/ImageConvertInvocation'; +import type { ImageCropInvocation } from '../models/ImageCropInvocation'; +import type { ImageInverseLerpInvocation } from '../models/ImageInverseLerpInvocation'; +import type { ImageLerpInvocation } from '../models/ImageLerpInvocation'; +import type { ImageMultiplyInvocation } from '../models/ImageMultiplyInvocation'; +import type { ImagePasteInvocation } from '../models/ImagePasteInvocation'; import type { ImageToImageInvocation } from '../models/ImageToImageInvocation'; import type { ImageToLatentsInvocation } from '../models/ImageToLatentsInvocation'; import type { InfillColorInvocation } from '../models/InfillColorInvocation'; import type { InfillPatchMatchInvocation } from '../models/InfillPatchMatchInvocation'; import type { InfillTileInvocation } from '../models/InfillTileInvocation'; import type { InpaintInvocation } from '../models/InpaintInvocation'; -import type { InverseLerpInvocation } from '../models/InverseLerpInvocation'; import type { IterateInvocation } from '../models/IterateInvocation'; import type { LatentsToImageInvocation } from '../models/LatentsToImageInvocation'; import type { LatentsToLatentsInvocation } from '../models/LatentsToLatentsInvocation'; -import type { LerpInvocation } from '../models/LerpInvocation'; import type { LoadImageInvocation } from '../models/LoadImageInvocation'; import type { MaskFromAlphaInvocation } from '../models/MaskFromAlphaInvocation'; import type { MultiplyInvocation } from '../models/MultiplyInvocation'; import type { NoiseInvocation } from '../models/NoiseInvocation'; import type { PaginatedResults_GraphExecutionState_ } from '../models/PaginatedResults_GraphExecutionState_'; import type { ParamIntInvocation } from '../models/ParamIntInvocation'; -import type { PasteImageInvocation } from '../models/PasteImageInvocation'; import type { RandomIntInvocation } from '../models/RandomIntInvocation'; import type { RandomRangeInvocation } from '../models/RandomRangeInvocation'; import type { RangeInvocation } from '../models/RangeInvocation'; @@ -151,7 +154,7 @@ export class SessionsService { * The id of the session */ sessionId: string, - requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), + requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), }): CancelablePromise { return __request(OpenAPI, { method: 'POST', @@ -188,7 +191,7 @@ export class SessionsService { * The path to the node in the graph */ nodePath: string, - requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), + requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), }): CancelablePromise { return __request(OpenAPI, { method: 'PUT', From a2de5c9963483ce87ca5aad69c6ecc21cbf6673d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 May 2023 23:47:57 +1000 Subject: [PATCH 09/31] feat(ui): change intermediates handling - Update the canvas graph generation to flag its uploaded init and mask images as `intermediate`. - During canvas setup, hit the update route to associate the uploaded images with the session id. - Organize the socketio and RTK listener middlware better. Needed to facilitate the updated canvas logic. - Add a new action `sessionReadyToInvoke`. The `sessionInvoked` action is *only* ever run in response to this event. This lets us do whatever complicated setup (eg canvas) and explicitly invoking. Previously, invoking was tied to the socket subscribe events. - Some minor tidying. --- .../middleware/listenerMiddleware/index.ts | 25 ++- .../listeners/imageUploaded.ts | 2 +- .../listeners/sessionReadyToInvoke.ts | 19 ++ .../listeners/socketio/generatorProgress.ts | 28 +++ .../socketio/graphExecutionStateComplete.ts | 17 ++ .../{ => socketio}/invocationComplete.ts | 41 +++-- .../listeners/socketio/invocationError.ts | 17 ++ .../listeners/socketio/invocationStarted.ts | 28 +++ .../listeners/socketio/socketConnected.ts | 43 +++++ .../listeners/socketio/socketDisconnected.ts | 14 ++ .../listeners/socketio/socketSubscribed.ts | 17 ++ .../listeners/socketio/socketUnsubscribed.ts | 17 ++ .../listeners/userInvokedCanvas.ts | 172 +++++++++++------- .../listeners/userInvokedImageToImage.ts | 7 +- .../listeners/userInvokedNodes.ts | 7 +- .../listeners/userInvokedTextToImage.ts | 9 +- invokeai/frontend/web/src/app/store/store.ts | 2 + .../src/features/nodes/util/parseSchema.ts | 6 +- .../web/src/features/system/store/actions.ts | 3 + .../src/features/system/store/sessionSlice.ts | 62 +++++++ .../web/src/services/events/middleware.ts | 22 +-- .../services/events/util/setEventListeners.ts | 85 +-------- .../web/src/services/thunks/gallery.ts | 10 +- .../frontend/web/src/services/thunks/image.ts | 16 ++ .../web/src/services/thunks/session.ts | 45 +++++ 25 files changed, 529 insertions(+), 185 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/generatorProgress.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts rename invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/{ => socketio}/invocationComplete.ts (59%) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketConnected.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketDisconnected.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketSubscribed.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketUnsubscribed.ts create mode 100644 invokeai/frontend/web/src/features/system/store/actions.ts create mode 100644 invokeai/frontend/web/src/features/system/store/sessionSlice.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index f23e83a191..c04a3943f3 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -8,7 +8,6 @@ import type { TypedStartListening, TypedAddListener } from '@reduxjs/toolkit'; import type { RootState, AppDispatch } from '../../store'; import { addInitialImageSelectedListener } from './listeners/initialImageSelected'; -import { addImageResultReceivedListener } from './listeners/invocationComplete'; import { addImageUploadedListener } from './listeners/imageUploaded'; import { addRequestedImageDeletionListener } from './listeners/imageDeleted'; import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas'; @@ -19,6 +18,16 @@ import { addCanvasSavedToGalleryListener } from './listeners/canvasSavedToGaller import { addCanvasDownloadedAsImageListener } from './listeners/canvasDownloadedAsImage'; import { addCanvasCopiedToClipboardListener } from './listeners/canvasCopiedToClipboard'; import { addCanvasMergedListener } from './listeners/canvasMerged'; +import { addGeneratorProgressListener } from './listeners/socketio/generatorProgress'; +import { addGraphExecutionStateCompleteListener } from './listeners/socketio/graphExecutionStateComplete'; +import { addInvocationCompleteListener } from './listeners/socketio/invocationComplete'; +import { addInvocationErrorListener } from './listeners/socketio/invocationError'; +import { addInvocationStartedListener } from './listeners/socketio/invocationStarted'; +import { addSocketConnectedListener } from './listeners/socketio/socketConnected'; +import { addSocketDisconnectedListener } from './listeners/socketio/socketDisconnected'; +import { addSocketSubscribedListener } from './listeners/socketio/socketSubscribed'; +import { addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed'; +import { addSessionReadyToInvokeListener } from './listeners/sessionReadyToInvoke'; export const listenerMiddleware = createListenerMiddleware(); @@ -40,15 +49,27 @@ export type AppListenerEffect = ListenerEffect< addImageUploadedListener(); addInitialImageSelectedListener(); -addImageResultReceivedListener(); addRequestedImageDeletionListener(); addUserInvokedCanvasListener(); addUserInvokedNodesListener(); addUserInvokedTextToImageListener(); addUserInvokedImageToImageListener(); +addSessionReadyToInvokeListener(); addCanvasSavedToGalleryListener(); addCanvasDownloadedAsImageListener(); addCanvasCopiedToClipboardListener(); addCanvasMergedListener(); + +// socketio + +addGeneratorProgressListener(); +addGraphExecutionStateCompleteListener(); +addInvocationCompleteListener(); +addInvocationErrorListener(); +addInvocationStartedListener(); +addSocketConnectedListener(); +addSocketDisconnectedListener(); +addSocketSubscribedListener(); +addSocketUnsubscribedListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index 1d66166c12..b37cd3d139 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -12,7 +12,7 @@ export const addImageUploadedListener = () => { startAppListening({ predicate: (action): action is ReturnType => imageUploaded.fulfilled.match(action) && - action.payload.response.image_type !== 'intermediates', + action.payload.response.is_intermediate === false, effect: (action, { dispatch, getState }) => { const { response: image } = action.payload; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts new file mode 100644 index 0000000000..eb65017a25 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts @@ -0,0 +1,19 @@ +import { startAppListening } from '..'; +import { sessionInvoked } from 'services/thunks/session'; +import { log } from 'app/logging/useLogger'; +import { sessionReadyToInvoke } from 'features/system/store/actions'; + +const moduleLog = log.child({ namespace: 'invoke' }); + +export const addSessionReadyToInvokeListener = () => { + startAppListening({ + actionCreator: sessionReadyToInvoke, + effect: (action, { getState, dispatch }) => { + const { sessionId } = getState().system; + if (sessionId) { + moduleLog.info({ sessionId }, `Session invoked (${sessionId})})`); + dispatch(sessionInvoked({ sessionId })); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/generatorProgress.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/generatorProgress.ts new file mode 100644 index 0000000000..341b5e46d3 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/generatorProgress.ts @@ -0,0 +1,28 @@ +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; +import { generatorProgress } from 'services/events/actions'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addGeneratorProgressListener = () => { + startAppListening({ + actionCreator: generatorProgress, + effect: (action, { dispatch, getState }) => { + if ( + getState().system.canceledSession === + action.payload.data.graph_execution_state_id + ) { + moduleLog.trace( + action.payload, + 'Ignored generator progress for canceled session' + ); + return; + } + + moduleLog.trace( + action.payload, + `Generator progress (${action.payload.data.node.type})` + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts new file mode 100644 index 0000000000..c8ac46f6f1 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts @@ -0,0 +1,17 @@ +import { log } from 'app/logging/useLogger'; +import { graphExecutionStateComplete } from 'services/events/actions'; +import { startAppListening } from '../..'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addGraphExecutionStateCompleteListener = () => { + startAppListening({ + actionCreator: graphExecutionStateComplete, + effect: (action, { dispatch, getState }) => { + moduleLog.debug( + action.payload, + `Graph execution state complete (${action.payload.data.graph_execution_state_id})` + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/invocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts similarity index 59% rename from invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/invocationComplete.ts rename to invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts index 0222eea93c..76ae46c4a2 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/invocationComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts @@ -1,40 +1,49 @@ +import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; import { invocationComplete } from 'services/events/actions'; -import { isImageOutput } from 'services/types/guards'; import { imageMetadataReceived, imageUrlsReceived, } from 'services/thunks/image'; -import { startAppListening } from '..'; -import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; +import { sessionCanceled } from 'services/thunks/session'; +import { isImageOutput } from 'services/types/guards'; +const moduleLog = log.child({ namespace: 'socketio' }); const nodeDenylist = ['dataURL_image']; -export const addImageResultReceivedListener = () => { +export const addInvocationCompleteListener = () => { startAppListening({ - predicate: (action) => { - if ( - invocationComplete.match(action) && - isImageOutput(action.payload.data.result) - ) { - return true; - } - return false; - }, - effect: async (action, { getState, dispatch, take }) => { - if (!invocationComplete.match(action)) { - return; + actionCreator: invocationComplete, + effect: async (action, { dispatch, getState, take }) => { + moduleLog.info( + action.payload, + `Invocation complete (${action.payload.data.node.type})` + ); + + const sessionId = action.payload.data.graph_execution_state_id; + + const { cancelType, isCancelScheduled } = getState().system; + + // Handle scheduled cancelation + if (cancelType === 'scheduled' && isCancelScheduled) { + dispatch(sessionCanceled({ sessionId })); } const { data } = action.payload; const { result, node, graph_execution_state_id } = data; + // This complete event has an associated image output if (isImageOutput(result) && !nodeDenylist.includes(node.type)) { const { image_name, image_type } = result.image; + // Get its URLS + // TODO: is this extraneous? I think so... dispatch( imageUrlsReceived({ imageName: image_name, imageType: image_type }) ); + // Get its metadata dispatch( imageMetadataReceived({ imageName: image_name, diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts new file mode 100644 index 0000000000..d0e4d975be --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts @@ -0,0 +1,17 @@ +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; +import { invocationError } from 'services/events/actions'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addInvocationErrorListener = () => { + startAppListening({ + actionCreator: invocationError, + effect: (action, { dispatch, getState }) => { + moduleLog.debug( + action.payload, + `Invocation error (${action.payload.data.node.type})` + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts new file mode 100644 index 0000000000..373802fa16 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts @@ -0,0 +1,28 @@ +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; +import { invocationStarted } from 'services/events/actions'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addInvocationStartedListener = () => { + startAppListening({ + actionCreator: invocationStarted, + effect: (action, { dispatch, getState }) => { + if ( + getState().system.canceledSession === + action.payload.data.graph_execution_state_id + ) { + moduleLog.trace( + action.payload, + 'Ignored invocation started for canceled session' + ); + return; + } + + moduleLog.info( + action.payload, + `Invocation started (${action.payload.data.node.type})` + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketConnected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketConnected.ts new file mode 100644 index 0000000000..bc9ecbec1e --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketConnected.ts @@ -0,0 +1,43 @@ +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; +import { socketConnected } from 'services/events/actions'; +import { + receivedResultImagesPage, + receivedUploadImagesPage, +} from 'services/thunks/gallery'; +import { receivedModels } from 'services/thunks/model'; +import { receivedOpenAPISchema } from 'services/thunks/schema'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addSocketConnectedListener = () => { + startAppListening({ + actionCreator: socketConnected, + effect: (action, { dispatch, getState }) => { + const { timestamp } = action.payload; + + moduleLog.debug({ timestamp }, 'Connected'); + + const { results, uploads, models, nodes, config } = getState(); + + const { disabledTabs } = config; + + // These thunks need to be dispatch in middleware; cannot handle in a reducer + if (!results.ids.length) { + dispatch(receivedResultImagesPage()); + } + + if (!uploads.ids.length) { + dispatch(receivedUploadImagesPage()); + } + + if (!models.ids.length) { + dispatch(receivedModels()); + } + + if (!nodes.schema && !disabledTabs.includes('nodes')) { + dispatch(receivedOpenAPISchema()); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketDisconnected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketDisconnected.ts new file mode 100644 index 0000000000..131c3ba18f --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketDisconnected.ts @@ -0,0 +1,14 @@ +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; +import { socketDisconnected } from 'services/events/actions'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addSocketDisconnectedListener = () => { + startAppListening({ + actionCreator: socketDisconnected, + effect: (action, { dispatch, getState }) => { + moduleLog.debug(action.payload, 'Disconnected'); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketSubscribed.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketSubscribed.ts new file mode 100644 index 0000000000..400f8a1689 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketSubscribed.ts @@ -0,0 +1,17 @@ +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; +import { socketSubscribed } from 'services/events/actions'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addSocketSubscribedListener = () => { + startAppListening({ + actionCreator: socketSubscribed, + effect: (action, { dispatch, getState }) => { + moduleLog.debug( + action.payload, + `Subscribed (${action.payload.sessionId}))` + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketUnsubscribed.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketUnsubscribed.ts new file mode 100644 index 0000000000..af15c55d42 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketUnsubscribed.ts @@ -0,0 +1,17 @@ +import { startAppListening } from '../..'; +import { log } from 'app/logging/useLogger'; +import { socketUnsubscribed } from 'services/events/actions'; + +const moduleLog = log.child({ namespace: 'socketio' }); + +export const addSocketUnsubscribedListener = () => { + startAppListening({ + actionCreator: socketUnsubscribed, + effect: (action, { dispatch, getState }) => { + moduleLog.debug( + action.payload, + `Unsubscribed (${action.payload.sessionId})` + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts index 2ebd3684e9..b90197f7cb 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts @@ -1,9 +1,9 @@ import { startAppListening } from '..'; -import { sessionCreated, sessionInvoked } from 'services/thunks/session'; +import { nodeUpdated, sessionCreated } from 'services/thunks/session'; import { buildCanvasGraphComponents } from 'features/nodes/util/graphBuilders/buildCanvasGraph'; import { log } from 'app/logging/useLogger'; import { canvasGraphBuilt } from 'features/nodes/store/actions'; -import { imageUploaded } from 'services/thunks/image'; +import { imageUpdated, imageUploaded } from 'services/thunks/image'; import { v4 as uuidv4 } from 'uuid'; import { Graph } from 'services/api'; import { @@ -15,12 +15,25 @@ import { getCanvasData } from 'features/canvas/util/getCanvasData'; import { getCanvasGenerationMode } from 'features/canvas/util/getCanvasGenerationMode'; import { blobToDataURL } from 'features/canvas/util/blobToDataURL'; import openBase64ImageInTab from 'common/util/openBase64ImageInTab'; +import { sessionReadyToInvoke } from 'features/system/store/actions'; const moduleLog = log.child({ namespace: 'invoke' }); /** - * This listener is responsible for building the canvas graph and blobs when the user invokes the canvas. - * It is also responsible for uploading the base and mask layers to the server. + * This listener is responsible invoking the canvas. This involved a number of steps: + * + * 1. Generate image blobs from the canvas layers + * 2. Determine the generation mode from the layers (txt2img, img2img, inpaint) + * 3. Build the canvas graph + * 4. Create the session + * 5. Upload the init image if necessary, then update the graph to refer to it (needs a separate request) + * 6. Upload the mask image if necessary, then update the graph to refer to it (needs a separate request) + * 7. Initialize the staging area if not yet initialized + * 8. Finally, dispatch the sessionReadyToInvoke action to invoke the session + * + * We have to do the uploads after creating the session: + * - We need to associate these particular uploads to a session, and flag them as intermediates + * - To do this, we need to associa */ export const addUserInvokedCanvasListener = () => { startAppListening({ @@ -70,63 +83,7 @@ export const addUserInvokedCanvasListener = () => { const { rangeNode, iterateNode, baseNode, edges } = graphComponents; - // Upload the base layer, to be used as init image - const baseFilename = `${uuidv4()}.png`; - - dispatch( - imageUploaded({ - imageType: 'intermediates', - formData: { - file: new File([baseBlob], baseFilename, { type: 'image/png' }), - }, - }) - ); - - if (baseNode.type === 'img2img' || baseNode.type === 'inpaint') { - const [{ payload: basePayload }] = await take( - (action): action is ReturnType => - imageUploaded.fulfilled.match(action) && - action.meta.arg.formData.file.name === baseFilename - ); - - const { image_name: baseName, image_type: baseType } = - basePayload.response; - - baseNode.image = { - image_name: baseName, - image_type: baseType, - }; - } - - // Upload the mask layer image - const maskFilename = `${uuidv4()}.png`; - - if (baseNode.type === 'inpaint') { - dispatch( - imageUploaded({ - imageType: 'intermediates', - formData: { - file: new File([maskBlob], maskFilename, { type: 'image/png' }), - }, - }) - ); - - const [{ payload: maskPayload }] = await take( - (action): action is ReturnType => - imageUploaded.fulfilled.match(action) && - action.meta.arg.formData.file.name === maskFilename - ); - - const { image_name: maskName, image_type: maskType } = - maskPayload.response; - - baseNode.mask = { - image_name: maskName, - image_type: maskType, - }; - } - - // Assemble! + // Assemble! Note that this graph *does not have the init or mask image set yet!* const nodes: Graph['nodes'] = { [rangeNode.id]: rangeNode, [iterateNode.id]: iterateNode, @@ -136,15 +93,96 @@ export const addUserInvokedCanvasListener = () => { const graph = { nodes, edges }; dispatch(canvasGraphBuilt(graph)); + moduleLog({ data: graph }, 'Canvas graph built'); - // Actually create the session + // If we are generating img2img or inpaint, we need to upload the init images + if (baseNode.type === 'img2img' || baseNode.type === 'inpaint') { + const baseFilename = `${uuidv4()}.png`; + dispatch( + imageUploaded({ + formData: { + file: new File([baseBlob], baseFilename, { type: 'image/png' }), + }, + isIntermediate: true, + }) + ); + + // Wait for the image to be uploaded + const [{ payload: basePayload }] = await take( + (action): action is ReturnType => + imageUploaded.fulfilled.match(action) && + action.meta.arg.formData.file.name === baseFilename + ); + + // Update the base node with the image name and type + const { image_name: baseName, image_type: baseType } = + basePayload.response; + + baseNode.image = { + image_name: baseName, + image_type: baseType, + }; + } + + // For inpaint, we also need to upload the mask layer + if (baseNode.type === 'inpaint') { + const maskFilename = `${uuidv4()}.png`; + dispatch( + imageUploaded({ + formData: { + file: new File([maskBlob], maskFilename, { type: 'image/png' }), + }, + isIntermediate: true, + }) + ); + + // Wait for the mask to be uploaded + const [{ payload: maskPayload }] = await take( + (action): action is ReturnType => + imageUploaded.fulfilled.match(action) && + action.meta.arg.formData.file.name === maskFilename + ); + + // Update the base node with the image name and type + const { image_name: maskName, image_type: maskType } = + maskPayload.response; + + baseNode.mask = { + image_name: maskName, + image_type: maskType, + }; + } + + // Create the session and wait for response dispatch(sessionCreated({ graph })); + const [sessionCreatedAction] = await take(sessionCreated.fulfilled.match); + const sessionId = sessionCreatedAction.payload.id; - // Wait for the session to be invoked (this is just the HTTP request to start processing) - const [{ meta }] = await take(sessionInvoked.fulfilled.match); + // Associate the init image with the session, now that we have the session ID + if ( + (baseNode.type === 'img2img' || baseNode.type === 'inpaint') && + baseNode.image + ) { + dispatch( + imageUpdated({ + imageName: baseNode.image.image_name, + imageType: baseNode.image.image_type, + requestBody: { session_id: sessionId }, + }) + ); + } - const { sessionId } = meta.arg; + // Associate the mask image with the session, now that we have the session ID + if (baseNode.type === 'inpaint' && baseNode.mask) { + dispatch( + imageUpdated({ + imageName: baseNode.mask.image_name, + imageType: baseNode.mask.image_type, + requestBody: { session_id: sessionId }, + }) + ); + } if (!state.canvas.layerState.stagingArea.boundingBox) { dispatch( @@ -158,7 +196,11 @@ export const addUserInvokedCanvasListener = () => { ); } + // Flag the session with the canvas session ID dispatch(canvasSessionIdChanged(sessionId)); + + // We are ready to invoke the session! + dispatch(sessionReadyToInvoke()); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts index e747aefa08..8940237782 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts @@ -4,6 +4,7 @@ import { sessionCreated } from 'services/thunks/session'; import { log } from 'app/logging/useLogger'; import { imageToImageGraphBuilt } from 'features/nodes/store/actions'; import { userInvoked } from 'app/store/actions'; +import { sessionReadyToInvoke } from 'features/system/store/actions'; const moduleLog = log.child({ namespace: 'invoke' }); @@ -11,7 +12,7 @@ export const addUserInvokedImageToImageListener = () => { startAppListening({ predicate: (action): action is ReturnType => userInvoked.match(action) && action.payload === 'img2img', - effect: (action, { getState, dispatch }) => { + effect: async (action, { getState, dispatch, take }) => { const state = getState(); const graph = buildImageToImageGraph(state); @@ -19,6 +20,10 @@ export const addUserInvokedImageToImageListener = () => { moduleLog({ data: graph }, 'Image to Image graph built'); dispatch(sessionCreated({ graph })); + + await take(sessionCreated.fulfilled.match); + + dispatch(sessionReadyToInvoke()); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts index 01e532d5ff..45dcf7b0b2 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts @@ -4,6 +4,7 @@ import { buildNodesGraph } from 'features/nodes/util/graphBuilders/buildNodesGra import { log } from 'app/logging/useLogger'; import { nodesGraphBuilt } from 'features/nodes/store/actions'; import { userInvoked } from 'app/store/actions'; +import { sessionReadyToInvoke } from 'features/system/store/actions'; const moduleLog = log.child({ namespace: 'invoke' }); @@ -11,7 +12,7 @@ export const addUserInvokedNodesListener = () => { startAppListening({ predicate: (action): action is ReturnType => userInvoked.match(action) && action.payload === 'nodes', - effect: (action, { getState, dispatch }) => { + effect: async (action, { getState, dispatch, take }) => { const state = getState(); const graph = buildNodesGraph(state); @@ -19,6 +20,10 @@ export const addUserInvokedNodesListener = () => { moduleLog({ data: graph }, 'Nodes graph built'); dispatch(sessionCreated({ graph })); + + await take(sessionCreated.fulfilled.match); + + dispatch(sessionReadyToInvoke()); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts index e3eb5d0b38..f7245b9301 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts @@ -4,6 +4,7 @@ import { sessionCreated } from 'services/thunks/session'; import { log } from 'app/logging/useLogger'; import { textToImageGraphBuilt } from 'features/nodes/store/actions'; import { userInvoked } from 'app/store/actions'; +import { sessionReadyToInvoke } from 'features/system/store/actions'; const moduleLog = log.child({ namespace: 'invoke' }); @@ -11,14 +12,20 @@ export const addUserInvokedTextToImageListener = () => { startAppListening({ predicate: (action): action is ReturnType => userInvoked.match(action) && action.payload === 'txt2img', - effect: (action, { getState, dispatch }) => { + effect: async (action, { getState, dispatch, take }) => { const state = getState(); const graph = buildTextToImageGraph(state); + dispatch(textToImageGraphBuilt(graph)); + moduleLog({ data: graph }, 'Text to Image graph built'); dispatch(sessionCreated({ graph })); + + await take(sessionCreated.fulfilled.match); + + dispatch(sessionReadyToInvoke()); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index b89615b2c0..4e9c154f3a 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -16,6 +16,7 @@ import lightboxReducer from 'features/lightbox/store/lightboxSlice'; import generationReducer from 'features/parameters/store/generationSlice'; import postprocessingReducer from 'features/parameters/store/postprocessingSlice'; import systemReducer from 'features/system/store/systemSlice'; +// import sessionReducer from 'features/system/store/sessionSlice'; import configReducer from 'features/system/store/configSlice'; import uiReducer from 'features/ui/store/uiSlice'; import hotkeysReducer from 'features/ui/store/hotkeysSlice'; @@ -46,6 +47,7 @@ const allReducers = { ui: uiReducer, uploads: uploadsReducer, hotkeys: hotkeysReducer, + // session: sessionReducer, }; const rootReducer = combineReducers(allReducers); diff --git a/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts b/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts index ddd19b8749..631552414d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts +++ b/invokeai/frontend/web/src/features/nodes/util/parseSchema.ts @@ -13,7 +13,9 @@ import { buildOutputFieldTemplates, } from './fieldTemplateBuilders'; -const invocationDenylist = ['Graph']; +const RESERVED_FIELD_NAMES = ['id', 'type', 'meta']; + +const invocationDenylist = ['Graph', 'InvocationMeta']; export const parseSchema = (openAPI: OpenAPIV3.Document) => { // filter out non-invocation schemas, plus some tricky invocations for now @@ -73,7 +75,7 @@ export const parseSchema = (openAPI: OpenAPIV3.Document) => { (inputsAccumulator, property, propertyName) => { if ( // `type` and `id` are not valid inputs/outputs - !['type', 'id'].includes(propertyName) && + !RESERVED_FIELD_NAMES.includes(propertyName) && isSchemaObject(property) ) { const field: InputFieldTemplate | undefined = diff --git a/invokeai/frontend/web/src/features/system/store/actions.ts b/invokeai/frontend/web/src/features/system/store/actions.ts new file mode 100644 index 0000000000..66181bc803 --- /dev/null +++ b/invokeai/frontend/web/src/features/system/store/actions.ts @@ -0,0 +1,3 @@ +import { createAction } from '@reduxjs/toolkit'; + +export const sessionReadyToInvoke = createAction('system/sessionReadyToInvoke'); diff --git a/invokeai/frontend/web/src/features/system/store/sessionSlice.ts b/invokeai/frontend/web/src/features/system/store/sessionSlice.ts new file mode 100644 index 0000000000..40d59c7baa --- /dev/null +++ b/invokeai/frontend/web/src/features/system/store/sessionSlice.ts @@ -0,0 +1,62 @@ +// TODO: split system slice inot this + +// import type { PayloadAction } from '@reduxjs/toolkit'; +// import { createSlice } from '@reduxjs/toolkit'; +// import { socketSubscribed, socketUnsubscribed } from 'services/events/actions'; + +// export type SessionState = { +// /** +// * The current socket session id +// */ +// sessionId: string; +// /** +// * Whether the current session is a canvas session. Needed to manage the staging area. +// */ +// isCanvasSession: boolean; +// /** +// * When a session is canceled, its ID is stored here until a new session is created. +// */ +// canceledSessionId: string; +// }; + +// export const initialSessionState: SessionState = { +// sessionId: '', +// isCanvasSession: false, +// canceledSessionId: '', +// }; + +// export const sessionSlice = createSlice({ +// name: 'session', +// initialState: initialSessionState, +// reducers: { +// sessionIdChanged: (state, action: PayloadAction) => { +// state.sessionId = action.payload; +// }, +// isCanvasSessionChanged: (state, action: PayloadAction) => { +// state.isCanvasSession = action.payload; +// }, +// }, +// extraReducers: (builder) => { +// /** +// * Socket Subscribed +// */ +// builder.addCase(socketSubscribed, (state, action) => { +// state.sessionId = action.payload.sessionId; +// state.canceledSessionId = ''; +// }); + +// /** +// * Socket Unsubscribed +// */ +// builder.addCase(socketUnsubscribed, (state) => { +// state.sessionId = ''; +// }); +// }, +// }); + +// export const { sessionIdChanged, isCanvasSessionChanged } = +// sessionSlice.actions; + +// export default sessionSlice.reducer; + +export default {}; diff --git a/invokeai/frontend/web/src/services/events/middleware.ts b/invokeai/frontend/web/src/services/events/middleware.ts index bd1d60099a..a78e0de97b 100644 --- a/invokeai/frontend/web/src/services/events/middleware.ts +++ b/invokeai/frontend/web/src/services/events/middleware.ts @@ -8,7 +8,11 @@ import { import { socketSubscribed, socketUnsubscribed } from './actions'; import { AppThunkDispatch, RootState } from 'app/store/store'; import { getTimestamp } from 'common/util/getTimestamp'; -import { sessionInvoked, sessionCreated } from 'services/thunks/session'; +import { + sessionInvoked, + sessionCreated, + sessionWithoutGraphCreated, +} from 'services/thunks/session'; import { OpenAPI } from 'services/api'; import { setEventListeners } from 'services/events/util/setEventListeners'; import { log } from 'app/logging/useLogger'; @@ -62,17 +66,14 @@ export const socketMiddleware = () => { socket.connect(); } - if (sessionCreated.fulfilled.match(action)) { + if ( + sessionCreated.fulfilled.match(action) || + sessionWithoutGraphCreated.fulfilled.match(action) + ) { const sessionId = action.payload.id; - const sessionLog = socketioLog.child({ sessionId }); const oldSessionId = getState().system.sessionId; if (oldSessionId) { - sessionLog.debug( - { oldSessionId }, - `Unsubscribed from old session (${oldSessionId})` - ); - socket.emit('unsubscribe', { session: oldSessionId, }); @@ -85,8 +86,6 @@ export const socketMiddleware = () => { ); } - sessionLog.debug(`Subscribe to new session (${sessionId})`); - socket.emit('subscribe', { session: sessionId }); dispatch( @@ -95,9 +94,6 @@ export const socketMiddleware = () => { timestamp: getTimestamp(), }) ); - - // Finally we actually invoke the session, starting processing - dispatch(sessionInvoked({ sessionId })); } next(action); diff --git a/invokeai/frontend/web/src/services/events/util/setEventListeners.ts b/invokeai/frontend/web/src/services/events/util/setEventListeners.ts index 4431a9fd8b..5262b26d1e 100644 --- a/invokeai/frontend/web/src/services/events/util/setEventListeners.ts +++ b/invokeai/frontend/web/src/services/events/util/setEventListeners.ts @@ -1,7 +1,6 @@ import { MiddlewareAPI } from '@reduxjs/toolkit'; import { AppDispatch, RootState } from 'app/store/store'; import { getTimestamp } from 'common/util/getTimestamp'; -import { sessionCanceled } from 'services/thunks/session'; import { Socket } from 'socket.io-client'; import { generatorProgress, @@ -16,12 +15,6 @@ import { import { ClientToServerEvents, ServerToClientEvents } from '../types'; import { Logger } from 'roarr'; import { JsonObject } from 'roarr/dist/types'; -import { - receivedResultImagesPage, - receivedUploadImagesPage, -} from 'services/thunks/gallery'; -import { receivedModels } from 'services/thunks/model'; -import { receivedOpenAPISchema } from 'services/thunks/schema'; import { makeToast } from '../../../app/components/Toaster'; import { addToast } from '../../../features/system/store/systemSlice'; @@ -43,37 +36,13 @@ export const setEventListeners = (arg: SetEventListenersArg) => { dispatch(socketConnected({ timestamp: getTimestamp() })); - const { results, uploads, models, nodes, config, system } = getState(); + const { sessionId } = getState().system; - const { disabledTabs } = config; - - // These thunks need to be dispatch in middleware; cannot handle in a reducer - if (!results.ids.length) { - dispatch(receivedResultImagesPage()); - } - - if (!uploads.ids.length) { - dispatch(receivedUploadImagesPage()); - } - - if (!models.ids.length) { - dispatch(receivedModels()); - } - - if (!nodes.schema && !disabledTabs.includes('nodes')) { - dispatch(receivedOpenAPISchema()); - } - - if (system.sessionId) { - log.debug( - { sessionId: system.sessionId }, - `Subscribed to existing session (${system.sessionId})` - ); - - socket.emit('subscribe', { session: system.sessionId }); + if (sessionId) { + socket.emit('subscribe', { session: sessionId }); dispatch( socketSubscribed({ - sessionId: system.sessionId, + sessionId, timestamp: getTimestamp(), }) ); @@ -101,7 +70,6 @@ export const setEventListeners = (arg: SetEventListenersArg) => { * Disconnect */ socket.on('disconnect', () => { - log.debug('Disconnected'); dispatch(socketDisconnected({ timestamp: getTimestamp() })); }); @@ -109,18 +77,6 @@ export const setEventListeners = (arg: SetEventListenersArg) => { * Invocation started */ socket.on('invocation_started', (data) => { - if (getState().system.canceledSession === data.graph_execution_state_id) { - log.trace( - { data, sessionId: data.graph_execution_state_id }, - `Ignored invocation started (${data.node.type}) for canceled session (${data.graph_execution_state_id})` - ); - return; - } - - log.info( - { data, sessionId: data.graph_execution_state_id }, - `Invocation started (${data.node.type})` - ); dispatch(invocationStarted({ data, timestamp: getTimestamp() })); }); @@ -128,18 +84,6 @@ export const setEventListeners = (arg: SetEventListenersArg) => { * Generator progress */ socket.on('generator_progress', (data) => { - if (getState().system.canceledSession === data.graph_execution_state_id) { - log.trace( - { data, sessionId: data.graph_execution_state_id }, - `Ignored generator progress (${data.node.type}) for canceled session (${data.graph_execution_state_id})` - ); - return; - } - - log.trace( - { data, sessionId: data.graph_execution_state_id }, - `Generator progress (${data.node.type})` - ); dispatch(generatorProgress({ data, timestamp: getTimestamp() })); }); @@ -147,10 +91,6 @@ export const setEventListeners = (arg: SetEventListenersArg) => { * Invocation error */ socket.on('invocation_error', (data) => { - log.error( - { data, sessionId: data.graph_execution_state_id }, - `Invocation error (${data.node.type})` - ); dispatch(invocationError({ data, timestamp: getTimestamp() })); }); @@ -158,19 +98,6 @@ export const setEventListeners = (arg: SetEventListenersArg) => { * Invocation complete */ socket.on('invocation_complete', (data) => { - log.info( - { data, sessionId: data.graph_execution_state_id }, - `Invocation complete (${data.node.type})` - ); - const sessionId = data.graph_execution_state_id; - - const { cancelType, isCancelScheduled } = getState().system; - - // Handle scheduled cancelation - if (cancelType === 'scheduled' && isCancelScheduled) { - dispatch(sessionCanceled({ sessionId })); - } - dispatch( invocationComplete({ data, @@ -183,10 +110,6 @@ export const setEventListeners = (arg: SetEventListenersArg) => { * Graph complete */ socket.on('graph_execution_state_complete', (data) => { - log.info( - { data, sessionId: data.graph_execution_state_id }, - `Graph execution state complete (${data.graph_execution_state_id})` - ); dispatch(graphExecutionStateComplete({ data, timestamp: getTimestamp() })); }); }; diff --git a/invokeai/frontend/web/src/services/thunks/gallery.ts b/invokeai/frontend/web/src/services/thunks/gallery.ts index 01e8a986b2..5321b7ca3e 100644 --- a/invokeai/frontend/web/src/services/thunks/gallery.ts +++ b/invokeai/frontend/web/src/services/thunks/gallery.ts @@ -12,7 +12,7 @@ export const receivedResultImagesPage = createAppAsyncThunk( const { page, pages, nextPage } = getState().results; if (nextPage === page) { - rejectWithValue([]); + return rejectWithValue([]); } const response = await ImagesService.listImagesWithMetadata({ @@ -30,7 +30,13 @@ export const receivedResultImagesPage = createAppAsyncThunk( export const receivedUploadImagesPage = createAppAsyncThunk( 'uploads/receivedUploadImagesPage', - async (_arg, { getState }) => { + async (_arg, { getState, rejectWithValue }) => { + const { page, pages, nextPage } = getState().uploads; + + if (nextPage === page) { + return rejectWithValue([]); + } + const response = await ImagesService.listImagesWithMetadata({ imageType: 'uploads', imageCategory: 'general', diff --git a/invokeai/frontend/web/src/services/thunks/image.ts b/invokeai/frontend/web/src/services/thunks/image.ts index 6831eb647d..34b369e3eb 100644 --- a/invokeai/frontend/web/src/services/thunks/image.ts +++ b/invokeai/frontend/web/src/services/thunks/image.ts @@ -76,3 +76,19 @@ export const imageDeleted = createAppAsyncThunk( return response; } ); + +type ImageUpdatedArg = Parameters<(typeof ImagesService)['updateImage']>[0]; + +/** + * `ImagesService.deleteImage()` thunk + */ +export const imageUpdated = createAppAsyncThunk( + 'api/imageUpdated', + async (arg: ImageUpdatedArg) => { + const response = await ImagesService.updateImage(arg); + + imagesLog.debug({ arg, response }, 'Image updated'); + + return response; + } +); diff --git a/invokeai/frontend/web/src/services/thunks/session.ts b/invokeai/frontend/web/src/services/thunks/session.ts index dca4134886..a1ee5a34ed 100644 --- a/invokeai/frontend/web/src/services/thunks/session.ts +++ b/invokeai/frontend/web/src/services/thunks/session.ts @@ -35,6 +35,28 @@ export const sessionCreated = createAppAsyncThunk( } ); +/** + * `SessionsService.createSession()` without graph thunk + */ +export const sessionWithoutGraphCreated = createAppAsyncThunk( + 'api/sessionWithoutGraphCreated', + async (_, { rejectWithValue }) => { + try { + const response = await SessionsService.createSession({}); + sessionLog.info({ response }, `Session created (${response.id})`); + return response; + } catch (err: any) { + sessionLog.error( + { + error: serializeError(err), + }, + 'Problem creating session' + ); + return rejectWithValue(err.message); + } + } +); + type NodeAddedArg = Parameters<(typeof SessionsService)['addNode']>[0]; /** @@ -57,6 +79,29 @@ export const nodeAdded = createAppAsyncThunk( } ); +type NodeUpdatedArg = Parameters<(typeof SessionsService)['updateNode']>[0]; + +/** + * `SessionsService.addNode()` thunk + */ +export const nodeUpdated = createAppAsyncThunk( + 'api/nodeUpdated', + async ( + arg: { node: NodeUpdatedArg['requestBody']; sessionId: string }, + _thunkApi + ) => { + const response = await SessionsService.updateNode({ + requestBody: arg.node, + sessionId: arg.sessionId, + nodePath: arg.node.id, + }); + + sessionLog.info({ arg, response }, `Node updated (${response})`); + + return response; + } +); + /** * `SessionsService.invokeSession()` thunk */ From 291e9cf14bb980e640f4fd9098e820cb20597692 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 25 May 2023 23:55:36 +1000 Subject: [PATCH 10/31] fix(nodes): add `is_intermediate` to all image-outputting nodes --- invokeai/app/invocations/cv.py | 1 + invokeai/app/invocations/generate.py | 3 +++ invokeai/app/invocations/image.py | 9 +++++++++ invokeai/app/invocations/infill.py | 3 +++ invokeai/app/invocations/reconstruct.py | 1 + invokeai/app/invocations/upscale.py | 1 + 6 files changed, 18 insertions(+) diff --git a/invokeai/app/invocations/cv.py b/invokeai/app/invocations/cv.py index d900ecfdbf..f24a83a795 100644 --- a/invokeai/app/invocations/cv.py +++ b/invokeai/app/invocations/cv.py @@ -61,6 +61,7 @@ class CvInpaintInvocation(BaseInvocation, CvInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/generate.py b/invokeai/app/invocations/generate.py index 6af959a5bd..2fa2a15faa 100644 --- a/invokeai/app/invocations/generate.py +++ b/invokeai/app/invocations/generate.py @@ -101,6 +101,7 @@ class TextToImageInvocation(BaseInvocation, SDImageInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -181,6 +182,7 @@ class ImageToImageInvocation(TextToImageInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -296,6 +298,7 @@ class InpaintInvocation(ImageToImageInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/image.py b/invokeai/app/invocations/image.py index 21dfb4c1cd..c561eb100f 100644 --- a/invokeai/app/invocations/image.py +++ b/invokeai/app/invocations/image.py @@ -143,6 +143,7 @@ class ImageCropInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -204,6 +205,7 @@ class ImagePasteInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -242,6 +244,7 @@ class MaskFromAlphaInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.MASK, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return MaskOutput( @@ -280,6 +283,7 @@ class ImageMultiplyInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -318,6 +322,7 @@ class ImageChannelInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -356,6 +361,7 @@ class ImageConvertInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -397,6 +403,7 @@ class ImageBlurInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -437,6 +444,7 @@ class ImageLerpInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -482,6 +490,7 @@ class ImageInverseLerpInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/infill.py b/invokeai/app/invocations/infill.py index 17a43dbdac..313edec201 100644 --- a/invokeai/app/invocations/infill.py +++ b/invokeai/app/invocations/infill.py @@ -149,6 +149,7 @@ class InfillColorInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -193,6 +194,7 @@ class InfillTileInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( @@ -230,6 +232,7 @@ class InfillPatchMatchInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/reconstruct.py b/invokeai/app/invocations/reconstruct.py index a234693128..0fa83df941 100644 --- a/invokeai/app/invocations/reconstruct.py +++ b/invokeai/app/invocations/reconstruct.py @@ -47,6 +47,7 @@ class RestoreFaceInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/upscale.py b/invokeai/app/invocations/upscale.py index 75aeec784f..07b574e02e 100644 --- a/invokeai/app/invocations/upscale.py +++ b/invokeai/app/invocations/upscale.py @@ -49,6 +49,7 @@ class UpscaleInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, + is_intermediate=self.meta.is_intermediate, ) return ImageOutput( From 43d991cfdbcc41cd301a29cc102b7768c29acf74 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 00:01:31 +1000 Subject: [PATCH 11/31] fix(ui): fix incorrect comment --- .../listeners/userInvokedCanvas.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts index b90197f7cb..46f6efe934 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts @@ -1,5 +1,5 @@ import { startAppListening } from '..'; -import { nodeUpdated, sessionCreated } from 'services/thunks/session'; +import { sessionCreated } from 'services/thunks/session'; import { buildCanvasGraphComponents } from 'features/nodes/util/graphBuilders/buildCanvasGraph'; import { log } from 'app/logging/useLogger'; import { canvasGraphBuilt } from 'features/nodes/store/actions'; @@ -20,20 +20,17 @@ import { sessionReadyToInvoke } from 'features/system/store/actions'; const moduleLog = log.child({ namespace: 'invoke' }); /** - * This listener is responsible invoking the canvas. This involved a number of steps: + * This listener is responsible invoking the canvas. This involves a number of steps: * * 1. Generate image blobs from the canvas layers * 2. Determine the generation mode from the layers (txt2img, img2img, inpaint) * 3. Build the canvas graph - * 4. Create the session - * 5. Upload the init image if necessary, then update the graph to refer to it (needs a separate request) - * 6. Upload the mask image if necessary, then update the graph to refer to it (needs a separate request) - * 7. Initialize the staging area if not yet initialized - * 8. Finally, dispatch the sessionReadyToInvoke action to invoke the session - * - * We have to do the uploads after creating the session: - * - We need to associate these particular uploads to a session, and flag them as intermediates - * - To do this, we need to associa + * 4. Create the session with the graph + * 5. Upload the init image if necessary + * 6. Upload the mask image if necessary + * 7. Update the init and mask images with the session ID + * 8. Initialize the staging area if not yet initialized + * 9. Dispatch the sessionReadyToInvoke action to invoke the session */ export const addUserInvokedCanvasListener = () => { startAppListening({ From 33e13820fc341c10ff4186cd1a3f981e9415d95c Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 11:39:19 +1000 Subject: [PATCH 12/31] feat(nodes): remove `meta` node field; use individual `is_intermediate` field instead as suggested by @Kyle0654 --- invokeai/app/invocations/baseinvocation.py | 9 +-------- invokeai/app/invocations/cv.py | 2 +- invokeai/app/invocations/generate.py | 6 +++--- invokeai/app/invocations/image.py | 18 +++++++++--------- invokeai/app/invocations/infill.py | 6 +++--- invokeai/app/invocations/latent.py | 2 +- invokeai/app/invocations/reconstruct.py | 2 +- invokeai/app/invocations/upscale.py | 2 +- 8 files changed, 20 insertions(+), 27 deletions(-) diff --git a/invokeai/app/invocations/baseinvocation.py b/invokeai/app/invocations/baseinvocation.py index 1ba498c9d8..5f75cfd3b8 100644 --- a/invokeai/app/invocations/baseinvocation.py +++ b/invokeai/app/invocations/baseinvocation.py @@ -39,12 +39,6 @@ class BaseInvocationOutput(BaseModel): return tuple(subclasses) -class InvocationMeta(BaseModel): - is_intermediate: bool = Field( - default=False, - description="Whether this is an intermediate node. Intermediate nodes are periodically deleted." - ) - class BaseInvocation(ABC, BaseModel): """A node to process inputs and produce outputs. May use dependency injection in __init__ to receive providers. @@ -84,8 +78,7 @@ class BaseInvocation(ABC, BaseModel): #fmt: off id: str = Field(description="The id of this node. Must be unique among all nodes.") - type: str = Field(description="The type of this node. Must be unique among all nodes.") - meta: InvocationMeta = Field(default=InvocationMeta(), description="The meta properties of this node.") + is_intermediate: bool = Field(default=False, description="Whether or not this node is an intermediate node.") #fmt: on diff --git a/invokeai/app/invocations/cv.py b/invokeai/app/invocations/cv.py index f24a83a795..5e9fe088b5 100644 --- a/invokeai/app/invocations/cv.py +++ b/invokeai/app/invocations/cv.py @@ -61,7 +61,7 @@ class CvInpaintInvocation(BaseInvocation, CvInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/generate.py b/invokeai/app/invocations/generate.py index 2fa2a15faa..44280c3b41 100644 --- a/invokeai/app/invocations/generate.py +++ b/invokeai/app/invocations/generate.py @@ -101,7 +101,7 @@ class TextToImageInvocation(BaseInvocation, SDImageInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -182,7 +182,7 @@ class ImageToImageInvocation(TextToImageInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -298,7 +298,7 @@ class InpaintInvocation(ImageToImageInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/image.py b/invokeai/app/invocations/image.py index c561eb100f..69d51e6158 100644 --- a/invokeai/app/invocations/image.py +++ b/invokeai/app/invocations/image.py @@ -143,7 +143,7 @@ class ImageCropInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -205,7 +205,7 @@ class ImagePasteInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -244,7 +244,7 @@ class MaskFromAlphaInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.MASK, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return MaskOutput( @@ -283,7 +283,7 @@ class ImageMultiplyInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -322,7 +322,7 @@ class ImageChannelInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -361,7 +361,7 @@ class ImageConvertInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -403,7 +403,7 @@ class ImageBlurInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -444,7 +444,7 @@ class ImageLerpInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -490,7 +490,7 @@ class ImageInverseLerpInvocation(BaseInvocation, PILInvocationConfig): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/infill.py b/invokeai/app/invocations/infill.py index 313edec201..ad60b62633 100644 --- a/invokeai/app/invocations/infill.py +++ b/invokeai/app/invocations/infill.py @@ -149,7 +149,7 @@ class InfillColorInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -194,7 +194,7 @@ class InfillTileInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( @@ -232,7 +232,7 @@ class InfillPatchMatchInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 9f78d72b77..11ea7134bb 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -370,7 +370,7 @@ class LatentsToImageInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, session_id=context.graph_execution_state_id, node_id=self.id, - is_intermediate=self.meta.is_intermediate + is_intermediate=self.is_intermediate ) return ImageOutput( diff --git a/invokeai/app/invocations/reconstruct.py b/invokeai/app/invocations/reconstruct.py index 0fa83df941..db71e4201d 100644 --- a/invokeai/app/invocations/reconstruct.py +++ b/invokeai/app/invocations/reconstruct.py @@ -47,7 +47,7 @@ class RestoreFaceInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( diff --git a/invokeai/app/invocations/upscale.py b/invokeai/app/invocations/upscale.py index 07b574e02e..90c9e4bf4f 100644 --- a/invokeai/app/invocations/upscale.py +++ b/invokeai/app/invocations/upscale.py @@ -49,7 +49,7 @@ class UpscaleInvocation(BaseInvocation): image_category=ImageCategory.GENERAL, node_id=self.id, session_id=context.graph_execution_state_id, - is_intermediate=self.meta.is_intermediate, + is_intermediate=self.is_intermediate, ) return ImageOutput( From 0c3b4bb70d78e9884636a9a439067acac55205af Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 11:39:29 +1000 Subject: [PATCH 13/31] chore(ui): regen api client --- invokeai/frontend/web/src/services/api/index.ts | 1 - .../web/src/services/api/models/AddInvocation.ts | 8 +++----- .../web/src/services/api/models/CollectInvocation.ts | 8 +++----- .../web/src/services/api/models/CompelInvocation.ts | 8 +++----- .../src/services/api/models/CvInpaintInvocation.ts | 7 +++---- .../web/src/services/api/models/DivideInvocation.ts | 8 +++----- .../web/src/services/api/models/GraphInvocation.ts | 7 +++---- .../src/services/api/models/ImageBlurInvocation.ts | 7 +++---- .../src/services/api/models/ImageChannelInvocation.ts | 7 +++---- .../src/services/api/models/ImageConvertInvocation.ts | 7 +++---- .../src/services/api/models/ImageCropInvocation.ts | 7 +++---- .../services/api/models/ImageInverseLerpInvocation.ts | 7 +++---- .../src/services/api/models/ImageLerpInvocation.ts | 7 +++---- .../services/api/models/ImageMultiplyInvocation.ts | 7 +++---- .../src/services/api/models/ImagePasteInvocation.ts | 7 +++---- .../src/services/api/models/ImageToImageInvocation.ts | 7 +++---- .../services/api/models/ImageToLatentsInvocation.ts | 7 +++---- .../src/services/api/models/InfillColorInvocation.ts | 7 +++---- .../services/api/models/InfillPatchMatchInvocation.ts | 7 +++---- .../src/services/api/models/InfillTileInvocation.ts | 7 +++---- .../web/src/services/api/models/InpaintInvocation.ts | 7 +++---- .../web/src/services/api/models/InvocationMeta.ts | 11 ----------- .../web/src/services/api/models/IterateInvocation.ts | 8 +++----- .../services/api/models/LatentsToImageInvocation.ts | 7 +++---- .../services/api/models/LatentsToLatentsInvocation.ts | 7 +++---- .../src/services/api/models/LoadImageInvocation.ts | 7 +++---- .../services/api/models/MaskFromAlphaInvocation.ts | 7 +++---- .../web/src/services/api/models/MultiplyInvocation.ts | 8 +++----- .../web/src/services/api/models/NoiseInvocation.ts | 8 +++----- .../web/src/services/api/models/ParamIntInvocation.ts | 8 +++----- .../src/services/api/models/RandomIntInvocation.ts | 8 +++----- .../src/services/api/models/RandomRangeInvocation.ts | 8 +++----- .../web/src/services/api/models/RangeInvocation.ts | 8 +++----- .../src/services/api/models/RangeOfSizeInvocation.ts | 8 +++----- .../services/api/models/ResizeLatentsInvocation.ts | 7 +++---- .../src/services/api/models/RestoreFaceInvocation.ts | 7 +++---- .../src/services/api/models/ScaleLatentsInvocation.ts | 7 +++---- .../src/services/api/models/ShowImageInvocation.ts | 7 +++---- .../web/src/services/api/models/SubtractInvocation.ts | 8 +++----- .../src/services/api/models/TextToImageInvocation.ts | 8 +++----- .../services/api/models/TextToLatentsInvocation.ts | 7 +++---- .../web/src/services/api/models/UpscaleInvocation.ts | 7 +++---- 42 files changed, 120 insertions(+), 186 deletions(-) delete mode 100644 invokeai/frontend/web/src/services/api/models/InvocationMeta.ts diff --git a/invokeai/frontend/web/src/services/api/index.ts b/invokeai/frontend/web/src/services/api/index.ts index b7cfb476a2..e75aeac6cb 100644 --- a/invokeai/frontend/web/src/services/api/index.ts +++ b/invokeai/frontend/web/src/services/api/index.ts @@ -50,7 +50,6 @@ export type { InfillTileInvocation } from './models/InfillTileInvocation'; export type { InpaintInvocation } from './models/InpaintInvocation'; export type { IntCollectionOutput } from './models/IntCollectionOutput'; export type { IntOutput } from './models/IntOutput'; -export type { InvocationMeta } from './models/InvocationMeta'; export type { IterateInvocation } from './models/IterateInvocation'; export type { IterateInvocationOutput } from './models/IterateInvocationOutput'; export type { LatentsField } from './models/LatentsField'; diff --git a/invokeai/frontend/web/src/services/api/models/AddInvocation.ts b/invokeai/frontend/web/src/services/api/models/AddInvocation.ts index 52898bfd0c..e9671a918f 100644 --- a/invokeai/frontend/web/src/services/api/models/AddInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/AddInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Adds two numbers */ @@ -12,11 +10,11 @@ export type AddInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'add'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'add'; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts b/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts index fb6c7b40e5..f190ab7073 100644 --- a/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/CollectInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Collects values into a collection */ @@ -12,11 +10,11 @@ export type CollectInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'collect'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'collect'; /** * The item to collect (all inputs must be of the same type) */ diff --git a/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts b/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts index ec483a76d5..1dc390c1be 100644 --- a/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/CompelInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Parse prompt using compel package to conditioning. */ @@ -12,11 +10,11 @@ export type CompelInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'compel'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'compel'; /** * Prompt */ diff --git a/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts b/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts index d8fafc02d4..874df93c30 100644 --- a/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/CvInpaintInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Simple inpaint using opencv. @@ -13,11 +12,11 @@ export type CvInpaintInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'cv_inpaint'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'cv_inpaint'; /** * The image to inpaint */ diff --git a/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts b/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts index 220f635580..fd5b3475ae 100644 --- a/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/DivideInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Divides two numbers */ @@ -12,11 +10,11 @@ export type DivideInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'div'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'div'; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts b/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts index b631edce62..8512faae74 100644 --- a/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/GraphInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { Graph } from './Graph'; -import type { InvocationMeta } from './InvocationMeta'; /** * Execute a graph @@ -13,11 +12,11 @@ export type GraphInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'graph'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'graph'; /** * The graph to run */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageBlurInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageBlurInvocation.ts index caa189c0ab..3ba86d8fab 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageBlurInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageBlurInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Blurs an image @@ -13,11 +12,11 @@ export type ImageBlurInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_blur'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_blur'; /** * The image to blur */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts index 0320b3d50a..47bfd4110f 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageChannelInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Gets a channel from an image. @@ -13,11 +12,11 @@ export type ImageChannelInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_chan'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_chan'; /** * The image to get the channel from */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts index 34f0365541..4bd59d03b0 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageConvertInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Converts an image to a different mode. @@ -13,11 +12,11 @@ export type ImageConvertInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_conv'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_conv'; /** * The image to convert */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageCropInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageCropInvocation.ts index ad672f1772..5207ebbf6d 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageCropInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageCropInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Crops an image to a specified box. The box can be outside of the image. @@ -13,11 +12,11 @@ export type ImageCropInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_crop'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_crop'; /** * The image to crop */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageInverseLerpInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageInverseLerpInvocation.ts index be88fb5d54..0347d4dc38 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageInverseLerpInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageInverseLerpInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Inverse linear interpolation of all pixels of an image @@ -13,11 +12,11 @@ export type ImageInverseLerpInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_ilerp'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_ilerp'; /** * The image to lerp */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageLerpInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageLerpInvocation.ts index fcf1ea184c..388c86061c 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageLerpInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageLerpInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Linear interpolation of all pixels of an image @@ -13,11 +12,11 @@ export type ImageLerpInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_lerp'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_lerp'; /** * The image to lerp */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts index c1b76b3864..751ee49158 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageMultiplyInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Multiplies two images together using `PIL.ImageChops.multiply()`. @@ -13,11 +12,11 @@ export type ImageMultiplyInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_mul'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_mul'; /** * The first image to multiply */ diff --git a/invokeai/frontend/web/src/services/api/models/ImagePasteInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImagePasteInvocation.ts index 3db0c33bf9..c883b9a5d8 100644 --- a/invokeai/frontend/web/src/services/api/models/ImagePasteInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImagePasteInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Pastes an image into another image. @@ -13,11 +12,11 @@ export type ImagePasteInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img_paste'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img_paste'; /** * The base image */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts index 809403e38c..7287b4cb71 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Generates an image using img2img. @@ -13,11 +12,11 @@ export type ImageToImageInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'img2img'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'img2img'; /** * The prompt to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts index fc510a2b4c..5569c2fa86 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageToLatentsInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Encodes an image into latents. @@ -13,11 +12,11 @@ export type ImageToLatentsInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'i2l'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'i2l'; /** * The image to encode */ diff --git a/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts index dbfac9cd60..3e637b299c 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts @@ -4,7 +4,6 @@ import type { ColorField } from './ColorField'; import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Infills transparent areas of an image with a solid color @@ -14,11 +13,11 @@ export type InfillColorInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'infill_rgba'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'infill_rgba'; /** * The image to infill */ diff --git a/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts index 49d8519e13..325bfe2080 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Infills transparent areas of an image using the PatchMatch algorithm @@ -13,11 +12,11 @@ export type InfillPatchMatchInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'infill_patchmatch'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'infill_patchmatch'; /** * The image to infill */ diff --git a/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts index ce318b9c29..dfb1cbc61d 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillTileInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Infills transparent areas of an image with tiles of the image @@ -13,11 +12,11 @@ export type InfillTileInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'infill_tile'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'infill_tile'; /** * The image to infill */ diff --git a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts index c73d001c5b..fa5ae01c8f 100644 --- a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts @@ -4,7 +4,6 @@ import type { ColorField } from './ColorField'; import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Generates an image using inpaint. @@ -14,11 +13,11 @@ export type InpaintInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'inpaint'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'inpaint'; /** * The prompt to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/InvocationMeta.ts b/invokeai/frontend/web/src/services/api/models/InvocationMeta.ts deleted file mode 100644 index e2973c150a..0000000000 --- a/invokeai/frontend/web/src/services/api/models/InvocationMeta.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type InvocationMeta = { - /** - * Whether this is an intermediate node. Intermediate nodes are periodically deleted. - */ - is_intermediate?: boolean; -}; - diff --git a/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts b/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts index 419caa15a4..15bf92dfea 100644 --- a/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/IterateInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Iterates over a list of items */ @@ -12,11 +10,11 @@ export type IterateInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'iterate'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'iterate'; /** * The list of items to iterate over */ diff --git a/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts index a01877cc40..fcaa37d7e8 100644 --- a/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LatentsToImageInvocation.ts @@ -2,7 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -13,11 +12,11 @@ export type LatentsToImageInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'l2i'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'l2i'; /** * The latents to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts index b441f51b3f..6436557f64 100644 --- a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ConditioningField } from './ConditioningField'; -import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -14,11 +13,11 @@ export type LatentsToLatentsInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'l2l'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'l2l'; /** * Positive conditioning for generation */ diff --git a/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts index c147597806..f20d983f9b 100644 --- a/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LoadImageInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Load an image and provide it as output. @@ -13,11 +12,11 @@ export type LoadImageInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'load_image'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'load_image'; /** * The image to load */ diff --git a/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts b/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts index 74aee00b2f..e3693f6d98 100644 --- a/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/MaskFromAlphaInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Extracts the alpha channel of an image as a mask. @@ -13,11 +12,11 @@ export type MaskFromAlphaInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'tomask'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'tomask'; /** * The image to create the mask from */ diff --git a/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts b/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts index aa6cbf6fb4..9fd716f33d 100644 --- a/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/MultiplyInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Multiplies two numbers */ @@ -12,11 +10,11 @@ export type MultiplyInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'mul'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'mul'; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts b/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts index 67c77d024e..239a24bfe5 100644 --- a/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/NoiseInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Generates latent noise. */ @@ -12,11 +10,11 @@ export type NoiseInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'noise'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'noise'; /** * The seed to use */ diff --git a/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts b/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts index 6dc597f183..7a45d0a0ac 100644 --- a/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ParamIntInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * An integer parameter */ @@ -12,11 +10,11 @@ export type ParamIntInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'param_int'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'param_int'; /** * The integer value */ diff --git a/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts b/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts index 1cccffe93e..a2f7c2f02a 100644 --- a/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RandomIntInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Outputs a single random integer. */ @@ -12,11 +10,11 @@ export type RandomIntInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'rand_int'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'rand_int'; /** * The inclusive low value */ diff --git a/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts b/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts index fbf8455226..925511578d 100644 --- a/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RandomRangeInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Creates a collection of random numbers */ @@ -12,11 +10,11 @@ export type RandomRangeInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'random_range'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'random_range'; /** * The inclusive low value */ diff --git a/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts b/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts index 5173ceeb7f..3681602a95 100644 --- a/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RangeInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Creates a range of numbers from start to stop with step */ @@ -12,11 +10,11 @@ export type RangeInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'range'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'range'; /** * The start of the range */ diff --git a/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts b/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts index 2a7d5bae2a..7dfac68d39 100644 --- a/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RangeOfSizeInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Creates a range from start to start + size with step */ @@ -12,11 +10,11 @@ export type RangeOfSizeInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'range_of_size'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'range_of_size'; /** * The start of the range */ diff --git a/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts index e0cbb5c0c7..9a7b6c61e4 100644 --- a/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ResizeLatentsInvocation.ts @@ -2,7 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -13,11 +12,11 @@ export type ResizeLatentsInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'lresize'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'lresize'; /** * The latents to resize */ diff --git a/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts b/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts index 01de107ab9..0bacb5d805 100644 --- a/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/RestoreFaceInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Restores faces in an image. @@ -13,11 +12,11 @@ export type RestoreFaceInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'restore_face'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'restore_face'; /** * The input image */ diff --git a/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts index 08cd8de900..506b21e540 100644 --- a/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ScaleLatentsInvocation.ts @@ -2,7 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -13,11 +12,11 @@ export type ScaleLatentsInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'lscale'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'lscale'; /** * The latents to scale */ diff --git a/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts index 6934eeaa90..1b73055584 100644 --- a/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ShowImageInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Displays a provided image, and passes it forward in the pipeline. @@ -13,11 +12,11 @@ export type ShowImageInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'show_image'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'show_image'; /** * The image to show */ diff --git a/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts b/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts index 8ef305183c..23334bd891 100644 --- a/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/SubtractInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Subtracts two numbers */ @@ -12,11 +10,11 @@ export type SubtractInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'sub'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'sub'; /** * The first number */ diff --git a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts index 56dfd076f9..de95ff738c 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts @@ -2,8 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { InvocationMeta } from './InvocationMeta'; - /** * Generates an image using text2img. */ @@ -12,11 +10,11 @@ export type TextToImageInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'txt2img'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'txt2img'; /** * The prompt to generate an image from */ diff --git a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts index 865360d39b..33eedc0f02 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ConditioningField } from './ConditioningField'; -import type { InvocationMeta } from './InvocationMeta'; import type { LatentsField } from './LatentsField'; /** @@ -14,11 +13,11 @@ export type TextToLatentsInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 't2l'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 't2l'; /** * Positive conditioning for generation */ diff --git a/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts b/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts index fe34b74678..d0aca63964 100644 --- a/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/UpscaleInvocation.ts @@ -3,7 +3,6 @@ /* eslint-disable */ import type { ImageField } from './ImageField'; -import type { InvocationMeta } from './InvocationMeta'; /** * Upscales an image. @@ -13,11 +12,11 @@ export type UpscaleInvocation = { * The id of this node. Must be unique among all nodes. */ id: string; - type?: 'upscale'; /** - * The meta properties of this node. + * Whether or not this node is an intermediate node. */ - meta?: InvocationMeta; + is_intermediate?: boolean; + type?: 'upscale'; /** * The input image */ From 82a8972bde318de3c168af57ee29b8ed08738cbe Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Thu, 25 May 2023 16:11:05 -0400 Subject: [PATCH 14/31] create listener for imageMetdataReceived to swap our progressImage --- .../middleware/listenerMiddleware/index.ts | 2 ++ .../listeners/imageMetadataReceived.ts | 26 +++++++++++++++++++ .../src/features/system/store/systemSlice.ts | 5 +++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index c04a3943f3..48608b789a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -28,6 +28,7 @@ import { addSocketDisconnectedListener } from './listeners/socketio/socketDiscon import { addSocketSubscribedListener } from './listeners/socketio/socketSubscribed'; import { addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed'; import { addSessionReadyToInvokeListener } from './listeners/sessionReadyToInvoke'; +import { addImageMetadataReceivedListener } from './listeners/imageMetadataReceived'; export const listenerMiddleware = createListenerMiddleware(); @@ -50,6 +51,7 @@ export type AppListenerEffect = ListenerEffect< addImageUploadedListener(); addInitialImageSelectedListener(); addRequestedImageDeletionListener(); +addImageMetadataReceivedListener(); addUserInvokedCanvasListener(); addUserInvokedNodesListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts new file mode 100644 index 0000000000..4a610154ad --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts @@ -0,0 +1,26 @@ +import { invocationComplete } from 'services/events/actions'; +import { isImageOutput } from 'services/types/guards'; +import { imageMetadataReceived } from 'services/thunks/image'; +import { startAppListening } from '..'; +import { progressImageSet } from '../../../../../features/system/store/systemSlice'; + +export const addImageMetadataReceivedListener = () => { + startAppListening({ + predicate: (action) => { + if ( + invocationComplete.match(action) && + isImageOutput(action.payload.data.result) + ) { + return true; + } + return false; + }, + effect: async (action, { getState, dispatch, take }) => { + if (imageMetadataReceived.fulfilled.match(action)) { + return; + } + + dispatch(progressImageSet(null)); + }, + }); +}; diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 7331fcdba9..06fa0e47d8 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -215,6 +215,9 @@ export const systemSlice = createSlice({ languageChanged: (state, action: PayloadAction) => { state.language = action.payload; }, + progressImageSet(state, action: PayloadAction) { + state.progressImage = action.payload; + }, }, extraReducers(builder) { /** @@ -305,7 +308,6 @@ export const systemSlice = createSlice({ state.currentStep = 0; state.totalSteps = 0; state.statusTranslationKey = 'common.statusProcessingComplete'; - state.progressImage = null; if (state.canceledSession === data.graph_execution_state_id) { state.isProcessing = false; @@ -438,6 +440,7 @@ export const { isPersistedChanged, shouldAntialiasProgressImageChanged, languageChanged, + progressImageSet, } = systemSlice.actions; export default systemSlice.reducer; From 57a3eb365271facbd884bcae92921b08322ed962 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 13:24:44 +1000 Subject: [PATCH 15/31] feat(ui): unset progress image inside invocationComplete listener --- .../middleware/listenerMiddleware/index.ts | 2 -- .../listeners/imageMetadataReceived.ts | 26 ------------------- .../listeners/socketio/invocationComplete.ts | 9 +++---- 3 files changed, 3 insertions(+), 34 deletions(-) delete mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index 48608b789a..c04a3943f3 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -28,7 +28,6 @@ import { addSocketDisconnectedListener } from './listeners/socketio/socketDiscon import { addSocketSubscribedListener } from './listeners/socketio/socketSubscribed'; import { addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed'; import { addSessionReadyToInvokeListener } from './listeners/sessionReadyToInvoke'; -import { addImageMetadataReceivedListener } from './listeners/imageMetadataReceived'; export const listenerMiddleware = createListenerMiddleware(); @@ -51,7 +50,6 @@ export type AppListenerEffect = ListenerEffect< addImageUploadedListener(); addInitialImageSelectedListener(); addRequestedImageDeletionListener(); -addImageMetadataReceivedListener(); addUserInvokedCanvasListener(); addUserInvokedNodesListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts deleted file mode 100644 index 4a610154ad..0000000000 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { invocationComplete } from 'services/events/actions'; -import { isImageOutput } from 'services/types/guards'; -import { imageMetadataReceived } from 'services/thunks/image'; -import { startAppListening } from '..'; -import { progressImageSet } from '../../../../../features/system/store/systemSlice'; - -export const addImageMetadataReceivedListener = () => { - startAppListening({ - predicate: (action) => { - if ( - invocationComplete.match(action) && - isImageOutput(action.payload.data.result) - ) { - return true; - } - return false; - }, - effect: async (action, { getState, dispatch, take }) => { - if (imageMetadataReceived.fulfilled.match(action)) { - return; - } - - dispatch(progressImageSet(null)); - }, - }); -}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts index 76ae46c4a2..70ae1d2c71 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts @@ -8,6 +8,7 @@ import { } from 'services/thunks/image'; import { sessionCanceled } from 'services/thunks/session'; import { isImageOutput } from 'services/types/guards'; +import { progressImageSet } from 'features/system/store/systemSlice'; const moduleLog = log.child({ namespace: 'socketio' }); const nodeDenylist = ['dataURL_image']; @@ -37,12 +38,6 @@ export const addInvocationCompleteListener = () => { if (isImageOutput(result) && !nodeDenylist.includes(node.type)) { const { image_name, image_type } = result.image; - // Get its URLS - // TODO: is this extraneous? I think so... - dispatch( - imageUrlsReceived({ imageName: image_name, imageType: image_type }) - ); - // Get its metadata dispatch( imageMetadataReceived({ @@ -65,6 +60,8 @@ export const addInvocationCompleteListener = () => { ); dispatch(addImageToStagingArea(image)); } + + dispatch(progressImageSet(null)); } }, }); From f88ccabe30986320d71d3c26b06ff4d8b86fc1d0 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 13:55:44 +1000 Subject: [PATCH 16/31] fix(ui): gallery not loading on page load --- invokeai/frontend/web/src/services/thunks/gallery.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/invokeai/frontend/web/src/services/thunks/gallery.ts b/invokeai/frontend/web/src/services/thunks/gallery.ts index 5321b7ca3e..25d8ce0738 100644 --- a/invokeai/frontend/web/src/services/thunks/gallery.ts +++ b/invokeai/frontend/web/src/services/thunks/gallery.ts @@ -11,10 +11,6 @@ export const receivedResultImagesPage = createAppAsyncThunk( async (_arg, { getState, rejectWithValue }) => { const { page, pages, nextPage } = getState().results; - if (nextPage === page) { - return rejectWithValue([]); - } - const response = await ImagesService.listImagesWithMetadata({ imageType: 'results', imageCategory: 'general', @@ -33,10 +29,6 @@ export const receivedUploadImagesPage = createAppAsyncThunk( async (_arg, { getState, rejectWithValue }) => { const { page, pages, nextPage } = getState().uploads; - if (nextPage === page) { - return rejectWithValue([]); - } - const response = await ImagesService.listImagesWithMetadata({ imageType: 'uploads', imageCategory: 'general', From 17164a37a8f97dd52c10b5f8308d988075545b7e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 13:56:53 +1000 Subject: [PATCH 17/31] fix(ui): fix gallery auto switch --- .../listeners/socketio/invocationComplete.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts index 70ae1d2c71..cfd9706306 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts @@ -2,13 +2,11 @@ import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; import { startAppListening } from '../..'; import { log } from 'app/logging/useLogger'; import { invocationComplete } from 'services/events/actions'; -import { - imageMetadataReceived, - imageUrlsReceived, -} from 'services/thunks/image'; +import { imageMetadataReceived } from 'services/thunks/image'; import { sessionCanceled } from 'services/thunks/session'; import { isImageOutput } from 'services/types/guards'; import { progressImageSet } from 'features/system/store/systemSlice'; +import { imageSelected } from 'features/gallery/store/gallerySlice'; const moduleLog = log.child({ namespace: 'socketio' }); const nodeDenylist = ['dataURL_image']; @@ -46,6 +44,14 @@ export const addInvocationCompleteListener = () => { }) ); + const [{ payload: imageDTO }] = await take( + imageMetadataReceived.fulfilled.match + ); + + if (getState().gallery.shouldAutoSwitchToNewImages) { + dispatch(imageSelected(imageDTO)); + } + // Handle canvas image if ( graph_execution_state_id === From 0d5f44b1535cf4a476a40f3089dec6ae40cb5dde Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 14:19:32 +1000 Subject: [PATCH 18/31] feat(ui): improve image upload handling --- .../middleware/listenerMiddleware/index.ts | 14 +++++++-- .../listeners/canvasMerged.ts | 3 +- .../listeners/canvasSavedToGallery.ts | 1 - .../listeners/imageUploaded.ts | 29 +++++++++++++++++-- .../listeners/userInvokedCanvas.ts | 18 ++++-------- .../src/common/components/ImageUploader.tsx | 1 - .../frontend/web/src/services/thunks/image.ts | 5 +--- 7 files changed, 45 insertions(+), 26 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index c04a3943f3..d762e73d95 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -8,7 +8,10 @@ import type { TypedStartListening, TypedAddListener } from '@reduxjs/toolkit'; import type { RootState, AppDispatch } from '../../store'; import { addInitialImageSelectedListener } from './listeners/initialImageSelected'; -import { addImageUploadedListener } from './listeners/imageUploaded'; +import { + addImageUploadedFulfilledListener, + addImageUploadedRejectedListener, +} from './listeners/imageUploaded'; import { addRequestedImageDeletionListener } from './listeners/imageDeleted'; import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas'; import { addUserInvokedNodesListener } from './listeners/userInvokedNodes'; @@ -47,23 +50,28 @@ export type AppListenerEffect = ListenerEffect< AppDispatch >; -addImageUploadedListener(); +// Image uploads +addImageUploadedFulfilledListener(); +addImageUploadedRejectedListener(); + addInitialImageSelectedListener(); + addRequestedImageDeletionListener(); +// Invoking stuff addUserInvokedCanvasListener(); addUserInvokedNodesListener(); addUserInvokedTextToImageListener(); addUserInvokedImageToImageListener(); addSessionReadyToInvokeListener(); +// Canvas actions addCanvasSavedToGalleryListener(); addCanvasDownloadedAsImageListener(); addCanvasCopiedToClipboardListener(); addCanvasMergedListener(); // socketio - addGeneratorProgressListener(); addGraphExecutionStateCompleteListener(); addInvocationCompleteListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts index 1e2d99541c..fbc9c9c225 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts @@ -52,7 +52,6 @@ export const addCanvasMergedListener = () => { dispatch( imageUploaded({ - imageType: 'intermediates', formData: { file: new File([blob], filename, { type: 'image/png' }), }, @@ -65,7 +64,7 @@ export const addCanvasMergedListener = () => { action.meta.arg.formData.file.name === filename ); - const mergedCanvasImage = payload.response; + const mergedCanvasImage = payload; dispatch( setMergedCanvas({ diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts index d8237d1d5c..2df3dacea2 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts @@ -29,7 +29,6 @@ export const addCanvasSavedToGalleryListener = () => { dispatch( imageUploaded({ - imageType: 'results', formData: { file: new File([blob], 'mergedCanvas.png', { type: 'image/png' }), }, diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index b37cd3d139..c0ed294850 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -7,17 +7,23 @@ import { initialImageSelected } from 'features/parameters/store/actions'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { resultAdded } from 'features/gallery/store/resultsSlice'; import { isResultsImageDTO, isUploadsImageDTO } from 'services/types/guards'; +import { log } from 'app/logging/useLogger'; -export const addImageUploadedListener = () => { +const moduleLog = log.child({ namespace: 'image' }); + +export const addImageUploadedFulfilledListener = () => { startAppListening({ predicate: (action): action is ReturnType => imageUploaded.fulfilled.match(action) && - action.payload.response.is_intermediate === false, + action.payload.is_intermediate === false, effect: (action, { dispatch, getState }) => { - const { response: image } = action.payload; + const image = action.payload; + + moduleLog.debug({ arg: '', image }, 'Image uploaded'); const state = getState(); + // Handle uploads if (isUploadsImageDTO(image)) { dispatch(uploadAdded(image)); @@ -36,9 +42,26 @@ export const addImageUploadedListener = () => { } } + // Handle results + // TODO: Can this ever happen? I don't think so... if (isResultsImageDTO(image)) { dispatch(resultAdded(image)); } }, }); }; + +export const addImageUploadedRejectedListener = () => { + startAppListening({ + actionCreator: imageUploaded.rejected, + effect: (action, { dispatch }) => { + dispatch( + addToast({ + title: 'Image Upload Failed', + description: action.error.message, + status: 'error', + }) + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts index 46f6efe934..641cbb5ca5 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts @@ -106,19 +106,16 @@ export const addUserInvokedCanvasListener = () => { ); // Wait for the image to be uploaded - const [{ payload: basePayload }] = await take( + const [{ payload: baseImageDTO }] = await take( (action): action is ReturnType => imageUploaded.fulfilled.match(action) && action.meta.arg.formData.file.name === baseFilename ); // Update the base node with the image name and type - const { image_name: baseName, image_type: baseType } = - basePayload.response; - baseNode.image = { - image_name: baseName, - image_type: baseType, + image_name: baseImageDTO.image_name, + image_type: baseImageDTO.image_type, }; } @@ -135,19 +132,16 @@ export const addUserInvokedCanvasListener = () => { ); // Wait for the mask to be uploaded - const [{ payload: maskPayload }] = await take( + const [{ payload: maskImageDTO }] = await take( (action): action is ReturnType => imageUploaded.fulfilled.match(action) && action.meta.arg.formData.file.name === maskFilename ); // Update the base node with the image name and type - const { image_name: maskName, image_type: maskType } = - maskPayload.response; - baseNode.mask = { - image_name: maskName, - image_type: maskType, + image_name: maskImageDTO.image_name, + image_type: maskImageDTO.image_type, }; } diff --git a/invokeai/frontend/web/src/common/components/ImageUploader.tsx b/invokeai/frontend/web/src/common/components/ImageUploader.tsx index db6b9ee517..628d44b6f1 100644 --- a/invokeai/frontend/web/src/common/components/ImageUploader.tsx +++ b/invokeai/frontend/web/src/common/components/ImageUploader.tsx @@ -68,7 +68,6 @@ const ImageUploader = (props: ImageUploaderProps) => { async (file: File) => { dispatch( imageUploaded({ - imageType: 'uploads', formData: { file }, activeTabName, }) diff --git a/invokeai/frontend/web/src/services/thunks/image.ts b/invokeai/frontend/web/src/services/thunks/image.ts index 34b369e3eb..17176ac09b 100644 --- a/invokeai/frontend/web/src/services/thunks/image.ts +++ b/invokeai/frontend/web/src/services/thunks/image.ts @@ -53,11 +53,8 @@ export const imageUploaded = createAppAsyncThunk( // strip out `activeTabName` from arg - the route does not need it const { activeTabName, ...rest } = arg; const response = await ImagesService.uploadImage(rest); - const { location } = getHeaders(response); - imagesLog.debug({ arg: '', response, location }, 'Image uploaded'); - - return { response, location }; + return response; } ); From 6059db4f158268e5cd280f4ee45fdafee79851aa Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 14:31:32 +1000 Subject: [PATCH 19/31] feat(ui): improve image delete handling --- .../middleware/listenerMiddleware/index.ts | 13 ++++- .../listeners/imageDeleted.ts | 56 +++++++++++++++++-- .../features/gallery/store/resultsSlice.ts | 12 ---- .../features/gallery/store/uploadsSlice.ts | 12 ---- .../frontend/web/src/services/thunks/image.ts | 5 +- 5 files changed, 63 insertions(+), 35 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index d762e73d95..9dc25b262a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -12,7 +12,12 @@ import { addImageUploadedFulfilledListener, addImageUploadedRejectedListener, } from './listeners/imageUploaded'; -import { addRequestedImageDeletionListener } from './listeners/imageDeleted'; +import { + addImageDeletedFulfilledListener, + addImageDeletedPendingListener, + addImageDeletedRejectedListener, + addRequestedImageDeletionListener, +} from './listeners/imageDeleted'; import { addUserInvokedCanvasListener } from './listeners/userInvokedCanvas'; import { addUserInvokedNodesListener } from './listeners/userInvokedNodes'; import { addUserInvokedTextToImageListener } from './listeners/userInvokedTextToImage'; @@ -50,13 +55,17 @@ export type AppListenerEffect = ListenerEffect< AppDispatch >; -// Image uploads +// Image uploaded addImageUploadedFulfilledListener(); addImageUploadedRejectedListener(); addInitialImageSelectedListener(); +// Image deleted addRequestedImageDeletionListener(); +addImageDeletedPendingListener(); +addImageDeletedFulfilledListener(); +addImageDeletedRejectedListener(); // Invoking stuff addUserInvokedCanvasListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts index 42a62b3d80..804c713fc5 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts @@ -4,9 +4,14 @@ import { imageDeleted } from 'services/thunks/image'; import { log } from 'app/logging/useLogger'; import { clamp } from 'lodash-es'; import { imageSelected } from 'features/gallery/store/gallerySlice'; +import { uploadsAdapter } from 'features/gallery/store/uploadsSlice'; +import { resultsAdapter } from 'features/gallery/store/resultsSlice'; const moduleLog = log.child({ namespace: 'addRequestedImageDeletionListener' }); +/** + * Called when the user requests an image deletion + */ export const addRequestedImageDeletionListener = () => { startAppListening({ actionCreator: requestedImageDeletion, @@ -19,11 +24,6 @@ export const addRequestedImageDeletionListener = () => { const { image_name, image_type } = image; - if (image_type !== 'uploads' && image_type !== 'results') { - moduleLog.warn({ data: image }, `Invalid image type ${image_type}`); - return; - } - const selectedImageName = getState().gallery.selectedImage?.image_name; if (selectedImageName === image_name) { @@ -57,3 +57,49 @@ export const addRequestedImageDeletionListener = () => { }, }); }; + +/** + * Called when the actual delete request is sent to the server + */ +export const addImageDeletedPendingListener = () => { + startAppListening({ + actionCreator: imageDeleted.pending, + effect: (action, { dispatch, getState }) => { + const { imageName, imageType } = action.meta.arg; + // Preemptively remove the image from the gallery + if (imageType === 'uploads') { + uploadsAdapter.removeOne(getState().uploads, imageName); + } + if (imageType === 'results') { + resultsAdapter.removeOne(getState().results, imageName); + } + }, + }); +}; + +/** + * Called on successful delete + */ +export const addImageDeletedFulfilledListener = () => { + startAppListening({ + actionCreator: imageDeleted.fulfilled, + effect: (action, { dispatch, getState }) => { + moduleLog.info({ data: { image: action.meta.arg } }, 'Image deleted'); + }, + }); +}; + +/** + * Called on failed delete + */ +export const addImageDeletedRejectedListener = () => { + startAppListening({ + actionCreator: imageDeleted.rejected, + effect: (action, { dispatch, getState }) => { + moduleLog.warn( + { data: { image: action.meta.arg } }, + 'Unable to delete image' + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts index 125f4ff5d5..87d17e6ee8 100644 --- a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts @@ -97,18 +97,6 @@ const resultsSlice = createSlice({ }); } }); - - /** - * Delete Image - PENDING - * Pre-emptively remove the image from the gallery - */ - builder.addCase(imageDeleted.pending, (state, action) => { - const { imageType, imageName } = action.meta.arg; - - if (imageType === 'results') { - resultsAdapter.removeOne(state, imageName); - } - }); }, }); diff --git a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts index 5e458503ec..3e1abc9bb4 100644 --- a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts @@ -83,18 +83,6 @@ const uploadsSlice = createSlice({ }); } }); - - /** - * Delete Image - pending - * Pre-emptively remove the image from the gallery - */ - builder.addCase(imageDeleted.pending, (state, action) => { - const { imageType, imageName } = action.meta.arg; - - if (imageType === 'uploads') { - uploadsAdapter.removeOne(state, imageName); - } - }); }, }); diff --git a/invokeai/frontend/web/src/services/thunks/image.ts b/invokeai/frontend/web/src/services/thunks/image.ts index 17176ac09b..00add17d41 100644 --- a/invokeai/frontend/web/src/services/thunks/image.ts +++ b/invokeai/frontend/web/src/services/thunks/image.ts @@ -67,9 +67,6 @@ export const imageDeleted = createAppAsyncThunk( 'api/imageDeleted', async (arg: ImageDeletedArg) => { const response = await ImagesService.deleteImage(arg); - - imagesLog.debug({ arg, response }, 'Image deleted'); - return response; } ); @@ -77,7 +74,7 @@ export const imageDeleted = createAppAsyncThunk( type ImageUpdatedArg = Parameters<(typeof ImagesService)['updateImage']>[0]; /** - * `ImagesService.deleteImage()` thunk + * `ImagesService.updateImage()` thunk */ export const imageUpdated = createAppAsyncThunk( 'api/imageUpdated', From b3f71b307826b3f4c1c38730d1c43fbe8cfbb3dd Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 14:42:11 +1000 Subject: [PATCH 20/31] feat(ui): improve image metadata handling --- .../middleware/listenerMiddleware/index.ts | 8 +++ .../listeners/imageMetadataReceived.ts | 49 +++++++++++++++++++ .../features/gallery/store/resultsSlice.ts | 11 ----- .../frontend/web/src/services/thunks/image.ts | 3 +- 4 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index 9dc25b262a..be3a798015 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -36,6 +36,10 @@ import { addSocketDisconnectedListener } from './listeners/socketio/socketDiscon import { addSocketSubscribedListener } from './listeners/socketio/socketSubscribed'; import { addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed'; import { addSessionReadyToInvokeListener } from './listeners/sessionReadyToInvoke'; +import { + addImageMetadataReceivedFulfilledListener, + addImageMetadataReceivedRejectedListener, +} from './listeners/imageMetadataReceived'; export const listenerMiddleware = createListenerMiddleware(); @@ -67,6 +71,10 @@ addImageDeletedPendingListener(); addImageDeletedFulfilledListener(); addImageDeletedRejectedListener(); +// Image metadata +addImageMetadataReceivedFulfilledListener(); +addImageMetadataReceivedRejectedListener(); + // Invoking stuff addUserInvokedCanvasListener(); addUserInvokedNodesListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts new file mode 100644 index 0000000000..417b7c49cf --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts @@ -0,0 +1,49 @@ +import { log } from 'app/logging/useLogger'; +import { startAppListening } from '..'; +import { imageMetadataReceived } from 'services/thunks/image'; +import { + ResultsImageDTO, + resultsAdapter, +} from 'features/gallery/store/resultsSlice'; +import { + UploadsImageDTO, + uploadsAdapter, +} from 'features/gallery/store/uploadsSlice'; + +const moduleLog = log.child({ namespace: 'image' }); + +export const addImageMetadataReceivedFulfilledListener = () => { + startAppListening({ + actionCreator: imageMetadataReceived.fulfilled, + effect: (action, { getState, dispatch }) => { + const image = action.payload; + moduleLog.debug({ data: { image } }, 'Image metadata received'); + + if (image.image_type === 'results') { + resultsAdapter.upsertOne( + getState().results, + action.payload as ResultsImageDTO + ); + } + + if (image.image_type === 'uploads') { + uploadsAdapter.upsertOne( + getState().uploads, + action.payload as UploadsImageDTO + ); + } + }, + }); +}; + +export const addImageMetadataReceivedRejectedListener = () => { + startAppListening({ + actionCreator: imageMetadataReceived.rejected, + effect: (action, { getState, dispatch }) => { + moduleLog.debug( + { data: { image: action.meta.arg } }, + 'Problem receiving image metadata' + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts index 87d17e6ee8..0a0ba01c40 100644 --- a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts @@ -69,17 +69,6 @@ const resultsSlice = createSlice({ state.isLoading = false; }); - /** - * Image Metadata Received - FULFILLED - */ - builder.addCase(imageMetadataReceived.fulfilled, (state, action) => { - const { image_type } = action.payload; - - if (image_type === 'results') { - resultsAdapter.upsertOne(state, action.payload as ResultsImageDTO); - } - }); - /** * Image URLs Received - FULFILLED */ diff --git a/invokeai/frontend/web/src/services/thunks/image.ts b/invokeai/frontend/web/src/services/thunks/image.ts index 00add17d41..4326b82811 100644 --- a/invokeai/frontend/web/src/services/thunks/image.ts +++ b/invokeai/frontend/web/src/services/thunks/image.ts @@ -2,7 +2,6 @@ import { log } from 'app/logging/useLogger'; import { createAppAsyncThunk } from 'app/store/storeUtils'; import { InvokeTabName } from 'features/ui/store/tabMap'; import { ImagesService } from 'services/api'; -import { getHeaders } from 'services/util/getHeaders'; const imagesLog = log.child({ namespace: 'image' }); @@ -81,7 +80,7 @@ export const imageUpdated = createAppAsyncThunk( async (arg: ImageUpdatedArg) => { const response = await ImagesService.updateImage(arg); - imagesLog.debug({ arg, response }, 'Image updated'); + imagesLog.debug({ data: { arg, response } }, 'Image updated'); return response; } From 1d4d705795e591fdce8b4af49a93cd8dc4db5821 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 14:50:47 +1000 Subject: [PATCH 21/31] feat(ui): improve image urls handling --- .../middleware/listenerMiddleware/index.ts | 8 +++ .../listeners/imageUrlsReceived.ts | 51 +++++++++++++++++++ .../features/gallery/store/resultsSlice.ts | 23 --------- .../features/gallery/store/uploadsSlice.ts | 19 ------- .../frontend/web/src/services/thunks/image.ts | 9 ---- 5 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUrlsReceived.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index be3a798015..8467b24c08 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -40,6 +40,10 @@ import { addImageMetadataReceivedFulfilledListener, addImageMetadataReceivedRejectedListener, } from './listeners/imageMetadataReceived'; +import { + addImageUrlsReceivedFulfilledListener, + addImageUrlsReceivedRejectedListener, +} from './listeners/imageUrlsReceived'; export const listenerMiddleware = createListenerMiddleware(); @@ -75,6 +79,10 @@ addImageDeletedRejectedListener(); addImageMetadataReceivedFulfilledListener(); addImageMetadataReceivedRejectedListener(); +// Image URLs +addImageUrlsReceivedFulfilledListener(); +addImageUrlsReceivedRejectedListener(); + // Invoking stuff addUserInvokedCanvasListener(); addUserInvokedNodesListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUrlsReceived.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUrlsReceived.ts new file mode 100644 index 0000000000..4ff2a02118 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUrlsReceived.ts @@ -0,0 +1,51 @@ +import { log } from 'app/logging/useLogger'; +import { startAppListening } from '..'; +import { imageUrlsReceived } from 'services/thunks/image'; +import { resultsAdapter } from 'features/gallery/store/resultsSlice'; +import { uploadsAdapter } from 'features/gallery/store/uploadsSlice'; + +const moduleLog = log.child({ namespace: 'image' }); + +export const addImageUrlsReceivedFulfilledListener = () => { + startAppListening({ + actionCreator: imageUrlsReceived.fulfilled, + effect: (action, { getState, dispatch }) => { + const image = action.payload; + moduleLog.debug({ data: { image } }, 'Image URLs received'); + + const { image_type, image_name, image_url, thumbnail_url } = image; + + if (image_type === 'results') { + resultsAdapter.updateOne(getState().results, { + id: image_name, + changes: { + image_url, + thumbnail_url, + }, + }); + } + + if (image_type === 'uploads') { + uploadsAdapter.updateOne(getState().uploads, { + id: image_name, + changes: { + image_url, + thumbnail_url, + }, + }); + } + }, + }); +}; + +export const addImageUrlsReceivedRejectedListener = () => { + startAppListening({ + actionCreator: imageUrlsReceived.rejected, + effect: (action, { getState, dispatch }) => { + moduleLog.debug( + { data: { image: action.meta.arg } }, + 'Problem getting image URLs' + ); + }, + }); +}; diff --git a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts index 0a0ba01c40..18d0466033 100644 --- a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts @@ -4,11 +4,6 @@ import { receivedResultImagesPage, IMAGES_PER_PAGE, } from 'services/thunks/gallery'; -import { - imageDeleted, - imageMetadataReceived, - imageUrlsReceived, -} from 'services/thunks/image'; import { ImageDTO } from 'services/api'; import { dateComparator } from 'common/util/dateComparator'; @@ -68,24 +63,6 @@ const resultsSlice = createSlice({ state.nextPage = items.length < IMAGES_PER_PAGE ? page : page + 1; state.isLoading = false; }); - - /** - * Image URLs Received - FULFILLED - */ - builder.addCase(imageUrlsReceived.fulfilled, (state, action) => { - const { image_name, image_type, image_url, thumbnail_url } = - action.payload; - - if (image_type === 'results') { - resultsAdapter.updateOne(state, { - id: image_name, - changes: { - image_url: image_url, - thumbnail_url: thumbnail_url, - }, - }); - } - }); }, }); diff --git a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts index 3e1abc9bb4..1a03ed3391 100644 --- a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts @@ -5,7 +5,6 @@ import { receivedUploadImagesPage, IMAGES_PER_PAGE, } from 'services/thunks/gallery'; -import { imageDeleted, imageUrlsReceived } from 'services/thunks/image'; import { ImageDTO } from 'services/api'; import { dateComparator } from 'common/util/dateComparator'; @@ -65,24 +64,6 @@ const uploadsSlice = createSlice({ state.nextPage = items.length < IMAGES_PER_PAGE ? page : page + 1; state.isLoading = false; }); - - /** - * Image URLs Received - FULFILLED - */ - builder.addCase(imageUrlsReceived.fulfilled, (state, action) => { - const { image_name, image_type, image_url, thumbnail_url } = - action.payload; - - if (image_type === 'uploads') { - uploadsAdapter.updateOne(state, { - id: image_name, - changes: { - image_url: image_url, - thumbnail_url: thumbnail_url, - }, - }); - } - }); }, }); diff --git a/invokeai/frontend/web/src/services/thunks/image.ts b/invokeai/frontend/web/src/services/thunks/image.ts index 4326b82811..f0c0456202 100644 --- a/invokeai/frontend/web/src/services/thunks/image.ts +++ b/invokeai/frontend/web/src/services/thunks/image.ts @@ -1,10 +1,7 @@ -import { log } from 'app/logging/useLogger'; import { createAppAsyncThunk } from 'app/store/storeUtils'; import { InvokeTabName } from 'features/ui/store/tabMap'; import { ImagesService } from 'services/api'; -const imagesLog = log.child({ namespace: 'image' }); - type imageUrlsReceivedArg = Parameters< (typeof ImagesService)['getImageUrls'] >[0]; @@ -16,7 +13,6 @@ export const imageUrlsReceived = createAppAsyncThunk( 'api/imageUrlsReceived', async (arg: imageUrlsReceivedArg) => { const response = await ImagesService.getImageUrls(arg); - imagesLog.info({ arg, response }, 'Received image urls'); return response; } ); @@ -32,7 +28,6 @@ export const imageMetadataReceived = createAppAsyncThunk( 'api/imageMetadataReceived', async (arg: imageMetadataReceivedArg) => { const response = await ImagesService.getImageMetadata(arg); - imagesLog.info({ arg, response }, 'Received image record'); return response; } ); @@ -52,7 +47,6 @@ export const imageUploaded = createAppAsyncThunk( // strip out `activeTabName` from arg - the route does not need it const { activeTabName, ...rest } = arg; const response = await ImagesService.uploadImage(rest); - return response; } ); @@ -79,9 +73,6 @@ export const imageUpdated = createAppAsyncThunk( 'api/imageUpdated', async (arg: ImageUpdatedArg) => { const response = await ImagesService.updateImage(arg); - - imagesLog.debug({ data: { arg, response } }, 'Image updated'); - return response; } ); From 8f190169db49c617494f7723dae0a3b2b955958e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 15:53:08 +1000 Subject: [PATCH 22/31] feat(ui): improve session creation handling --- invokeai/frontend/web/public/locales/en.json | 1 + .../middleware/listenerMiddleware/index.ts | 10 +++ .../listeners/sessionCreated.ts | 45 ++++++++++++++ .../src/features/system/store/systemSlice.ts | 25 +++++++- .../web/src/services/events/middleware.ts | 11 +--- .../web/src/services/thunks/session.ts | 61 ++++++------------- 6 files changed, 99 insertions(+), 54 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 94dff3934a..b96175c25d 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -546,6 +546,7 @@ "availableSchedulers": "Available Schedulers" }, "toast": { + "problemCreatingSession": "Problem Creating Session", "serverError": "Server Error", "disconnected": "Disconnected from Server", "connected": "Connected to Server", diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index 8467b24c08..5c726c317f 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -44,6 +44,11 @@ import { addImageUrlsReceivedFulfilledListener, addImageUrlsReceivedRejectedListener, } from './listeners/imageUrlsReceived'; +import { + addSessionCreatedFulfilledListener, + addSessionCreatedPendingListener, + addSessionCreatedRejectedListener, +} from './listeners/sessionCreated'; export const listenerMiddleware = createListenerMiddleware(); @@ -106,3 +111,8 @@ addSocketConnectedListener(); addSocketDisconnectedListener(); addSocketSubscribedListener(); addSocketUnsubscribedListener(); + +// Sessions +addSessionCreatedPendingListener(); +addSessionCreatedFulfilledListener(); +addSessionCreatedRejectedListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts new file mode 100644 index 0000000000..e960510d0f --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts @@ -0,0 +1,45 @@ +import { log } from 'app/logging/useLogger'; +import { startAppListening } from '..'; +import { sessionCreated } from 'services/thunks/session'; +import { serializeError } from 'serialize-error'; + +const moduleLog = log.child({ namespace: 'session' }); + +export const addSessionCreatedPendingListener = () => { + startAppListening({ + actionCreator: sessionCreated.pending, + effect: (action, { getState, dispatch }) => { + // + }, + }); +}; + +export const addSessionCreatedFulfilledListener = () => { + startAppListening({ + actionCreator: sessionCreated.fulfilled, + effect: (action, { getState, dispatch }) => { + const session = action.payload; + moduleLog.info({ data: { session } }, `Session created (${session.id})`); + }, + }); +}; + +export const addSessionCreatedRejectedListener = () => { + startAppListening({ + actionCreator: sessionCreated.rejected, + effect: (action, { getState, dispatch }) => { + if (action.payload) { + const { arg, error } = action.payload; + moduleLog.error( + { + data: { + arg, + error: serializeError(error), + }, + }, + `Problem creating session` + ); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 06fa0e47d8..8845e0b843 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -16,7 +16,11 @@ import { import { ProgressImage } from 'services/events/types'; import { makeToast } from '../../../app/components/Toaster'; -import { sessionCanceled, sessionInvoked } from 'services/thunks/session'; +import { + sessionCanceled, + sessionCreated, + sessionInvoked, +} from 'services/thunks/session'; import { receivedModels } from 'services/thunks/model'; import { parsedOpenAPISchema } from 'features/nodes/store/nodesSlice'; import { LogLevelName } from 'roarr'; @@ -353,7 +357,7 @@ export const systemSlice = createSlice({ }); /** - * Session Canceled + * Session Canceled - FULFILLED */ builder.addCase(sessionCanceled.fulfilled, (state, action) => { state.canceledSession = action.meta.arg.sessionId; @@ -370,6 +374,23 @@ export const systemSlice = createSlice({ ); }); + /** + * Session Created - REJECTED + */ + builder.addCase(sessionCreated.rejected, (state, action) => { + state.isProcessing = false; + state.isCancelable = false; + state.isCancelScheduled = false; + state.currentStep = 0; + state.totalSteps = 0; + state.statusTranslationKey = 'common.statusConnected'; + state.progressImage = null; + + state.toastQueue.push( + makeToast({ title: t('toast.problemCreatingSession'), status: 'error' }) + ); + }); + /** * Session Canceled */ diff --git a/invokeai/frontend/web/src/services/events/middleware.ts b/invokeai/frontend/web/src/services/events/middleware.ts index a78e0de97b..f1eb844f2c 100644 --- a/invokeai/frontend/web/src/services/events/middleware.ts +++ b/invokeai/frontend/web/src/services/events/middleware.ts @@ -8,11 +8,7 @@ import { import { socketSubscribed, socketUnsubscribed } from './actions'; import { AppThunkDispatch, RootState } from 'app/store/store'; import { getTimestamp } from 'common/util/getTimestamp'; -import { - sessionInvoked, - sessionCreated, - sessionWithoutGraphCreated, -} from 'services/thunks/session'; +import { sessionCreated } from 'services/thunks/session'; import { OpenAPI } from 'services/api'; import { setEventListeners } from 'services/events/util/setEventListeners'; import { log } from 'app/logging/useLogger'; @@ -66,10 +62,7 @@ export const socketMiddleware = () => { socket.connect(); } - if ( - sessionCreated.fulfilled.match(action) || - sessionWithoutGraphCreated.fulfilled.match(action) - ) { + if (sessionCreated.fulfilled.match(action)) { const sessionId = action.payload.id; const oldSessionId = getState().system.sessionId; diff --git a/invokeai/frontend/web/src/services/thunks/session.ts b/invokeai/frontend/web/src/services/thunks/session.ts index a1ee5a34ed..546789d5b7 100644 --- a/invokeai/frontend/web/src/services/thunks/session.ts +++ b/invokeai/frontend/web/src/services/thunks/session.ts @@ -1,7 +1,6 @@ import { createAppAsyncThunk } from 'app/store/storeUtils'; -import { SessionsService } from 'services/api'; +import { GraphExecutionState, SessionsService } from 'services/api'; import { log } from 'app/logging/useLogger'; -import { serializeError } from 'serialize-error'; const sessionLog = log.child({ namespace: 'session' }); @@ -11,51 +10,27 @@ type SessionCreatedArg = { >[0]['requestBody']; }; +type SessionCreatedThunkConfig = { + rejectValue: { arg: SessionCreatedArg; error: unknown }; +}; + /** * `SessionsService.createSession()` thunk */ -export const sessionCreated = createAppAsyncThunk( - 'api/sessionCreated', - async (arg: SessionCreatedArg, { rejectWithValue }) => { - try { - const response = await SessionsService.createSession({ - requestBody: arg.graph, - }); - sessionLog.info({ arg, response }, `Session created (${response.id})`); - return response; - } catch (err: any) { - sessionLog.error( - { - error: serializeError(err), - }, - 'Problem creating session' - ); - return rejectWithValue(err.message); - } +export const sessionCreated = createAppAsyncThunk< + GraphExecutionState, + SessionCreatedArg, + SessionCreatedThunkConfig +>('api/sessionCreated', async (arg: SessionCreatedArg, { rejectWithValue }) => { + try { + const response = await SessionsService.createSession({ + requestBody: arg.graph, + }); + return response; + } catch (error) { + return rejectWithValue({ arg, error }); } -); - -/** - * `SessionsService.createSession()` without graph thunk - */ -export const sessionWithoutGraphCreated = createAppAsyncThunk( - 'api/sessionWithoutGraphCreated', - async (_, { rejectWithValue }) => { - try { - const response = await SessionsService.createSession({}); - sessionLog.info({ response }, `Session created (${response.id})`); - return response; - } catch (err: any) { - sessionLog.error( - { - error: serializeError(err), - }, - 'Problem creating session' - ); - return rejectWithValue(err.message); - } - } -); +}); type NodeAddedArg = Parameters<(typeof SessionsService)['addNode']>[0]; From b599c40099dfc42b40fba123fb8d9bf4e0cde86f Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:17:12 +1000 Subject: [PATCH 23/31] feat(ui): improve session invoked handling --- invokeai/frontend/web/public/locales/en.json | 1 - .../middleware/listenerMiddleware/index.ts | 12 ++- .../listeners/sessionInvoked.ts | 45 ++++++++++ .../listeners/sessionReadyToInvoke.ts | 7 +- .../src/features/system/store/systemSlice.ts | 51 +++++------ .../web/src/services/thunks/session.ts | 90 ++++++------------- 6 files changed, 115 insertions(+), 91 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index b96175c25d..94dff3934a 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -546,7 +546,6 @@ "availableSchedulers": "Available Schedulers" }, "toast": { - "problemCreatingSession": "Problem Creating Session", "serverError": "Server Error", "disconnected": "Disconnected from Server", "connected": "Connected to Server", diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index 5c726c317f..dae56804af 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -49,6 +49,11 @@ import { addSessionCreatedPendingListener, addSessionCreatedRejectedListener, } from './listeners/sessionCreated'; +import { + addSessionInvokedFulfilledListener, + addSessionInvokedPendingListener, + addSessionInvokedRejectedListener, +} from './listeners/sessionInvoked'; export const listenerMiddleware = createListenerMiddleware(); @@ -88,13 +93,18 @@ addImageMetadataReceivedRejectedListener(); addImageUrlsReceivedFulfilledListener(); addImageUrlsReceivedRejectedListener(); -// Invoking stuff +// Invoking on tabs addUserInvokedCanvasListener(); addUserInvokedNodesListener(); addUserInvokedTextToImageListener(); addUserInvokedImageToImageListener(); addSessionReadyToInvokeListener(); +// Actual session invoking +addSessionInvokedPendingListener(); +addSessionInvokedFulfilledListener(); +addSessionInvokedRejectedListener(); + // Canvas actions addCanvasSavedToGalleryListener(); addCanvasDownloadedAsImageListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts new file mode 100644 index 0000000000..bddadbc359 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts @@ -0,0 +1,45 @@ +import { log } from 'app/logging/useLogger'; +import { startAppListening } from '..'; +import { sessionInvoked } from 'services/thunks/session'; +import { serializeError } from 'serialize-error'; + +const moduleLog = log.child({ namespace: 'session' }); + +export const addSessionInvokedPendingListener = () => { + startAppListening({ + actionCreator: sessionInvoked.pending, + effect: (action, { getState, dispatch }) => { + // + }, + }); +}; + +export const addSessionInvokedFulfilledListener = () => { + startAppListening({ + actionCreator: sessionInvoked.fulfilled, + effect: (action, { getState, dispatch }) => { + const { sessionId } = action.meta.arg; + moduleLog.info({ data: { sessionId } }, `Session invoked (${sessionId})`); + }, + }); +}; + +export const addSessionInvokedRejectedListener = () => { + startAppListening({ + actionCreator: sessionInvoked.rejected, + effect: (action, { getState, dispatch }) => { + if (action.payload) { + const { arg, error } = action.payload; + moduleLog.error( + { + data: { + arg, + error: serializeError(error), + }, + }, + `Problem invoking session` + ); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts index eb65017a25..4fae2d96c0 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts @@ -3,7 +3,7 @@ import { sessionInvoked } from 'services/thunks/session'; import { log } from 'app/logging/useLogger'; import { sessionReadyToInvoke } from 'features/system/store/actions'; -const moduleLog = log.child({ namespace: 'invoke' }); +const moduleLog = log.child({ namespace: 'session' }); export const addSessionReadyToInvokeListener = () => { startAppListening({ @@ -11,7 +11,10 @@ export const addSessionReadyToInvokeListener = () => { effect: (action, { getState, dispatch }) => { const { sessionId } = getState().system; if (sessionId) { - moduleLog.info({ sessionId }, `Session invoked (${sessionId})})`); + moduleLog.info( + { sessionId }, + `Session ready to invoke (${sessionId})})` + ); dispatch(sessionInvoked({ sessionId })); } }, diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 8845e0b843..403fd60501 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -1,5 +1,5 @@ import { UseToastOptions } from '@chakra-ui/react'; -import type { PayloadAction } from '@reduxjs/toolkit'; +import { PayloadAction, isAnyOf } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import * as InvokeAI from 'app/types/invokeai'; import { @@ -349,13 +349,6 @@ export const systemSlice = createSlice({ state.statusTranslationKey = 'common.statusPreparing'; }); - builder.addCase(sessionInvoked.rejected, (state, action) => { - const error = action.payload as string | undefined; - state.toastQueue.push( - makeToast({ title: error || t('toast.serverError'), status: 'error' }) - ); - }); - /** * Session Canceled - FULFILLED */ @@ -374,23 +367,6 @@ export const systemSlice = createSlice({ ); }); - /** - * Session Created - REJECTED - */ - builder.addCase(sessionCreated.rejected, (state, action) => { - state.isProcessing = false; - state.isCancelable = false; - state.isCancelScheduled = false; - state.currentStep = 0; - state.totalSteps = 0; - state.statusTranslationKey = 'common.statusConnected'; - state.progressImage = null; - - state.toastQueue.push( - makeToast({ title: t('toast.problemCreatingSession'), status: 'error' }) - ); - }); - /** * Session Canceled */ @@ -437,6 +413,26 @@ export const systemSlice = createSlice({ builder.addCase(imageUploaded.fulfilled, (state) => { state.isUploading = false; }); + + // *** Matchers - must be after all cases *** + + /** + * Session Invoked - REJECTED + * Session Created - REJECTED + */ + builder.addMatcher(isAnySessionRejected, (state, action) => { + state.isProcessing = false; + state.isCancelable = false; + state.isCancelScheduled = false; + state.currentStep = 0; + state.totalSteps = 0; + state.statusTranslationKey = 'common.statusConnected'; + state.progressImage = null; + + state.toastQueue.push( + makeToast({ title: t('toast.serverError'), status: 'error' }) + ); + }); }, }); @@ -465,3 +461,8 @@ export const { } = systemSlice.actions; export default systemSlice.reducer; + +const isAnySessionRejected = isAnyOf( + sessionCreated.rejected, + sessionInvoked.rejected +); diff --git a/invokeai/frontend/web/src/services/thunks/session.ts b/invokeai/frontend/web/src/services/thunks/session.ts index 546789d5b7..efab401520 100644 --- a/invokeai/frontend/web/src/services/thunks/session.ts +++ b/invokeai/frontend/web/src/services/thunks/session.ts @@ -1,6 +1,7 @@ import { createAppAsyncThunk } from 'app/store/storeUtils'; import { GraphExecutionState, SessionsService } from 'services/api'; import { log } from 'app/logging/useLogger'; +import { isObject } from 'lodash-es'; const sessionLog = log.child({ namespace: 'session' }); @@ -21,7 +22,7 @@ export const sessionCreated = createAppAsyncThunk< GraphExecutionState, SessionCreatedArg, SessionCreatedThunkConfig ->('api/sessionCreated', async (arg: SessionCreatedArg, { rejectWithValue }) => { +>('api/sessionCreated', async (arg, { rejectWithValue }) => { try { const response = await SessionsService.createSession({ requestBody: arg.graph, @@ -32,76 +33,41 @@ export const sessionCreated = createAppAsyncThunk< } }); -type NodeAddedArg = Parameters<(typeof SessionsService)['addNode']>[0]; +type SessionInvokedArg = { sessionId: string }; -/** - * `SessionsService.addNode()` thunk - */ -export const nodeAdded = createAppAsyncThunk( - 'api/nodeAdded', - async ( - arg: { node: NodeAddedArg['requestBody']; sessionId: string }, - _thunkApi - ) => { - const response = await SessionsService.addNode({ - requestBody: arg.node, - sessionId: arg.sessionId, - }); +type SessionInvokedThunkConfig = { + rejectValue: { + arg: SessionInvokedArg; + error: unknown; + }; +}; - sessionLog.info({ arg, response }, `Node added (${response})`); - - return response; - } -); - -type NodeUpdatedArg = Parameters<(typeof SessionsService)['updateNode']>[0]; - -/** - * `SessionsService.addNode()` thunk - */ -export const nodeUpdated = createAppAsyncThunk( - 'api/nodeUpdated', - async ( - arg: { node: NodeUpdatedArg['requestBody']; sessionId: string }, - _thunkApi - ) => { - const response = await SessionsService.updateNode({ - requestBody: arg.node, - sessionId: arg.sessionId, - nodePath: arg.node.id, - }); - - sessionLog.info({ arg, response }, `Node updated (${response})`); - - return response; - } -); +const isErrorWithStatus = (error: unknown): error is { status: number } => + isObject(error) && 'status' in error; /** * `SessionsService.invokeSession()` thunk */ -export const sessionInvoked = createAppAsyncThunk( - 'api/sessionInvoked', - async (arg: { sessionId: string }, { rejectWithValue }) => { - const { sessionId } = arg; +export const sessionInvoked = createAppAsyncThunk< + any, + SessionInvokedArg, + SessionInvokedThunkConfig +>('api/sessionInvoked', async (arg, { rejectWithValue }) => { + const { sessionId } = arg; - try { - const response = await SessionsService.invokeSession({ - sessionId, - all: true, - }); - sessionLog.info({ arg, response }, `Session invoked (${sessionId})`); - - return response; - } catch (error) { - const err = error as any; - if (err.status === 403) { - return rejectWithValue(err.body.detail); - } - throw error; + try { + const response = await SessionsService.invokeSession({ + sessionId, + all: true, + }); + return response; + } catch (error) { + if (isErrorWithStatus(error) && error.status === 403) { + return rejectWithValue({ arg, error: (error as any).body.detail }); } + return rejectWithValue({ arg, error }); } -); +}); type SessionCanceledArg = Parameters< (typeof SessionsService)['cancelSessionInvoke'] From 30e0033ebe8955317cc962b93549a9c44031ea28 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:23:18 +1000 Subject: [PATCH 24/31] fix(ui): fix results not added to gallery --- .../listeners/imageMetadataReceived.ts | 16 ++++++---------- .../listeners/imageUploaded.ts | 8 ++++---- .../src/features/gallery/store/resultsSlice.ts | 4 ++-- .../src/features/gallery/store/uploadsSlice.ts | 4 ++-- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts index 417b7c49cf..aa386a2f7f 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts @@ -3,11 +3,11 @@ import { startAppListening } from '..'; import { imageMetadataReceived } from 'services/thunks/image'; import { ResultsImageDTO, - resultsAdapter, + resultUpserted, } from 'features/gallery/store/resultsSlice'; import { UploadsImageDTO, - uploadsAdapter, + uploadUpserted, } from 'features/gallery/store/uploadsSlice'; const moduleLog = log.child({ namespace: 'image' }); @@ -20,17 +20,13 @@ export const addImageMetadataReceivedFulfilledListener = () => { moduleLog.debug({ data: { image } }, 'Image metadata received'); if (image.image_type === 'results') { - resultsAdapter.upsertOne( - getState().results, - action.payload as ResultsImageDTO - ); + console.log('upsert results'); + dispatch(resultUpserted(action.payload as ResultsImageDTO)); } if (image.image_type === 'uploads') { - uploadsAdapter.upsertOne( - getState().uploads, - action.payload as UploadsImageDTO - ); + console.log('upsert uploads'); + dispatch(uploadUpserted(action.payload as UploadsImageDTO)); } }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index c0ed294850..5b177eae91 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -1,11 +1,11 @@ import { startAppListening } from '..'; -import { uploadAdded } from 'features/gallery/store/uploadsSlice'; +import { uploadUpserted } from 'features/gallery/store/uploadsSlice'; import { imageSelected } from 'features/gallery/store/gallerySlice'; import { imageUploaded } from 'services/thunks/image'; import { addToast } from 'features/system/store/systemSlice'; import { initialImageSelected } from 'features/parameters/store/actions'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; -import { resultAdded } from 'features/gallery/store/resultsSlice'; +import { resultUpserted } from 'features/gallery/store/resultsSlice'; import { isResultsImageDTO, isUploadsImageDTO } from 'services/types/guards'; import { log } from 'app/logging/useLogger'; @@ -25,7 +25,7 @@ export const addImageUploadedFulfilledListener = () => { // Handle uploads if (isUploadsImageDTO(image)) { - dispatch(uploadAdded(image)); + dispatch(uploadUpserted(image)); dispatch(addToast({ title: 'Image Uploaded', status: 'success' })); @@ -45,7 +45,7 @@ export const addImageUploadedFulfilledListener = () => { // Handle results // TODO: Can this ever happen? I don't think so... if (isResultsImageDTO(image)) { - dispatch(resultAdded(image)); + dispatch(resultUpserted(image)); } }, }); diff --git a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts index 18d0466033..e601af0443 100644 --- a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts @@ -37,7 +37,7 @@ const resultsSlice = createSlice({ name: 'results', initialState: initialResultsState, reducers: { - resultAdded: resultsAdapter.upsertOne, + resultUpserted: resultsAdapter.upsertOne, }, extraReducers: (builder) => { /** @@ -74,6 +74,6 @@ export const { selectTotal: selectResultsTotal, } = resultsAdapter.getSelectors((state) => state.results); -export const { resultAdded } = resultsSlice.actions; +export const { resultUpserted } = resultsSlice.actions; export default resultsSlice.reducer; diff --git a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts index 1a03ed3391..599e1b3830 100644 --- a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts @@ -38,7 +38,7 @@ const uploadsSlice = createSlice({ name: 'uploads', initialState: initialUploadsState, reducers: { - uploadAdded: uploadsAdapter.upsertOne, + uploadUpserted: uploadsAdapter.upsertOne, }, extraReducers: (builder) => { /** @@ -75,6 +75,6 @@ export const { selectTotal: selectUploadsTotal, } = uploadsAdapter.getSelectors((state) => state.uploads); -export const { uploadAdded } = uploadsSlice.actions; +export const { uploadUpserted } = uploadsSlice.actions; export default uploadsSlice.reducer; From 39088e42cc3ce75c5cd5051d7178875a9c8a8114 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:25:08 +1000 Subject: [PATCH 25/31] fix(ui): remove console logs --- .../listenerMiddleware/listeners/imageMetadataReceived.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts index aa386a2f7f..c93ed2820f 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageMetadataReceived.ts @@ -20,12 +20,10 @@ export const addImageMetadataReceivedFulfilledListener = () => { moduleLog.debug({ data: { image } }, 'Image metadata received'); if (image.image_type === 'results') { - console.log('upsert results'); dispatch(resultUpserted(action.payload as ResultsImageDTO)); } if (image.image_type === 'uploads') { - console.log('upsert uploads'); dispatch(uploadUpserted(action.payload as UploadsImageDTO)); } }, From 249522b568452bd3076fd7d88022e09a62a9088e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:42:24 +1000 Subject: [PATCH 26/31] fix(ui): fix gallery not loading more images correctly after generation --- .../web/src/features/gallery/store/resultsSlice.ts | 13 +++++++++++-- .../frontend/web/src/services/thunks/gallery.ts | 12 ++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts index e601af0443..36f4c49401 100644 --- a/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/resultsSlice.ts @@ -1,4 +1,8 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; +import { + PayloadAction, + createEntityAdapter, + createSlice, +} from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; import { receivedResultImagesPage, @@ -21,6 +25,7 @@ type AdditionalResultsState = { pages: number; isLoading: boolean; nextPage: number; + upsertedImageCount: number; }; export const initialResultsState = @@ -29,6 +34,7 @@ export const initialResultsState = pages: 0, isLoading: false, nextPage: 0, + upsertedImageCount: 0, }); export type ResultsState = typeof initialResultsState; @@ -37,7 +43,10 @@ const resultsSlice = createSlice({ name: 'results', initialState: initialResultsState, reducers: { - resultUpserted: resultsAdapter.upsertOne, + resultUpserted: (state, action: PayloadAction) => { + resultsAdapter.upsertOne(state, action.payload); + state.upsertedImageCount += 1; + }, }, extraReducers: (builder) => { /** diff --git a/invokeai/frontend/web/src/services/thunks/gallery.ts b/invokeai/frontend/web/src/services/thunks/gallery.ts index 25d8ce0738..85e60326a4 100644 --- a/invokeai/frontend/web/src/services/thunks/gallery.ts +++ b/invokeai/frontend/web/src/services/thunks/gallery.ts @@ -9,12 +9,14 @@ const galleryLog = log.child({ namespace: 'gallery' }); export const receivedResultImagesPage = createAppAsyncThunk( 'results/receivedResultImagesPage', async (_arg, { getState, rejectWithValue }) => { - const { page, pages, nextPage } = getState().results; + const { page, pages, nextPage, upsertedImageCount } = getState().results; + + const pageOffset = Math.floor(upsertedImageCount / IMAGES_PER_PAGE); const response = await ImagesService.listImagesWithMetadata({ imageType: 'results', imageCategory: 'general', - page: getState().results.nextPage, + page: nextPage + pageOffset, perPage: IMAGES_PER_PAGE, }); @@ -27,12 +29,14 @@ export const receivedResultImagesPage = createAppAsyncThunk( export const receivedUploadImagesPage = createAppAsyncThunk( 'uploads/receivedUploadImagesPage', async (_arg, { getState, rejectWithValue }) => { - const { page, pages, nextPage } = getState().uploads; + const { page, pages, nextPage, upsertedImageCount } = getState().uploads; + + const pageOffset = Math.floor(upsertedImageCount / IMAGES_PER_PAGE); const response = await ImagesService.listImagesWithMetadata({ imageType: 'uploads', imageCategory: 'general', - page: getState().uploads.nextPage, + page: nextPage + pageOffset, perPage: IMAGES_PER_PAGE, }); From 7b0938e7e48b99216c12ba073eddaedb9e14e6b2 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:43:25 +1000 Subject: [PATCH 27/31] feat(ui): add comments for weird stuff --- invokeai/frontend/web/src/services/thunks/gallery.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/invokeai/frontend/web/src/services/thunks/gallery.ts b/invokeai/frontend/web/src/services/thunks/gallery.ts index 85e60326a4..9e7ee6d5c6 100644 --- a/invokeai/frontend/web/src/services/thunks/gallery.ts +++ b/invokeai/frontend/web/src/services/thunks/gallery.ts @@ -11,6 +11,8 @@ export const receivedResultImagesPage = createAppAsyncThunk( async (_arg, { getState, rejectWithValue }) => { const { page, pages, nextPage, upsertedImageCount } = getState().results; + // If many images have been upserted, we need to offset the page number + // TODO: add an offset param to the list images endpoint const pageOffset = Math.floor(upsertedImageCount / IMAGES_PER_PAGE); const response = await ImagesService.listImagesWithMetadata({ @@ -31,6 +33,8 @@ export const receivedUploadImagesPage = createAppAsyncThunk( async (_arg, { getState, rejectWithValue }) => { const { page, pages, nextPage, upsertedImageCount } = getState().uploads; + // If many images have been upserted, we need to offset the page number + // TODO: add an offset param to the list images endpoint const pageOffset = Math.floor(upsertedImageCount / IMAGES_PER_PAGE); const response = await ImagesService.listImagesWithMetadata({ From 96b4d35d43279c1349b445dc6a741853dcc0073d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:44:13 +1000 Subject: [PATCH 28/31] fix(ui): fix uploads not loading more images correctly after generation --- .../web/src/features/gallery/store/uploadsSlice.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts index 599e1b3830..3058e82673 100644 --- a/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/uploadsSlice.ts @@ -1,4 +1,8 @@ -import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; +import { + PayloadAction, + createEntityAdapter, + createSlice, +} from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; import { @@ -22,6 +26,7 @@ type AdditionalUploadsState = { pages: number; isLoading: boolean; nextPage: number; + upsertedImageCount: number; }; export const initialUploadsState = @@ -30,6 +35,7 @@ export const initialUploadsState = pages: 0, nextPage: 0, isLoading: false, + upsertedImageCount: 0, }); export type UploadsState = typeof initialUploadsState; @@ -38,7 +44,10 @@ const uploadsSlice = createSlice({ name: 'uploads', initialState: initialUploadsState, reducers: { - uploadUpserted: uploadsAdapter.upsertOne, + uploadUpserted: (state, action: PayloadAction) => { + uploadsAdapter.upsertOne(state, action.payload); + state.upsertedImageCount += 1; + }, }, extraReducers: (builder) => { /** From c6f935bf1a79bdec83946b8606aae8e31956c966 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:53:13 +1000 Subject: [PATCH 29/31] feat(ui): improve gallery page handling --- .../middleware/listenerMiddleware/index.ts | 14 ++++++++ .../listeners/receivedResultImagesPage.ts | 33 +++++++++++++++++++ .../listeners/receivedUploadImagesPage.ts | 33 +++++++++++++++++++ .../web/src/services/thunks/gallery.ts | 30 ++++++++++++----- 4 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index dae56804af..2996b0f34a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -54,6 +54,14 @@ import { addSessionInvokedPendingListener, addSessionInvokedRejectedListener, } from './listeners/sessionInvoked'; +import { + addReceivedResultImagesPageFulfilledListener, + addReceivedResultImagesPageRejectedListener, +} from './listeners/receivedResultImagesPage'; +import { + addReceivedUploadImagesPageFulfilledListener, + addReceivedUploadImagesPageRejectedListener, +} from './listeners/receivedUploadImagesPage'; export const listenerMiddleware = createListenerMiddleware(); @@ -126,3 +134,9 @@ addSocketUnsubscribedListener(); addSessionCreatedPendingListener(); addSessionCreatedFulfilledListener(); addSessionCreatedRejectedListener(); + +// Gallery pages +addReceivedResultImagesPageFulfilledListener(); +addReceivedResultImagesPageRejectedListener(); +addReceivedUploadImagesPageFulfilledListener(); +addReceivedUploadImagesPageRejectedListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts new file mode 100644 index 0000000000..5e4bff4b48 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts @@ -0,0 +1,33 @@ +import { log } from 'app/logging/useLogger'; +import { startAppListening } from '..'; +import { receivedResultImagesPage } from 'services/thunks/gallery'; +import { serializeError } from 'serialize-error'; + +const moduleLog = log.child({ namespace: 'gallery' }); + +export const addReceivedResultImagesPageFulfilledListener = () => { + startAppListening({ + actionCreator: receivedResultImagesPage.fulfilled, + effect: (action, { getState, dispatch }) => { + const page = action.payload; + moduleLog.info( + { data: { page } }, + `Received ${page.items.length} results` + ); + }, + }); +}; + +export const addReceivedResultImagesPageRejectedListener = () => { + startAppListening({ + actionCreator: receivedResultImagesPage.rejected, + effect: (action, { getState, dispatch }) => { + if (action.payload) { + moduleLog.debug( + { data: { error: serializeError(action.payload.error) } }, + 'Problem receiving results' + ); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts new file mode 100644 index 0000000000..7b593987e5 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts @@ -0,0 +1,33 @@ +import { log } from 'app/logging/useLogger'; +import { startAppListening } from '..'; +import { receivedUploadImagesPage } from 'services/thunks/gallery'; +import { serializeError } from 'serialize-error'; + +const moduleLog = log.child({ namespace: 'gallery' }); + +export const addReceivedUploadImagesPageFulfilledListener = () => { + startAppListening({ + actionCreator: receivedUploadImagesPage.fulfilled, + effect: (action, { getState, dispatch }) => { + const page = action.payload; + moduleLog.info( + { data: { page } }, + `Received ${page.items.length} uploads` + ); + }, + }); +}; + +export const addReceivedUploadImagesPageRejectedListener = () => { + startAppListening({ + actionCreator: receivedUploadImagesPage.rejected, + effect: (action, { getState, dispatch }) => { + if (action.payload) { + moduleLog.debug( + { data: { error: serializeError(action.payload.error) } }, + 'Problem receiving uploads' + ); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/services/thunks/gallery.ts b/invokeai/frontend/web/src/services/thunks/gallery.ts index 9e7ee6d5c6..8a6505b6c2 100644 --- a/invokeai/frontend/web/src/services/thunks/gallery.ts +++ b/invokeai/frontend/web/src/services/thunks/gallery.ts @@ -1,12 +1,20 @@ import { log } from 'app/logging/useLogger'; import { createAppAsyncThunk } from 'app/store/storeUtils'; -import { ImagesService } from 'services/api'; +import { ImagesService, PaginatedResults_ImageDTO_ } from 'services/api'; export const IMAGES_PER_PAGE = 20; -const galleryLog = log.child({ namespace: 'gallery' }); +type ReceivedResultImagesPageThunkConfig = { + rejectValue: { + error: unknown; + }; +}; -export const receivedResultImagesPage = createAppAsyncThunk( +export const receivedResultImagesPage = createAppAsyncThunk< + PaginatedResults_ImageDTO_, + void, + ReceivedResultImagesPageThunkConfig +>( 'results/receivedResultImagesPage', async (_arg, { getState, rejectWithValue }) => { const { page, pages, nextPage, upsertedImageCount } = getState().results; @@ -22,13 +30,21 @@ export const receivedResultImagesPage = createAppAsyncThunk( perPage: IMAGES_PER_PAGE, }); - galleryLog.info({ response }, `Received ${response.items.length} results`); - return response; } ); -export const receivedUploadImagesPage = createAppAsyncThunk( +type ReceivedUploadImagesPageThunkConfig = { + rejectValue: { + error: unknown; + }; +}; + +export const receivedUploadImagesPage = createAppAsyncThunk< + PaginatedResults_ImageDTO_, + void, + ReceivedUploadImagesPageThunkConfig +>( 'uploads/receivedUploadImagesPage', async (_arg, { getState, rejectWithValue }) => { const { page, pages, nextPage, upsertedImageCount } = getState().uploads; @@ -44,8 +60,6 @@ export const receivedUploadImagesPage = createAppAsyncThunk( perPage: IMAGES_PER_PAGE, }); - galleryLog.info({ response }, `Received ${response.items.length} uploads`); - return response; } ); From 0ea35b1e3d8495ae9eb5ccddd48b284d43bd45cc Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 16:59:13 +1000 Subject: [PATCH 30/31] feat(ui): improve session canceled handling --- .../middleware/listenerMiddleware/index.ts | 24 +++++++--- .../listeners/sessionCanceled.ts | 48 +++++++++++++++++++ .../web/src/services/thunks/gallery.ts | 1 - .../web/src/services/thunks/session.ts | 32 +++++++------ 4 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCanceled.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index 2996b0f34a..1fbc2f978c 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -54,6 +54,11 @@ import { addSessionInvokedPendingListener, addSessionInvokedRejectedListener, } from './listeners/sessionInvoked'; +import { + addSessionCanceledFulfilledListener, + addSessionCanceledPendingListener, + addSessionCanceledRejectedListener, +} from './listeners/sessionCanceled'; import { addReceivedResultImagesPageFulfilledListener, addReceivedResultImagesPageRejectedListener, @@ -101,18 +106,13 @@ addImageMetadataReceivedRejectedListener(); addImageUrlsReceivedFulfilledListener(); addImageUrlsReceivedRejectedListener(); -// Invoking on tabs +// User Invoked addUserInvokedCanvasListener(); addUserInvokedNodesListener(); addUserInvokedTextToImageListener(); addUserInvokedImageToImageListener(); addSessionReadyToInvokeListener(); -// Actual session invoking -addSessionInvokedPendingListener(); -addSessionInvokedFulfilledListener(); -addSessionInvokedRejectedListener(); - // Canvas actions addCanvasSavedToGalleryListener(); addCanvasDownloadedAsImageListener(); @@ -130,11 +130,21 @@ addSocketDisconnectedListener(); addSocketSubscribedListener(); addSocketUnsubscribedListener(); -// Sessions +// Session Created addSessionCreatedPendingListener(); addSessionCreatedFulfilledListener(); addSessionCreatedRejectedListener(); +// Session Invoked +addSessionInvokedPendingListener(); +addSessionInvokedFulfilledListener(); +addSessionInvokedRejectedListener(); + +// Session Canceled +addSessionCanceledPendingListener(); +addSessionCanceledFulfilledListener(); +addSessionCanceledRejectedListener(); + // Gallery pages addReceivedResultImagesPageFulfilledListener(); addReceivedResultImagesPageRejectedListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCanceled.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCanceled.ts new file mode 100644 index 0000000000..6274ad4dc8 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCanceled.ts @@ -0,0 +1,48 @@ +import { log } from 'app/logging/useLogger'; +import { startAppListening } from '..'; +import { sessionCanceled } from 'services/thunks/session'; +import { serializeError } from 'serialize-error'; + +const moduleLog = log.child({ namespace: 'session' }); + +export const addSessionCanceledPendingListener = () => { + startAppListening({ + actionCreator: sessionCanceled.pending, + effect: (action, { getState, dispatch }) => { + // + }, + }); +}; + +export const addSessionCanceledFulfilledListener = () => { + startAppListening({ + actionCreator: sessionCanceled.fulfilled, + effect: (action, { getState, dispatch }) => { + const { sessionId } = action.meta.arg; + moduleLog.debug( + { data: { sessionId } }, + `Session canceled (${sessionId})` + ); + }, + }); +}; + +export const addSessionCanceledRejectedListener = () => { + startAppListening({ + actionCreator: sessionCanceled.rejected, + effect: (action, { getState, dispatch }) => { + if (action.payload) { + const { arg, error } = action.payload; + moduleLog.error( + { + data: { + arg, + error: serializeError(error), + }, + }, + `Problem canceling session` + ); + } + }, + }); +}; diff --git a/invokeai/frontend/web/src/services/thunks/gallery.ts b/invokeai/frontend/web/src/services/thunks/gallery.ts index 8a6505b6c2..11960e00d2 100644 --- a/invokeai/frontend/web/src/services/thunks/gallery.ts +++ b/invokeai/frontend/web/src/services/thunks/gallery.ts @@ -1,4 +1,3 @@ -import { log } from 'app/logging/useLogger'; import { createAppAsyncThunk } from 'app/store/storeUtils'; import { ImagesService, PaginatedResults_ImageDTO_ } from 'services/api'; diff --git a/invokeai/frontend/web/src/services/thunks/session.ts b/invokeai/frontend/web/src/services/thunks/session.ts index efab401520..cf87fb30f5 100644 --- a/invokeai/frontend/web/src/services/thunks/session.ts +++ b/invokeai/frontend/web/src/services/thunks/session.ts @@ -49,7 +49,7 @@ const isErrorWithStatus = (error: unknown): error is { status: number } => * `SessionsService.invokeSession()` thunk */ export const sessionInvoked = createAppAsyncThunk< - any, + void, SessionInvokedArg, SessionInvokedThunkConfig >('api/sessionInvoked', async (arg, { rejectWithValue }) => { @@ -72,24 +72,28 @@ export const sessionInvoked = createAppAsyncThunk< type SessionCanceledArg = Parameters< (typeof SessionsService)['cancelSessionInvoke'] >[0]; - +type SessionCanceledThunkConfig = { + rejectValue: { + arg: SessionCanceledArg; + error: unknown; + }; +}; /** * `SessionsService.cancelSession()` thunk */ -export const sessionCanceled = createAppAsyncThunk( - 'api/sessionCanceled', - async (arg: SessionCanceledArg, _thunkApi) => { - const { sessionId } = arg; +export const sessionCanceled = createAppAsyncThunk< + void, + SessionCanceledArg, + SessionCanceledThunkConfig +>('api/sessionCanceled', async (arg: SessionCanceledArg, _thunkApi) => { + const { sessionId } = arg; - const response = await SessionsService.cancelSessionInvoke({ - sessionId, - }); + const response = await SessionsService.cancelSessionInvoke({ + sessionId, + }); - sessionLog.info({ arg, response }, `Session canceled (${sessionId})`); - - return response; - } -); + return response; +}); type SessionsListedArg = Parameters< (typeof SessionsService)['listSessions'] From a25bae2545e2dfa7821ff5b8aadbec56940981cb Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 26 May 2023 17:27:42 +1000 Subject: [PATCH 31/31] fix(ui): tweak log levels --- .../middleware/listenerMiddleware/listeners/imageDeleted.ts | 4 ++-- .../listenerMiddleware/listeners/receivedResultImagesPage.ts | 2 +- .../listenerMiddleware/listeners/receivedUploadImagesPage.ts | 2 +- .../listenerMiddleware/listeners/sessionCreated.ts | 2 +- .../listenerMiddleware/listeners/sessionInvoked.ts | 5 ++++- .../listenerMiddleware/listeners/sessionReadyToInvoke.ts | 2 +- .../listeners/socketio/graphExecutionStateComplete.ts | 2 +- .../listeners/socketio/invocationComplete.ts | 2 +- .../listenerMiddleware/listeners/socketio/invocationError.ts | 2 +- .../listeners/socketio/invocationStarted.ts | 2 +- .../listenerMiddleware/listeners/userInvokedCanvas.ts | 2 +- .../listenerMiddleware/listeners/userInvokedImageToImage.ts | 2 +- .../listenerMiddleware/listeners/userInvokedNodes.ts | 2 +- .../listenerMiddleware/listeners/userInvokedTextToImage.ts | 2 +- 14 files changed, 18 insertions(+), 15 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts index 804c713fc5..cd4771b96a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts @@ -84,7 +84,7 @@ export const addImageDeletedFulfilledListener = () => { startAppListening({ actionCreator: imageDeleted.fulfilled, effect: (action, { dispatch, getState }) => { - moduleLog.info({ data: { image: action.meta.arg } }, 'Image deleted'); + moduleLog.debug({ data: { image: action.meta.arg } }, 'Image deleted'); }, }); }; @@ -96,7 +96,7 @@ export const addImageDeletedRejectedListener = () => { startAppListening({ actionCreator: imageDeleted.rejected, effect: (action, { dispatch, getState }) => { - moduleLog.warn( + moduleLog.debug( { data: { image: action.meta.arg } }, 'Unable to delete image' ); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts index 5e4bff4b48..bcdd11ef97 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedResultImagesPage.ts @@ -10,7 +10,7 @@ export const addReceivedResultImagesPageFulfilledListener = () => { actionCreator: receivedResultImagesPage.fulfilled, effect: (action, { getState, dispatch }) => { const page = action.payload; - moduleLog.info( + moduleLog.debug( { data: { page } }, `Received ${page.items.length} results` ); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts index 7b593987e5..68813aae27 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/receivedUploadImagesPage.ts @@ -10,7 +10,7 @@ export const addReceivedUploadImagesPageFulfilledListener = () => { actionCreator: receivedUploadImagesPage.fulfilled, effect: (action, { getState, dispatch }) => { const page = action.payload; - moduleLog.info( + moduleLog.debug( { data: { page } }, `Received ${page.items.length} uploads` ); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts index e960510d0f..fb8a64d2e3 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionCreated.ts @@ -19,7 +19,7 @@ export const addSessionCreatedFulfilledListener = () => { actionCreator: sessionCreated.fulfilled, effect: (action, { getState, dispatch }) => { const session = action.payload; - moduleLog.info({ data: { session } }, `Session created (${session.id})`); + moduleLog.debug({ data: { session } }, `Session created (${session.id})`); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts index bddadbc359..272d1d9e1d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionInvoked.ts @@ -19,7 +19,10 @@ export const addSessionInvokedFulfilledListener = () => { actionCreator: sessionInvoked.fulfilled, effect: (action, { getState, dispatch }) => { const { sessionId } = action.meta.arg; - moduleLog.info({ data: { sessionId } }, `Session invoked (${sessionId})`); + moduleLog.debug( + { data: { sessionId } }, + `Session invoked (${sessionId})` + ); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts index 4fae2d96c0..8d4262e7da 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/sessionReadyToInvoke.ts @@ -11,7 +11,7 @@ export const addSessionReadyToInvokeListener = () => { effect: (action, { getState, dispatch }) => { const { sessionId } = getState().system; if (sessionId) { - moduleLog.info( + moduleLog.debug( { sessionId }, `Session ready to invoke (${sessionId})})` ); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts index c8ac46f6f1..a66a7fb547 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/graphExecutionStateComplete.ts @@ -10,7 +10,7 @@ export const addGraphExecutionStateCompleteListener = () => { effect: (action, { dispatch, getState }) => { moduleLog.debug( action.payload, - `Graph execution state complete (${action.payload.data.graph_execution_state_id})` + `Session invocation complete (${action.payload.data.graph_execution_state_id})` ); }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts index cfd9706306..95e6d831c0 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationComplete.ts @@ -15,7 +15,7 @@ export const addInvocationCompleteListener = () => { startAppListening({ actionCreator: invocationComplete, effect: async (action, { dispatch, getState, take }) => { - moduleLog.info( + moduleLog.debug( action.payload, `Invocation complete (${action.payload.data.node.type})` ); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts index d0e4d975be..3a98af120a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationError.ts @@ -8,7 +8,7 @@ export const addInvocationErrorListener = () => { startAppListening({ actionCreator: invocationError, effect: (action, { dispatch, getState }) => { - moduleLog.debug( + moduleLog.error( action.payload, `Invocation error (${action.payload.data.node.type})` ); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts index 373802fa16..f898c62b23 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/invocationStarted.ts @@ -19,7 +19,7 @@ export const addInvocationStartedListener = () => { return; } - moduleLog.info( + moduleLog.debug( action.payload, `Invocation started (${action.payload.data.node.type})` ); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts index 641cbb5ca5..ae388b85cf 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedCanvas.ts @@ -91,7 +91,7 @@ export const addUserInvokedCanvasListener = () => { dispatch(canvasGraphBuilt(graph)); - moduleLog({ data: graph }, 'Canvas graph built'); + moduleLog.debug({ data: graph }, 'Canvas graph built'); // If we are generating img2img or inpaint, we need to upload the init images if (baseNode.type === 'img2img' || baseNode.type === 'inpaint') { diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts index 8940237782..7dcbe8a41d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedImageToImage.ts @@ -17,7 +17,7 @@ export const addUserInvokedImageToImageListener = () => { const graph = buildImageToImageGraph(state); dispatch(imageToImageGraphBuilt(graph)); - moduleLog({ data: graph }, 'Image to Image graph built'); + moduleLog.debug({ data: graph }, 'Image to Image graph built'); dispatch(sessionCreated({ graph })); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts index 45dcf7b0b2..6fda3db0d6 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedNodes.ts @@ -17,7 +17,7 @@ export const addUserInvokedNodesListener = () => { const graph = buildNodesGraph(state); dispatch(nodesGraphBuilt(graph)); - moduleLog({ data: graph }, 'Nodes graph built'); + moduleLog.debug({ data: graph }, 'Nodes graph built'); dispatch(sessionCreated({ graph })); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts index f7245b9301..6042d86cb7 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/userInvokedTextToImage.ts @@ -19,7 +19,7 @@ export const addUserInvokedTextToImageListener = () => { dispatch(textToImageGraphBuilt(graph)); - moduleLog({ data: graph }, 'Text to Image graph built'); + moduleLog.debug({ data: graph }, 'Text to Image graph built'); dispatch(sessionCreated({ graph }));