From 04b57c408f4d3b5befaf8c1c2c1afbe789f911ca Mon Sep 17 00:00:00 2001 From: Sergey Borisov Date: Thu, 6 Jul 2023 16:09:40 +0300 Subject: [PATCH 01/21] Add clip skip option to prompt node --- invokeai/app/invocations/compel.py | 2 ++ invokeai/backend/model_management/lora.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/invokeai/app/invocations/compel.py b/invokeai/app/invocations/compel.py index 4850b9670d..4941f9ee8a 100644 --- a/invokeai/app/invocations/compel.py +++ b/invokeai/app/invocations/compel.py @@ -44,6 +44,7 @@ class CompelInvocation(BaseInvocation): prompt: str = Field(default="", description="Prompt") clip: ClipField = Field(None, description="Clip to use") + clip_skip: int = Field(0, description="Layers to skip in text_encoder") # Schema customisation class Config(InvocationConfig): @@ -95,6 +96,7 @@ class CompelInvocation(BaseInvocation): with ModelPatcher.apply_lora_text_encoder(text_encoder_info.context.model, _lora_loader()),\ ModelPatcher.apply_ti(tokenizer_info.context.model, text_encoder_info.context.model, ti_list) as (tokenizer, ti_manager),\ + ModelPatcher.apply_clip_skip(text_encoder_info.context.model, self.clip_skip),\ text_encoder_info as text_encoder: compel = Compel( diff --git a/invokeai/backend/model_management/lora.py b/invokeai/backend/model_management/lora.py index d8ecdf81c2..3d2fc327f4 100644 --- a/invokeai/backend/model_management/lora.py +++ b/invokeai/backend/model_management/lora.py @@ -615,6 +615,24 @@ class ModelPatcher: text_encoder.resize_token_embeddings(init_tokens_count) + @classmethod + @contextmanager + def apply_clip_skip( + cls, + text_encoder: CLIPTextModel, + clip_skip: int, + ): + skipped_layers = [] + try: + for i in range(clip_skip): + skipped_layers.append(text_encoder.text_model.encoder.layers.pop(-1)) + + yield + + finally: + while len(skipped_layers) > 0: + text_encoder.text_model.encoder.layers.append(skipped_layers.pop()) + class TextualInversionModel: name: str embedding: torch.Tensor # [n, 768]|[n, 1280] From a9e77675a8e89df64110de8304f7bcde96b85919 Mon Sep 17 00:00:00 2001 From: Sergey Borisov Date: Thu, 6 Jul 2023 17:39:49 +0300 Subject: [PATCH 02/21] Move clip skip to separate node --- invokeai/app/invocations/compel.py | 21 +++++++++++++++++++-- invokeai/app/invocations/model.py | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/invokeai/app/invocations/compel.py b/invokeai/app/invocations/compel.py index 4941f9ee8a..f48e4aa648 100644 --- a/invokeai/app/invocations/compel.py +++ b/invokeai/app/invocations/compel.py @@ -44,7 +44,6 @@ class CompelInvocation(BaseInvocation): prompt: str = Field(default="", description="Prompt") clip: ClipField = Field(None, description="Clip to use") - clip_skip: int = Field(0, description="Layers to skip in text_encoder") # Schema customisation class Config(InvocationConfig): @@ -96,7 +95,7 @@ class CompelInvocation(BaseInvocation): with ModelPatcher.apply_lora_text_encoder(text_encoder_info.context.model, _lora_loader()),\ ModelPatcher.apply_ti(tokenizer_info.context.model, text_encoder_info.context.model, ti_list) as (tokenizer, ti_manager),\ - ModelPatcher.apply_clip_skip(text_encoder_info.context.model, self.clip_skip),\ + ModelPatcher.apply_clip_skip(text_encoder_info.context.model, self.clip.skipped_layers),\ text_encoder_info as text_encoder: compel = Compel( @@ -136,6 +135,24 @@ class CompelInvocation(BaseInvocation): ), ) +class ClipSkipInvocationOutput(BaseInvocationOutput): + """Clip skip node output""" + type: Literal["clip_skip_output"] = "clip_skip_output" + clip: ClipField = Field(None, description="Clip with skipped layers") + +class ClipSkipInvocation(BaseInvocation): + """Skip layers in clip text_encoder model.""" + type: Literal["clip_skip"] = "clip_skip" + + clip: ClipField = Field(None, description="Clip to use") + skipped_layers: int = Field(0, description="Number of layers to skip in text_encoder") + + def invoke(self, context: InvocationContext) -> ClipSkipInvocationOutput: + self.clip.skipped_layers += self.skipped_layers + return ClipSkipInvocationOutput( + clip=self.clip, + ) + def get_max_token_count( tokenizer, prompt: Union[FlattenedPrompt, Blend, Conjunction], diff --git a/invokeai/app/invocations/model.py b/invokeai/app/invocations/model.py index 17297ba417..34836eabd2 100644 --- a/invokeai/app/invocations/model.py +++ b/invokeai/app/invocations/model.py @@ -30,6 +30,7 @@ class UNetField(BaseModel): class ClipField(BaseModel): tokenizer: ModelInfo = Field(description="Info to load tokenizer submodel") text_encoder: ModelInfo = Field(description="Info to load text_encoder submodel") + skipped_layers: int = Field(description="Number of skipped layers in text_encoder") loras: List[LoraInfo] = Field(description="Loras to apply on model loading") @@ -154,6 +155,7 @@ class MainModelLoaderInvocation(BaseInvocation): submodel=SubModelType.TextEncoder, ), loras=[], + skipped_layers=0, ), vae=VaeField( vae=ModelInfo( From ce7803231bb5b68022f8decf4b6c41f69d6238c0 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 7 Jul 2023 05:57:39 +1200 Subject: [PATCH 03/21] feat: Add Clip Skip To Linear UI --- invokeai/frontend/web/public/locales/en.json | 6 +- .../buildCanvasImageToImageGraph.ts | 19 ++++- .../graphBuilders/buildCanvasInpaintGraph.ts | 77 +++++++++++-------- .../buildCanvasTextToImageGraph.ts | 19 ++++- .../buildLinearImageToImageGraph.ts | 50 ++++++++---- .../buildLinearTextToImageGraph.ts | 77 +++++++++++-------- .../nodes/util/graphBuilders/constants.ts | 1 + .../Advanced/ParamAdvancedCollapse.tsx | 20 +++++ .../Parameters/Advanced/ParamClipSkip.tsx | 34 ++++++++ .../parameters/store/generationSlice.ts | 6 ++ .../SettingsModal/SettingsModal.tsx | 11 +++ .../ImageToImageTabParameters.tsx | 2 + .../TextToImage/TextToImageTabParameters.tsx | 2 + .../UnifiedCanvas/UnifiedCanvasParameters.tsx | 2 + .../web/src/features/ui/store/uiSlice.ts | 5 ++ .../web/src/features/ui/store/uiTypes.ts | 1 + .../frontend/web/src/services/api/schema.d.ts | 64 ++++++++++++++- 17 files changed, 311 insertions(+), 85 deletions(-) create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 9cf1e0bc48..b403fde2c6 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -527,7 +527,8 @@ "showOptionsPanel": "Show Options Panel", "hidePreview": "Hide Preview", "showPreview": "Show Preview", - "controlNetControlMode": "Control Mode" + "controlNetControlMode": "Control Mode", + "clipSkip": "Clip Skip" }, "settings": { "models": "Models", @@ -551,7 +552,8 @@ "generation": "Generation", "ui": "User Interface", "favoriteSchedulers": "Favorite Schedulers", - "favoriteSchedulersPlaceholder": "No schedulers favorited" + "favoriteSchedulersPlaceholder": "No schedulers favorited", + "showAdvancedOptions": "Show Advanced Options" }, "toast": { "serverError": "Server Error", diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts index 1843efef84..7d7b9cf4c9 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts @@ -12,6 +12,7 @@ import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addVAEToGraph } from './addVAEToGraph'; import { + CLIP_SKIP, IMAGE_TO_IMAGE_GRAPH, IMAGE_TO_LATENTS, LATENTS_TO_IMAGE, @@ -40,6 +41,7 @@ export const buildCanvasImageToImageGraph = ( scheduler, steps, img2imgStrength: strength, + clipSkip, iterations, seed, shouldRandomizeSeed, @@ -82,6 +84,11 @@ export const buildCanvasImageToImageGraph = ( id: MAIN_MODEL_LOADER, model, }, + [CLIP_SKIP]: { + type: 'clip_skip', + id: CLIP_SKIP, + skipped_layers: clipSkip, + }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, @@ -109,6 +116,16 @@ export const buildCanvasImageToImageGraph = ( node_id: MAIN_MODEL_LOADER, field: 'clip', }, + destination: { + node_id: CLIP_SKIP, + field: 'clip', + }, + }, + { + source: { + node_id: CLIP_SKIP, + field: 'clip', + }, destination: { node_id: POSITIVE_CONDITIONING, field: 'clip', @@ -116,7 +133,7 @@ export const buildCanvasImageToImageGraph = ( }, { source: { - node_id: MAIN_MODEL_LOADER, + node_id: CLIP_SKIP, field: 'clip', }, destination: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts index c4f9415067..8425b1dac5 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts @@ -11,6 +11,7 @@ import { modelIdToMainModelField } from '../modelIdToMainModelField'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addVAEToGraph } from './addVAEToGraph'; import { + CLIP_SKIP, INPAINT, INPAINT_GRAPH, ITERATE, @@ -49,6 +50,7 @@ export const buildCanvasInpaintGraph = ( seamStrength, tileSize, infillMethod, + clipSkip, } = state.generation; // The bounding box determines width and height, not the width and height params @@ -108,6 +110,11 @@ export const buildCanvasInpaintGraph = ( id: MAIN_MODEL_LOADER, model, }, + [CLIP_SKIP]: { + type: 'clip_skip', + id: CLIP_SKIP, + skipped_layers: clipSkip, + }, [RANGE_OF_SIZE]: { type: 'range_of_size', id: RANGE_OF_SIZE, @@ -122,6 +129,46 @@ export const buildCanvasInpaintGraph = ( }, }, edges: [ + { + source: { + node_id: MAIN_MODEL_LOADER, + field: 'unet', + }, + destination: { + node_id: INPAINT, + field: 'unet', + }, + }, + { + source: { + node_id: MAIN_MODEL_LOADER, + field: 'clip', + }, + destination: { + node_id: CLIP_SKIP, + field: 'clip', + }, + }, + { + source: { + node_id: CLIP_SKIP, + field: 'clip', + }, + destination: { + node_id: POSITIVE_CONDITIONING, + field: 'clip', + }, + }, + { + source: { + node_id: CLIP_SKIP, + field: 'clip', + }, + destination: { + node_id: NEGATIVE_CONDITIONING, + field: 'clip', + }, + }, { source: { node_id: NEGATIVE_CONDITIONING, @@ -142,36 +189,6 @@ export const buildCanvasInpaintGraph = ( field: 'positive_conditioning', }, }, - { - source: { - node_id: MAIN_MODEL_LOADER, - field: 'clip', - }, - destination: { - node_id: POSITIVE_CONDITIONING, - field: 'clip', - }, - }, - { - source: { - node_id: MAIN_MODEL_LOADER, - field: 'clip', - }, - destination: { - node_id: NEGATIVE_CONDITIONING, - field: 'clip', - }, - }, - { - source: { - node_id: MAIN_MODEL_LOADER, - field: 'unet', - }, - destination: { - node_id: INPAINT, - field: 'unet', - }, - }, { source: { node_id: RANGE_OF_SIZE, diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts index 976ea4fd01..25d7b13d7e 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts @@ -6,6 +6,7 @@ import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addVAEToGraph } from './addVAEToGraph'; import { + CLIP_SKIP, LATENTS_TO_IMAGE, MAIN_MODEL_LOADER, NEGATIVE_CONDITIONING, @@ -28,6 +29,7 @@ export const buildCanvasTextToImageGraph = ( cfgScale: cfg_scale, scheduler, steps, + clipSkip, iterations, seed, shouldRandomizeSeed, @@ -79,6 +81,11 @@ export const buildCanvasTextToImageGraph = ( id: MAIN_MODEL_LOADER, model, }, + [CLIP_SKIP]: { + type: 'clip_skip', + id: CLIP_SKIP, + skipped_layers: clipSkip, + }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, @@ -110,6 +117,16 @@ export const buildCanvasTextToImageGraph = ( node_id: MAIN_MODEL_LOADER, field: 'clip', }, + destination: { + node_id: CLIP_SKIP, + field: 'clip', + }, + }, + { + source: { + node_id: CLIP_SKIP, + field: 'clip', + }, destination: { node_id: POSITIVE_CONDITIONING, field: 'clip', @@ -117,7 +134,7 @@ export const buildCanvasTextToImageGraph = ( }, { source: { - node_id: MAIN_MODEL_LOADER, + node_id: CLIP_SKIP, field: 'clip', }, destination: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts index fe6d1292e4..b845d2d54f 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts @@ -13,6 +13,7 @@ import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addVAEToGraph } from './addVAEToGraph'; import { + CLIP_SKIP, IMAGE_COLLECTION, IMAGE_COLLECTION_ITERATE, IMAGE_TO_IMAGE_GRAPH, @@ -46,6 +47,7 @@ export const buildLinearImageToImageGraph = ( shouldFitToWidthHeight, width, height, + clipSkip, } = state.generation; const { @@ -77,6 +79,16 @@ export const buildLinearImageToImageGraph = ( const graph: NonNullableGraph = { id: IMAGE_TO_IMAGE_GRAPH, nodes: { + [MAIN_MODEL_LOADER]: { + type: 'main_model_loader', + id: MAIN_MODEL_LOADER, + model, + }, + [CLIP_SKIP]: { + type: 'clip_skip', + id: CLIP_SKIP, + skipped_layers: clipSkip, + }, [POSITIVE_CONDITIONING]: { type: 'compel', id: POSITIVE_CONDITIONING, @@ -91,11 +103,6 @@ export const buildLinearImageToImageGraph = ( type: 'noise', id: NOISE, }, - [MAIN_MODEL_LOADER]: { - type: 'main_model_loader', - id: MAIN_MODEL_LOADER, - model, - }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, @@ -121,6 +128,26 @@ export const buildLinearImageToImageGraph = ( { source: { node_id: MAIN_MODEL_LOADER, + field: 'unet', + }, + destination: { + node_id: LATENTS_TO_LATENTS, + field: 'unet', + }, + }, + { + source: { + node_id: MAIN_MODEL_LOADER, + field: 'clip', + }, + destination: { + node_id: CLIP_SKIP, + field: 'clip', + }, + }, + { + source: { + node_id: CLIP_SKIP, field: 'clip', }, destination: { @@ -130,7 +157,7 @@ export const buildLinearImageToImageGraph = ( }, { source: { - node_id: MAIN_MODEL_LOADER, + node_id: CLIP_SKIP, field: 'clip', }, destination: { @@ -168,17 +195,6 @@ export const buildLinearImageToImageGraph = ( field: 'noise', }, }, - - { - source: { - node_id: MAIN_MODEL_LOADER, - field: 'unet', - }, - destination: { - node_id: LATENTS_TO_LATENTS, - field: 'unet', - }, - }, { source: { node_id: NEGATIVE_CONDITIONING, diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts index 04dccf4983..b743a7221a 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts @@ -6,6 +6,7 @@ import { addDynamicPromptsToGraph } from './addDynamicPromptsToGraph'; import { addLoRAsToGraph } from './addLoRAsToGraph'; import { addVAEToGraph } from './addVAEToGraph'; import { + CLIP_SKIP, LATENTS_TO_IMAGE, MAIN_MODEL_LOADER, NEGATIVE_CONDITIONING, @@ -27,6 +28,7 @@ export const buildLinearTextToImageGraph = ( steps, width, height, + clipSkip, } = state.generation; const model = modelIdToMainModelField(modelId); @@ -44,6 +46,16 @@ export const buildLinearTextToImageGraph = ( const graph: NonNullableGraph = { id: TEXT_TO_IMAGE_GRAPH, nodes: { + [MAIN_MODEL_LOADER]: { + type: 'main_model_loader', + id: MAIN_MODEL_LOADER, + model, + }, + [CLIP_SKIP]: { + type: 'clip_skip', + id: CLIP_SKIP, + skipped_layers: clipSkip, + }, [POSITIVE_CONDITIONING]: { type: 'compel', id: POSITIVE_CONDITIONING, @@ -67,11 +79,6 @@ export const buildLinearTextToImageGraph = ( scheduler, steps, }, - [MAIN_MODEL_LOADER]: { - type: 'main_model_loader', - id: MAIN_MODEL_LOADER, - model, - }, [LATENTS_TO_IMAGE]: { type: 'l2i', id: LATENTS_TO_IMAGE, @@ -80,12 +87,42 @@ export const buildLinearTextToImageGraph = ( edges: [ { source: { - node_id: NEGATIVE_CONDITIONING, - field: 'conditioning', + node_id: MAIN_MODEL_LOADER, + field: 'clip', + }, + destination: { + node_id: CLIP_SKIP, + field: 'clip', + }, + }, + { + source: { + node_id: MAIN_MODEL_LOADER, + field: 'unet', }, destination: { node_id: TEXT_TO_LATENTS, - field: 'negative_conditioning', + field: 'unet', + }, + }, + { + source: { + node_id: CLIP_SKIP, + field: 'clip', + }, + destination: { + node_id: POSITIVE_CONDITIONING, + field: 'clip', + }, + }, + { + source: { + node_id: CLIP_SKIP, + field: 'clip', + }, + destination: { + node_id: NEGATIVE_CONDITIONING, + field: 'clip', }, }, { @@ -100,32 +137,12 @@ export const buildLinearTextToImageGraph = ( }, { source: { - node_id: MAIN_MODEL_LOADER, - field: 'clip', - }, - destination: { - node_id: POSITIVE_CONDITIONING, - field: 'clip', - }, - }, - { - source: { - node_id: MAIN_MODEL_LOADER, - field: 'clip', - }, - destination: { node_id: NEGATIVE_CONDITIONING, - field: 'clip', - }, - }, - { - source: { - node_id: MAIN_MODEL_LOADER, - field: 'unet', + field: 'conditioning', }, destination: { node_id: TEXT_TO_LATENTS, - field: 'unet', + field: 'negative_conditioning', }, }, { diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts index 7aace48def..256a623bba 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts @@ -10,6 +10,7 @@ export const ITERATE = 'iterate'; export const MAIN_MODEL_LOADER = 'main_model_loader'; export const VAE_LOADER = 'vae_loader'; export const LORA_LOADER = 'lora_loader'; +export const CLIP_SKIP = 'clip_skip'; export const IMAGE_TO_LATENTS = 'image_to_latents'; export const LATENTS_TO_LATENTS = 'latents_to_latents'; export const RESIZE = 'resize_image'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx new file mode 100644 index 0000000000..3ca2afb5cb --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx @@ -0,0 +1,20 @@ +import { Flex } from '@chakra-ui/react'; +import { RootState } from 'app/store/store'; +import { useAppSelector } from 'app/store/storeHooks'; +import IAICollapse from 'common/components/IAICollapse'; +import ParamClipSkip from './ParamClipSkip'; + +export default function ParamAdvancedCollapse() { + const shouldShowAdvancedOptions = useAppSelector( + (state: RootState) => state.ui.shouldShowAdvancedOptions + ); + return ( + shouldShowAdvancedOptions && ( + + + + + + ) + ); +} diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx new file mode 100644 index 0000000000..dcbde1b8dd --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx @@ -0,0 +1,34 @@ +import { RootState } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import IAINumberInput from 'common/components/IAINumberInput'; +import { setClipSkip } from 'features/parameters/store/generationSlice'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +export default function ParamClipSkip() { + const clipSkip = useAppSelector( + (state: RootState) => state.generation.clipSkip + ); + + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + + const handleClipSkipChange = useCallback( + (v: number) => { + dispatch(setClipSkip(v)); + }, + [dispatch] + ); + + return ( + + ); +} diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 960a41bb45..40bb8e91d4 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -51,6 +51,7 @@ export interface GenerationState { vae: VAEParam; seamlessXAxis: boolean; seamlessYAxis: boolean; + clipSkip: number; } export const initialGenerationState: GenerationState = { @@ -85,6 +86,7 @@ export const initialGenerationState: GenerationState = { vae: '', seamlessXAxis: false, seamlessYAxis: false, + clipSkip: 0, }; const initialState: GenerationState = initialGenerationState; @@ -217,6 +219,9 @@ export const generationSlice = createSlice({ vaeSelected: (state, action: PayloadAction) => { state.vae = action.payload; }, + setClipSkip: (state, action: PayloadAction) => { + state.clipSkip = action.payload; + }, }, extraReducers: (builder) => { builder.addCase(configChanged, (state, action) => { @@ -265,6 +270,7 @@ export const { setShouldUseNoiseSettings, setSeamlessXAxis, setSeamlessYAxis, + setClipSkip, } = generationSlice.actions; export default generationSlice.reducer; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx index c9508bb5fe..c8eb9cc34a 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -30,6 +30,7 @@ import { } from 'features/system/store/systemSlice'; import { uiSelector } from 'features/ui/store/uiSelectors'; import { + setShouldShowAdvancedOptions, setShouldShowProgressInViewer, setShouldUseCanvasBetaLayout, setShouldUseSliders, @@ -64,6 +65,7 @@ const selector = createSelector( shouldUseCanvasBetaLayout, shouldUseSliders, shouldShowProgressInViewer, + shouldShowAdvancedOptions, } = ui; return { @@ -76,6 +78,7 @@ const selector = createSelector( consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, + shouldShowAdvancedOptions, }; }, { @@ -132,6 +135,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { consoleLogLevel, shouldLogToConsole, shouldAntialiasProgressImage, + shouldShowAdvancedOptions, } = useAppSelector(selector); const handleClickResetWebUI = useCallback(() => { @@ -189,6 +193,13 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { dispatch(setShouldConfirmOnDelete(e.target.checked)) } /> + ) => + dispatch(setShouldShowAdvancedOptions(e.target.checked)) + } + /> diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabParameters.tsx index 32b71d6187..16c0f44158 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ImageToImage/ImageToImageTabParameters.tsx @@ -1,5 +1,6 @@ import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse'; import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse'; +import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse'; import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse'; import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning'; import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning'; @@ -25,6 +26,7 @@ const ImageToImageTabParameters = () => { + ); }; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx index 6291b69a8e..987f4ff0bc 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx @@ -1,5 +1,6 @@ import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse'; import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse'; +import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse'; import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse'; import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning'; import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning'; @@ -27,6 +28,7 @@ const TextToImageTabParameters = () => { + ); }; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx index 63ed4cc1cf..6c19a61372 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasParameters.tsx @@ -1,5 +1,6 @@ import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/ParamDynamicPromptsCollapse'; import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse'; +import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse'; import ParamInfillAndScalingCollapse from 'features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamInfillAndScalingCollapse'; import ParamSeamCorrectionCollapse from 'features/parameters/components/Parameters/Canvas/SeamCorrection/ParamSeamCorrectionCollapse'; import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse'; @@ -25,6 +26,7 @@ const UnifiedCanvasParameters = () => { + ); }; diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 861bf49405..04fee42126 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -20,6 +20,7 @@ export const initialUIState: UIState = { shouldHidePreview: false, shouldShowProgressInViewer: true, shouldShowEmbeddingPicker: false, + shouldShowAdvancedOptions: false, favoriteSchedulers: [], }; @@ -100,6 +101,9 @@ export const uiSlice = createSlice({ toggleEmbeddingPicker: (state) => { state.shouldShowEmbeddingPicker = !state.shouldShowEmbeddingPicker; }, + setShouldShowAdvancedOptions: (state, action: PayloadAction) => { + state.shouldShowAdvancedOptions = action.payload; + }, }, extraReducers(builder) { builder.addCase(initialImageChanged, (state) => { @@ -127,6 +131,7 @@ export const { setShouldShowProgressInViewer, favoriteSchedulersChanged, toggleEmbeddingPicker, + setShouldShowAdvancedOptions, } = uiSlice.actions; export default uiSlice.reducer; diff --git a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts index ad0250e56d..2356446030 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts @@ -28,5 +28,6 @@ export interface UIState { shouldShowGallery: boolean; shouldShowProgressInViewer: boolean; shouldShowEmbeddingPicker: boolean; + shouldShowAdvancedOptions: boolean; favoriteSchedulers: SchedulerParam[]; } diff --git a/invokeai/frontend/web/src/services/api/schema.d.ts b/invokeai/frontend/web/src/services/api/schema.d.ts index d7e50d004e..0854af4c3b 100644 --- a/invokeai/frontend/web/src/services/api/schema.d.ts +++ b/invokeai/frontend/web/src/services/api/schema.d.ts @@ -446,12 +446,68 @@ export type components = { * @description Info to load text_encoder submodel */ text_encoder: components["schemas"]["ModelInfo"]; + /** + * Skipped Layers + * @description Number of skipped layers in text_encoder + */ + skipped_layers: number; /** * Loras * @description Loras to apply on model loading */ loras: (components["schemas"]["LoraInfo"])[]; }; + /** + * ClipSkipInvocation + * @description Skip layers in clip text_encoder model. + */ + ClipSkipInvocation: { + /** + * Id + * @description The id of this node. Must be unique among all nodes. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this node is an intermediate node. + * @default false + */ + is_intermediate?: boolean; + /** + * Type + * @default clip_skip + * @enum {string} + */ + type?: "clip_skip"; + /** + * Clip + * @description Clip to use + */ + clip?: components["schemas"]["ClipField"]; + /** + * Skipped Layers + * @description Number of layers to skip in text_encoder + * @default 0 + */ + skipped_layers?: number; + }; + /** + * ClipSkipInvocationOutput + * @description Clip skip node output + */ + ClipSkipInvocationOutput: { + /** + * Type + * @default clip_skip_output + * @enum {string} + */ + type?: "clip_skip_output"; + /** + * Clip + * @description Clip with skipped layers + */ + clip?: components["schemas"]["ClipField"]; + }; /** * CollectInvocation * @description Collects values into a collection @@ -1054,7 +1110,7 @@ export type components = { * @description The nodes in this graph */ nodes?: { - [key: string]: (components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]) | undefined; + [key: string]: (components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]) | undefined; }; /** * Edges @@ -1097,7 +1153,7 @@ export type components = { * @description The results of node executions */ results: { - [key: string]: (components["schemas"]["ImageOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["PromptOutput"] | components["schemas"]["PromptCollectionOutput"] | components["schemas"]["CompelOutput"] | components["schemas"]["IntOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["IntCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]) | undefined; + [key: string]: (components["schemas"]["IntCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["CompelOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["IntOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PromptOutput"] | components["schemas"]["PromptCollectionOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]) | undefined; }; /** * Errors @@ -4583,7 +4639,7 @@ export type operations = { }; requestBody: { content: { - "application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; + "application/json": components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; }; }; responses: { @@ -4620,7 +4676,7 @@ export type operations = { }; requestBody: { content: { - "application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; + "application/json": components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; }; }; responses: { From 1ac787f3c13e2f5f90177b6f1f1230d8abbb8636 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 7 Jul 2023 06:37:07 +1200 Subject: [PATCH 04/21] feat: Change Clip Skip to Slider & Add Collapse Active Text --- .../web/src/common/components/IAISlider.tsx | 4 ++-- .../Advanced/ParamAdvancedCollapse.tsx | 18 ++++++++++++++++-- .../Parameters/Advanced/ParamClipSkip.tsx | 13 +++++++++++-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/invokeai/frontend/web/src/common/components/IAISlider.tsx b/invokeai/frontend/web/src/common/components/IAISlider.tsx index a435b50bbf..d923f2ad2d 100644 --- a/invokeai/frontend/web/src/common/components/IAISlider.tsx +++ b/invokeai/frontend/web/src/common/components/IAISlider.tsx @@ -26,7 +26,7 @@ import { } from '@chakra-ui/react'; import { clamp } from 'lodash-es'; -import { useTranslation } from 'react-i18next'; +import { roundDownToMultiple } from 'common/util/roundDownToMultiple'; import { FocusEvent, memo, @@ -36,9 +36,9 @@ import { useMemo, useState, } from 'react'; +import { useTranslation } from 'react-i18next'; import { BiReset } from 'react-icons/bi'; import IAIIconButton, { IAIIconButtonProps } from './IAIIconButton'; -import { roundDownToMultiple } from 'common/util/roundDownToMultiple'; const SLIDER_MARK_STYLES: ChakraProps['sx'] = { mt: 1.5, diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx index 3ca2afb5cb..5eae7f2800 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx @@ -1,16 +1,30 @@ import { Flex } from '@chakra-ui/react'; -import { RootState } from 'app/store/store'; +import { createSelector } from '@reduxjs/toolkit'; +import { RootState, stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAICollapse from 'common/components/IAICollapse'; import ParamClipSkip from './ParamClipSkip'; +const selector = createSelector( + stateSelector, + (state: RootState) => { + const clipSkip = state.generation.clipSkip; + return { + activeLabel: clipSkip > 0 ? `Clip Skip Active` : undefined, + }; + }, + defaultSelectorOptions +); + export default function ParamAdvancedCollapse() { + const { activeLabel } = useAppSelector(selector); const shouldShowAdvancedOptions = useAppSelector( (state: RootState) => state.ui.shouldShowAdvancedOptions ); return ( shouldShowAdvancedOptions && ( - + diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx index dcbde1b8dd..a096d3d3fc 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx @@ -1,6 +1,6 @@ import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAINumberInput from 'common/components/IAINumberInput'; +import IAISlider from 'common/components/IAISlider'; import { setClipSkip } from 'features/parameters/store/generationSlice'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -20,8 +20,12 @@ export default function ParamClipSkip() { [dispatch] ); + const handleClipSkipReset = useCallback(() => { + dispatch(setClipSkip(0)); + }, [dispatch]); + return ( - ); } From c6d6b33e3cabf935c97c8b94cd594daefdef6e81 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:21:16 +1200 Subject: [PATCH 05/21] feat: Reset clipSkip when advanced options is turned off --- .../web/src/features/parameters/store/generationSlice.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 40bb8e91d4..3e9356747d 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -2,6 +2,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import { DEFAULT_SCHEDULER_NAME } from 'app/constants'; import { configChanged } from 'features/system/store/configSlice'; +import { setShouldShowAdvancedOptions } from 'features/ui/store/uiSlice'; import { clamp } from 'lodash-es'; import { ImageDTO } from 'services/api/types'; import { @@ -230,6 +231,10 @@ export const generationSlice = createSlice({ state.model = defaultModel; } }); + builder.addCase(setShouldShowAdvancedOptions, (state, action) => { + const advancedOptionsStatus = action.payload; + if (!advancedOptionsStatus) state.clipSkip = 0; + }); }, }); From 803e1aaa175ff1c9b60ced4527f5dcb06a19c9bc Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:08:30 +1000 Subject: [PATCH 06/21] feat(ui): update `openapi-fetch`; fix upload issue My PR to fix an issue with the handling of formdata in `openapi-fetch` is released. This means we no longer need to patch the package (no patches at all now!). This PR bumps its version and adds a transformer to our typegen script to handle typing binary form fields correctly as `Blob`. Also regens types. --- invokeai/frontend/web/package.json | 4 +- .../web/patches/openapi-fetch+0.4.0.patch | 55 --- invokeai/frontend/web/scripts/package.json | 3 + invokeai/frontend/web/scripts/typegen.ts | 23 + .../frontend/web/src/services/api/schema.d.ts | 417 +++++++++--------- .../web/src/services/api/thunks/image.ts | 12 +- invokeai/frontend/web/tsconfig.json | 8 +- invokeai/frontend/web/yarn.lock | 8 +- 8 files changed, 246 insertions(+), 284 deletions(-) delete mode 100644 invokeai/frontend/web/patches/openapi-fetch+0.4.0.patch create mode 100644 invokeai/frontend/web/scripts/package.json create mode 100644 invokeai/frontend/web/scripts/typegen.ts diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index cd86bfdbe8..921b234aaf 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -23,7 +23,7 @@ "dev": "concurrently \"vite dev\" \"yarn run theme:watch\"", "dev:host": "concurrently \"vite dev --host\" \"yarn run theme:watch\"", "build": "yarn run lint && vite build", - "typegen": "npx openapi-typescript http://localhost:9090/openapi.json --output src/services/api/schema.d.ts -t", + "typegen": "npx ts-node scripts/typegen.ts", "preview": "vite preview", "lint:madge": "madge --circular src/main.tsx", "lint:eslint": "eslint --max-warnings=0 .", @@ -83,7 +83,7 @@ "konva": "^9.2.0", "lodash-es": "^4.17.21", "nanostores": "^0.9.2", - "openapi-fetch": "0.4.0", + "openapi-fetch": "^0.6.1", "overlayscrollbars": "^2.2.0", "overlayscrollbars-react": "^0.5.0", "patch-package": "^7.0.0", diff --git a/invokeai/frontend/web/patches/openapi-fetch+0.4.0.patch b/invokeai/frontend/web/patches/openapi-fetch+0.4.0.patch deleted file mode 100644 index d82843f71c..0000000000 --- a/invokeai/frontend/web/patches/openapi-fetch+0.4.0.patch +++ /dev/null @@ -1,55 +0,0 @@ -diff --git a/node_modules/openapi-fetch/dist/index.js b/node_modules/openapi-fetch/dist/index.js -index cd4528a..8976b51 100644 ---- a/node_modules/openapi-fetch/dist/index.js -+++ b/node_modules/openapi-fetch/dist/index.js -@@ -1,5 +1,5 @@ - // settings & const --const DEFAULT_HEADERS = { -+const CONTENT_TYPE_APPLICATION_JSON = { - "Content-Type": "application/json", - }; - const TRAILING_SLASH_RE = /\/*$/; -@@ -29,18 +29,29 @@ export function createFinalURL(url, options) { - } - return finalURL; - } -+function stringifyBody(body) { -+ if (body instanceof ArrayBuffer || body instanceof File || body instanceof DataView || body instanceof Blob || ArrayBuffer.isView(body) || body instanceof URLSearchParams || body instanceof FormData) { -+ return; -+ } -+ -+ if (typeof body === "string") { -+ return body; -+ } -+ -+ return JSON.stringify(body); -+ } -+ - export default function createClient(clientOptions = {}) { - const { fetch = globalThis.fetch, ...options } = clientOptions; -- const defaultHeaders = new Headers({ -- ...DEFAULT_HEADERS, -- ...(options.headers ?? {}), -- }); -+ const defaultHeaders = new Headers(options.headers ?? {}); - async function coreFetch(url, fetchOptions) { - const { headers, body: requestBody, params = {}, parseAs = "json", querySerializer = defaultSerializer, ...init } = fetchOptions || {}; - // URL - const finalURL = createFinalURL(url, { baseUrl: options.baseUrl, params, querySerializer }); -+ // Stringify body if needed -+ const stringifiedBody = stringifyBody(requestBody); - // headers -- const baseHeaders = new Headers(defaultHeaders); // clone defaults (don’t overwrite!) -+ const baseHeaders = new Headers(stringifiedBody ? { ...CONTENT_TYPE_APPLICATION_JSON, ...defaultHeaders } : defaultHeaders); // clone defaults (don’t overwrite!) - const headerOverrides = new Headers(headers); - for (const [k, v] of headerOverrides.entries()) { - if (v === undefined || v === null) -@@ -54,7 +65,7 @@ export default function createClient(clientOptions = {}) { - ...options, - ...init, - headers: baseHeaders, -- body: typeof requestBody === "string" ? requestBody : JSON.stringify(requestBody), -+ body: stringifiedBody ?? requestBody, - }); - // handle empty content - // note: we return `{}` because we want user truthy checks for `.data` or `.error` to succeed diff --git a/invokeai/frontend/web/scripts/package.json b/invokeai/frontend/web/scripts/package.json new file mode 100644 index 0000000000..3dbc1ca591 --- /dev/null +++ b/invokeai/frontend/web/scripts/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/invokeai/frontend/web/scripts/typegen.ts b/invokeai/frontend/web/scripts/typegen.ts new file mode 100644 index 0000000000..39d0b25d30 --- /dev/null +++ b/invokeai/frontend/web/scripts/typegen.ts @@ -0,0 +1,23 @@ +import fs from 'node:fs'; +import openapiTS from 'openapi-typescript'; + +const OPENAPI_URL = 'http://localhost:9090/openapi.json'; +const OUTPUT_FILE = 'src/services/api/schema.d.ts'; + +async function main() { + process.stdout.write( + `Generating types "${OPENAPI_URL}" --> "${OUTPUT_FILE}"...` + ); + const types = await openapiTS(OPENAPI_URL, { + exportType: true, + transform: (schemaObject, metadata) => { + if ('format' in schemaObject && schemaObject.format === 'binary') { + return schemaObject.nullable ? 'Blob | null' : 'Blob'; + } + }, + }); + fs.writeFileSync(OUTPUT_FILE, types); + process.stdout.write(` OK!\r\n`); +} + +main(); diff --git a/invokeai/frontend/web/src/services/api/schema.d.ts b/invokeai/frontend/web/src/services/api/schema.d.ts index 0854af4c3b..5add18a73f 100644 --- a/invokeai/frontend/web/src/services/api/schema.d.ts +++ b/invokeai/frontend/web/src/services/api/schema.d.ts @@ -75,25 +75,37 @@ export type paths = { * @description Gets a list of models */ get: operations["list_models"]; - /** - * Update Model - * @description Add Model - */ - post: operations["update_model"]; - }; - "/api/v1/models/import": { /** * Import Model * @description Add a model using its local path, repo_id, or remote URL */ post: operations["import_model"]; }; - "/api/v1/models/{model_name}": { + "/api/v1/models/{base_model}/{model_type}/{model_name}": { /** * Delete Model * @description Delete Model */ delete: operations["del_model"]; + /** + * Update Model + * @description Add Model + */ + patch: operations["update_model"]; + }; + "/api/v1/models/convert/{base_model}/{model_type}/{model_name}": { + /** + * Convert Model + * @description Convert a checkpoint model into a diffusers model + */ + put: operations["convert_model"]; + }; + "/api/v1/models/merge/{base_model}": { + /** + * Merge Models + * @description Convert a checkpoint model into a diffusers model + */ + put: operations["merge_models"]; }; "/api/v1/images/": { /** @@ -234,23 +246,6 @@ export type components = { */ b?: number; }; - /** AddModelResult */ - AddModelResult: { - /** - * Name - * @description The name of the model after import - */ - name: string; - /** @description The type of model */ - model_type: components["schemas"]["ModelType"]; - /** @description The base model */ - base_model: components["schemas"]["BaseModelType"]; - /** - * Config - * @description The configuration of the model - */ - config: components["schemas"]["ModelConfigBase"]; - }; /** * BaseModelType * @description An enumeration. @@ -324,6 +319,48 @@ export type components = { */ image_name: string; }; + /** Body_import_model */ + Body_import_model: { + /** + * Location + * @description A model path, repo_id or URL to import + */ + location: string; + /** + * Prediction Type + * @description Prediction type for SDv2 checkpoint files + * @default v_prediction + * @enum {string} + */ + prediction_type?: "v_prediction" | "epsilon" | "sample"; + }; + /** Body_merge_models */ + Body_merge_models: { + /** + * Model Names + * @description model name + */ + model_names: (string)[]; + /** + * Merged Model Name + * @description Name of destination model + */ + merged_model_name: string; + /** + * Alpha + * @description Alpha weighting strength to apply to 2d and 3d models + * @default 0.5 + */ + alpha?: number; + /** @description Interpolation method */ + interp: components["schemas"]["MergeInterpolationMethod"]; + /** + * Force + * @description Force merging of models created with different versions of diffusers + * @default false + */ + force?: boolean; + }; /** Body_remove_board_image */ Body_remove_board_image: { /** @@ -343,7 +380,7 @@ export type components = { * File * Format: binary */ - file: string; + file: Blob; }; /** * CannyImageProcessorInvocation @@ -385,55 +422,6 @@ export type components = { */ high_threshold?: number; }; - /** CkptModelInfo */ - CkptModelInfo: { - /** - * Description - * @description A description of the model - */ - description?: string; - /** - * Model Name - * @description The name of the model - */ - model_name: string; - /** - * Model Type - * @description The type of the model - */ - model_type: string; - /** - * Format - * @default ckpt - * @enum {string} - */ - format?: "ckpt"; - /** - * Config - * @description The path to the model config - */ - config: string; - /** - * Weights - * @description The path to the model weights - */ - weights: string; - /** - * Vae - * @description The path to the model VAE - */ - vae: string; - /** - * Width - * @description The width of the model - */ - width?: number; - /** - * Height - * @description The height of the model - */ - height?: number; - }; /** ClipField */ ClipField: { /** @@ -836,19 +824,6 @@ export type components = { */ control?: components["schemas"]["ControlField"]; }; - /** CreateModelRequest */ - CreateModelRequest: { - /** - * Name - * @description The name of the model - */ - name: string; - /** - * Info - * @description The model info - */ - info: components["schemas"]["CkptModelInfo"] | components["schemas"]["DiffusersModelInfo"]; - }; /** * CvInpaintInvocation * @description Simple inpaint using opencv. @@ -882,45 +857,6 @@ export type components = { */ mask?: components["schemas"]["ImageField"]; }; - /** DiffusersModelInfo */ - DiffusersModelInfo: { - /** - * Description - * @description A description of the model - */ - description?: string; - /** - * Model Name - * @description The name of the model - */ - model_name: string; - /** - * Model Type - * @description The type of the model - */ - model_type: string; - /** - * Format - * @default folder - * @enum {string} - */ - format?: "folder"; - /** - * Vae - * @description The VAE repo to use for this model - */ - vae?: components["schemas"]["VaeRepo"]; - /** - * Repo Id - * @description The repo ID to use for this model - */ - repo_id?: string; - /** - * Path - * @description The path to the model - */ - path?: string; - }; /** * DivideInvocation * @description Divides two numbers @@ -1110,7 +1046,7 @@ export type components = { * @description The nodes in this graph */ nodes?: { - [key: string]: (components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]) | undefined; + [key: string]: (components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]) | undefined; }; /** * Edges @@ -1153,7 +1089,7 @@ export type components = { * @description The results of node executions */ results: { - [key: string]: (components["schemas"]["IntCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["CompelOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["IntOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PromptOutput"] | components["schemas"]["PromptCollectionOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]) | undefined; + [key: string]: (components["schemas"]["ImageOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["LoraLoaderOutput"] | components["schemas"]["VaeLoaderOutput"] | components["schemas"]["PromptOutput"] | components["schemas"]["PromptCollectionOutput"] | components["schemas"]["CompelOutput"] | components["schemas"]["ClipSkipInvocationOutput"] | components["schemas"]["IntOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["IntCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["GraphInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["CollectInvocationOutput"]) | undefined; }; /** * Errors @@ -2055,24 +1991,6 @@ export type components = { */ thumbnail_url: string; }; - /** ImportModelResponse */ - ImportModelResponse: { - /** - * Name - * @description The name of the imported model - */ - name: string; - /** - * Info - * @description The model info - */ - info: components["schemas"]["AddModelResult"]; - /** - * Status - * @description The status of the API response - */ - status: string; - }; /** * InfillColorInvocation * @description Infills transparent areas of an image with a solid color @@ -3020,6 +2938,12 @@ export type components = { */ min_confidence?: number; }; + /** + * MergeInterpolationMethod + * @description An enumeration. + * @enum {string} + */ + MergeInterpolationMethod: "weighted_sum" | "sigmoid" | "inv_sigmoid" | "add_difference"; /** * MidasDepthImageProcessorInvocation * @description Applies Midas depth processing to image @@ -3112,16 +3036,6 @@ export type components = { */ thr_d?: number; }; - /** ModelConfigBase */ - ModelConfigBase: { - /** Path */ - path: string; - /** Description */ - description?: string; - /** Model Format */ - model_format?: string; - error?: components["schemas"]["ModelError"]; - }; /** * ModelError * @description An enumeration. @@ -3184,7 +3098,7 @@ export type components = { /** ModelsList */ ModelsList: { /** Models */ - models: (components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"])[]; + models: (components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"])[]; }; /** * MultiplyInvocation @@ -4462,24 +4376,6 @@ export type components = { * @enum {string} */ VaeModelFormat: "checkpoint" | "diffusers"; - /** VaeRepo */ - VaeRepo: { - /** - * Repo Id - * @description The repo ID to use for this VAE - */ - repo_id: string; - /** - * Path - * @description The path to the VAE - */ - path?: string; - /** - * Subfolder - * @description The subfolder to use for this VAE - */ - subfolder?: string; - }; /** ValidationError */ ValidationError: { /** Location */ @@ -4517,18 +4413,18 @@ export type components = { */ image?: components["schemas"]["ImageField"]; }; - /** - * StableDiffusion1ModelFormat - * @description An enumeration. - * @enum {string} - */ - StableDiffusion1ModelFormat: "checkpoint" | "diffusers"; /** * StableDiffusion2ModelFormat * @description An enumeration. * @enum {string} */ StableDiffusion2ModelFormat: "checkpoint" | "diffusers"; + /** + * StableDiffusion1ModelFormat + * @description An enumeration. + * @enum {string} + */ + StableDiffusion1ModelFormat: "checkpoint" | "diffusers"; }; responses: never; parameters: never; @@ -4639,7 +4535,7 @@ export type operations = { }; requestBody: { content: { - "application/json": components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; + "application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; }; }; responses: { @@ -4676,7 +4572,7 @@ export type operations = { }; requestBody: { content: { - "application/json": components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; + "application/json": components["schemas"]["LoadImageInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["ImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["LoraLoaderInvocation"] | components["schemas"]["VaeLoaderInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ClipSkipInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ParamIntInvocation"] | components["schemas"]["ParamFloatInvocation"] | components["schemas"]["TextToLatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["UpscaleInvocation"] | components["schemas"]["RestoreFaceInvocation"] | components["schemas"]["InpaintInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["GraphInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["OpenposeImageProcessorInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["LatentsToLatentsInvocation"]; }; }; responses: { @@ -4895,59 +4791,35 @@ export type operations = { }; }; }; - /** - * Update Model - * @description Add Model - */ - update_model: { - requestBody: { - content: { - "application/json": components["schemas"]["CreateModelRequest"]; - }; - }; - responses: { - /** @description Successful Response */ - 200: { - content: { - "application/json": unknown; - }; - }; - /** @description Validation Error */ - 422: { - content: { - "application/json": components["schemas"]["HTTPValidationError"]; - }; - }; - }; - }; /** * Import Model * @description Add a model using its local path, repo_id, or remote URL */ import_model: { - parameters: { - query: { - /** @description A model path, repo_id or URL to import */ - name: string; - /** @description Prediction type for SDv2 checkpoint files */ - prediction_type?: "v_prediction" | "epsilon" | "sample"; + requestBody: { + content: { + "application/json": components["schemas"]["Body_import_model"]; }; }; responses: { /** @description The model imported successfully */ 201: { content: { - "application/json": components["schemas"]["ImportModelResponse"]; + "application/json": components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"]; }; }; /** @description The model could not be found */ 404: never; + /** @description There is already a model corresponding to this path or repo_id */ + 409: never; /** @description Validation Error */ 422: { content: { "application/json": components["schemas"]["HTTPValidationError"]; }; }; + /** @description The model appeared to import successfully, but could not be found in the model manager */ + 424: never; }; }; /** @@ -4957,6 +4829,11 @@ export type operations = { del_model: { parameters: { path: { + /** @description Base model */ + base_model: components["schemas"]["BaseModelType"]; + /** @description The type of model */ + model_type: components["schemas"]["ModelType"]; + /** @description model name */ model_name: string; }; }; @@ -4979,6 +4856,114 @@ export type operations = { }; }; }; + /** + * Update Model + * @description Add Model + */ + update_model: { + parameters: { + path: { + /** @description Base model */ + base_model: components["schemas"]["BaseModelType"]; + /** @description The type of model */ + model_type: components["schemas"]["ModelType"]; + /** @description model name */ + model_name: string; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"]; + }; + }; + responses: { + /** @description The model was updated successfully */ + 200: { + content: { + "application/json": components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"]; + }; + }; + /** @description Bad request */ + 400: never; + /** @description The model could not be found */ + 404: never; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** + * Convert Model + * @description Convert a checkpoint model into a diffusers model + */ + convert_model: { + parameters: { + path: { + /** @description Base model */ + base_model: components["schemas"]["BaseModelType"]; + /** @description The type of model */ + model_type: components["schemas"]["ModelType"]; + /** @description model name */ + model_name: string; + }; + }; + responses: { + /** @description Model converted successfully */ + 200: { + content: { + "application/json": components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"]; + }; + }; + /** @description Bad request */ + 400: never; + /** @description Model not found */ + 404: never; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** + * Merge Models + * @description Convert a checkpoint model into a diffusers model + */ + merge_models: { + parameters: { + path: { + /** @description Base model */ + base_model: components["schemas"]["BaseModelType"]; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["Body_merge_models"]; + }; + }; + responses: { + /** @description Model converted successfully */ + 200: { + content: { + "application/json": components["schemas"]["StableDiffusion1ModelDiffusersConfig"] | components["schemas"]["StableDiffusion1ModelCheckpointConfig"] | components["schemas"]["VaeModelConfig"] | components["schemas"]["LoRAModelConfig"] | components["schemas"]["ControlNetModelConfig"] | components["schemas"]["TextualInversionModelConfig"] | components["schemas"]["StableDiffusion2ModelCheckpointConfig"] | components["schemas"]["StableDiffusion2ModelDiffusersConfig"]; + }; + }; + /** @description Incompatible models */ + 400: never; + /** @description One or more models not found */ + 404: never; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; /** * List Images With Metadata * @description Gets a list of images diff --git a/invokeai/frontend/web/src/services/api/thunks/image.ts b/invokeai/frontend/web/src/services/api/thunks/image.ts index d6e502bc54..71eedb0327 100644 --- a/invokeai/frontend/web/src/services/api/thunks/image.ts +++ b/invokeai/frontend/web/src/services/api/thunks/image.ts @@ -157,8 +157,6 @@ export const imageUploaded = createAppAsyncThunk< session_id, } = arg; const { post } = $client.get(); - const formData = new FormData(); - formData.append('file', file); const { data, error, response } = await post('/api/v1/images/', { params: { query: { @@ -167,10 +165,12 @@ export const imageUploaded = createAppAsyncThunk< session_id, }, }, - // TODO: Proper handling of `multipart/form-data` is coming soon, will fix type issues - // https://github.com/drwpow/openapi-typescript/issues/1123 - // @ts-ignore - body: formData, + body: { file }, + bodySerializer: (body) => { + const formData = new FormData(); + formData.append('file', body.file); + return formData; + }, }); if (error) { diff --git a/invokeai/frontend/web/tsconfig.json b/invokeai/frontend/web/tsconfig.json index 6f74cf3681..e722e2f9a8 100644 --- a/invokeai/frontend/web/tsconfig.json +++ b/invokeai/frontend/web/tsconfig.json @@ -23,5 +23,11 @@ }, "include": ["src/**/*.ts", "src/**/*.tsx", "*.d.ts"], "exclude": ["src/services/fixtures/*", "node_modules", "dist"], - "references": [{ "path": "./tsconfig.node.json" }] + "references": [{ "path": "./tsconfig.node.json" }], + "ts-node": { + "compilerOptions": { + "jsx": "preserve" + }, + "esm": true + } } diff --git a/invokeai/frontend/web/yarn.lock b/invokeai/frontend/web/yarn.lock index 770cc1ba7d..0cec46b9b0 100644 --- a/invokeai/frontend/web/yarn.lock +++ b/invokeai/frontend/web/yarn.lock @@ -4941,10 +4941,10 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -openapi-fetch@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/openapi-fetch/-/openapi-fetch-0.4.0.tgz#45c368321ba6c15bc2e168e7dc3fbf322e9cca6d" - integrity sha512-4lzZtH5J1ZH9EXfmpcmKi0gOgjy0hc6BAcucAdCmLHY6jZopMeGP51vD3Cd4rE1nTFMfJzmYDc8ar0+364gBVw== +openapi-fetch@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/openapi-fetch/-/openapi-fetch-0.6.1.tgz#90d785ead213b82beb8f094a756ad9320ba28b32" + integrity sha512-CGWPqqtL31uC2e4eEU9NHoqYMXnJ7Jk4H/4Yguil4tO22MIZi91hlQJ/51E8CiaKdSTODh03yF4ndjIOABVHUw== openapi-types@^12.1.3: version "12.1.3" From a4dec53b4d71ced1657a2159be991b98f5b15b88 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 7 Jul 2023 19:05:10 +1200 Subject: [PATCH 07/21] fix: Adjust clip skip layer count based on model --- .../Parameters/Advanced/ParamClipSkip.tsx | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx index a096d3d3fc..97a02b0c4b 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx @@ -5,11 +5,26 @@ import { setClipSkip } from 'features/parameters/store/generationSlice'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +const clipSkipMap = { + 'sd-1': { + maxClip: 12, + markers: [0, 1, 2, 3, 4, 8, 12], + }, + 'sd-2': { + maxClip: 24, + markers: [0, 1, 2, 3, 5, 10, 15, 20, 24], + }, +}; + export default function ParamClipSkip() { const clipSkip = useAppSelector( (state: RootState) => state.generation.clipSkip ); + const selectedModelId = useAppSelector( + (state: RootState) => state.generation.model + ).split('/')[0]; + const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -29,12 +44,14 @@ export default function ParamClipSkip() { label={t('parameters.clipSkip')} aria-label={t('parameters.clipSkip')} min={0} - max={30} + max={clipSkipMap[selectedModelId as keyof typeof clipSkipMap].maxClip} step={1} value={clipSkip} onChange={handleClipSkipChange} withSliderMarks - sliderMarks={[0, 1, 2, 3, 5, 10, 15, 25, 30]} + sliderMarks={ + clipSkipMap[selectedModelId as keyof typeof clipSkipMap].markers + } withInput withReset handleReset={handleClipSkipReset} From 53cb200f856c266aa84f55adb29af82bec6b872c Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 7 Jul 2023 19:29:11 +1200 Subject: [PATCH 08/21] fix: Clamp clipskip value when model changes --- .../components/Parameters/Advanced/ParamClipSkip.tsx | 2 +- .../web/src/features/parameters/store/generationSlice.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx index 97a02b0c4b..a4f9d37f3c 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx @@ -5,7 +5,7 @@ import { setClipSkip } from 'features/parameters/store/generationSlice'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const clipSkipMap = { +export const clipSkipMap = { 'sd-1': { maxClip: 12, markers: [0, 1, 2, 3, 4, 8, 12], diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 3e9356747d..0a5d358935 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -5,6 +5,7 @@ import { configChanged } from 'features/system/store/configSlice'; import { setShouldShowAdvancedOptions } from 'features/ui/store/uiSlice'; import { clamp } from 'lodash-es'; import { ImageDTO } from 'services/api/types'; +import { clipSkipMap } from '../components/Parameters/Advanced/ParamClipSkip'; import { CfgScaleParam, HeightParam, @@ -216,6 +217,12 @@ export const generationSlice = createSlice({ }, modelSelected: (state, action: PayloadAction) => { state.model = action.payload; + + // Clamp ClipSkip Based On Selected Model + const clipSkipMax = + clipSkipMap[action.payload.split('/')[0] as keyof typeof clipSkipMap] + .maxClip; + state.clipSkip = clamp(state.clipSkip, 0, clipSkipMax); }, vaeSelected: (state, action: PayloadAction) => { state.vae = action.payload; From 74557c8b6e7878b45ef1e6f71a34bf7aa4fc32c7 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 7 Jul 2023 23:27:21 +1200 Subject: [PATCH 09/21] fix: Loras breaking with clip skip --- .../nodes/util/graphBuilders/addLoRAsToGraph.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addLoRAsToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addLoRAsToGraph.ts index 9712ef4d5f..74cc8b1f57 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addLoRAsToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addLoRAsToGraph.ts @@ -4,6 +4,7 @@ import { forEach, size } from 'lodash-es'; import { LoraLoaderInvocation } from 'services/api/types'; import { modelIdToLoRAModelField } from '../modelIdToLoRAName'; import { + CLIP_SKIP, LORA_LOADER, MAIN_MODEL_LOADER, NEGATIVE_CONDITIONING, @@ -27,14 +28,19 @@ export const addLoRAsToGraph = ( const loraCount = size(loras); if (loraCount > 0) { - // remove any existing connections from main model loader, we need to insert the lora nodes + // Remove MAIN_MODEL_LOADER unet connection to feed it to LoRAs graph.edges = graph.edges.filter( (e) => !( e.source.node_id === MAIN_MODEL_LOADER && - ['unet', 'clip'].includes(e.source.field) + ['unet'].includes(e.source.field) ) ); + // Remove CLIP_SKIP connections to conditionings to feed it through LoRAs + graph.edges = graph.edges.filter( + (e) => + !(e.source.node_id === CLIP_SKIP && ['clip'].includes(e.source.field)) + ); } // we need to remember the last lora so we can chain from it @@ -73,7 +79,7 @@ export const addLoRAsToGraph = ( graph.edges.push({ source: { - node_id: MAIN_MODEL_LOADER, + node_id: CLIP_SKIP, field: 'clip', }, destination: { From a8fc75b6d0f28ad40eadb26886456bc5a6acb172 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 7 Jul 2023 21:42:16 +1000 Subject: [PATCH 10/21] feat(ui): make clipSkip activeLabel "Clip Skip" we know its active if it displays --- .../components/Parameters/Advanced/ParamAdvancedCollapse.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx index 5eae7f2800..984ad833a6 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse.tsx @@ -11,7 +11,7 @@ const selector = createSelector( (state: RootState) => { const clipSkip = state.generation.clipSkip; return { - activeLabel: clipSkip > 0 ? `Clip Skip Active` : undefined, + activeLabel: clipSkip > 0 ? 'Clip Skip' : undefined, }; }, defaultSelectorOptions From 6356dc335f85ed69c2324db54d61563779e17298 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Thu, 6 Jul 2023 11:54:16 -0400 Subject: [PATCH 11/21] change model store to object, update main model and vae dropdowns --- .../nodes/util/graphBuilders/addVAEToGraph.ts | 14 ++++++---- .../buildCanvasImageToImageGraph.ts | 4 +-- .../graphBuilders/buildCanvasInpaintGraph.ts | 4 +-- .../buildCanvasTextToImageGraph.ts | 4 +-- .../buildLinearImageToImageGraph.ts | 4 +-- .../buildLinearTextToImageGraph.ts | 4 +-- .../parameters/store/generationSlice.ts | 28 +++++++++++-------- .../parameters/store/parameterZodSchemas.ts | 17 +++++------ .../system/components/ModelSelect.tsx | 14 ++++------ .../features/system/components/VAESelect.tsx | 12 ++++---- 10 files changed, 55 insertions(+), 50 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts index 4dd3d644ee..e710a642ed 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts @@ -16,10 +16,12 @@ export const addVAEToGraph = ( graph: NonNullableGraph, state: RootState ): void => { - const { vae: vaeId } = state.generation; - const vae_model = modelIdToVAEModelField(vaeId); + const { vae } = state.generation; + const vae_model = modelIdToVAEModelField(vae?.id || ''); - if (vaeId !== 'auto') { + const isAutoVae = vae?.id === 'auto'; + + if (!isAutoVae) { graph.nodes[VAE_LOADER] = { type: 'vae_loader', id: VAE_LOADER, @@ -30,7 +32,7 @@ export const addVAEToGraph = ( if (graph.id === TEXT_TO_IMAGE_GRAPH || graph.id === IMAGE_TO_IMAGE_GRAPH) { graph.edges.push({ source: { - node_id: vaeId === 'auto' ? MAIN_MODEL_LOADER : VAE_LOADER, + node_id: isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER, field: 'vae', }, destination: { @@ -43,7 +45,7 @@ export const addVAEToGraph = ( if (graph.id === IMAGE_TO_IMAGE_GRAPH) { graph.edges.push({ source: { - node_id: vaeId === 'auto' ? MAIN_MODEL_LOADER : VAE_LOADER, + node_id: isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER, field: 'vae', }, destination: { @@ -56,7 +58,7 @@ export const addVAEToGraph = ( if (graph.id === INPAINT_GRAPH) { graph.edges.push({ source: { - node_id: vaeId === 'auto' ? MAIN_MODEL_LOADER : VAE_LOADER, + node_id: isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER, field: 'vae', }, destination: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts index 7d7b9cf4c9..419e6af93b 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasImageToImageGraph.ts @@ -36,7 +36,7 @@ export const buildCanvasImageToImageGraph = ( const { positivePrompt, negativePrompt, - model: modelId, + model: currentModel, cfgScale: cfg_scale, scheduler, steps, @@ -50,7 +50,7 @@ export const buildCanvasImageToImageGraph = ( // The bounding box determines width and height, not the width and height params const { width, height } = state.canvas.boundingBoxDimensions; - const model = modelIdToMainModelField(modelId); + const model = modelIdToMainModelField(currentModel?.id || ''); /** * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts index 8425b1dac5..2bac864015 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasInpaintGraph.ts @@ -35,7 +35,7 @@ export const buildCanvasInpaintGraph = ( const { positivePrompt, negativePrompt, - model: modelId, + model: currentModel, cfgScale: cfg_scale, scheduler, steps, @@ -59,7 +59,7 @@ export const buildCanvasInpaintGraph = ( // We may need to set the inpaint width and height to scale the image const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas; - const model = modelIdToMainModelField(modelId); + const model = modelIdToMainModelField(currentModel?.id || ''); const graph: NonNullableGraph = { id: INPAINT_GRAPH, diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts index 25d7b13d7e..70e167aead 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildCanvasTextToImageGraph.ts @@ -25,7 +25,7 @@ export const buildCanvasTextToImageGraph = ( const { positivePrompt, negativePrompt, - model: modelId, + model: currentModel, cfgScale: cfg_scale, scheduler, steps, @@ -38,7 +38,7 @@ export const buildCanvasTextToImageGraph = ( // The bounding box determines width and height, not the width and height params const { width, height } = state.canvas.boundingBoxDimensions; - const model = modelIdToMainModelField(modelId); + const model = modelIdToMainModelField(currentModel?.id || ''); /** * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts index b845d2d54f..8adf2cf342 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearImageToImageGraph.ts @@ -38,7 +38,7 @@ export const buildLinearImageToImageGraph = ( const { positivePrompt, negativePrompt, - model: modelId, + model: currentModel, cfgScale: cfg_scale, scheduler, steps, @@ -73,7 +73,7 @@ export const buildLinearImageToImageGraph = ( throw new Error('No initial image found in state'); } - const model = modelIdToMainModelField(modelId); + const model = modelIdToMainModelField(currentModel?.id || ''); // copy-pasted graph from node editor, filled in with state values & friendly node ids const graph: NonNullableGraph = { diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts index b743a7221a..28ff3656c1 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts @@ -22,7 +22,7 @@ export const buildLinearTextToImageGraph = ( const { positivePrompt, negativePrompt, - model: modelId, + model: currentModel, cfgScale: cfg_scale, scheduler, steps, @@ -31,7 +31,7 @@ export const buildLinearTextToImageGraph = ( clipSkip, } = state.generation; - const model = modelIdToMainModelField(modelId); + const model = modelIdToMainModelField(currentModel?.id || ''); /** * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 0a5d358935..753f46ea7d 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -16,7 +16,6 @@ import { SeedParam, StepsParam, StrengthParam, - VAEParam, WidthParam, } from './parameterZodSchemas'; @@ -50,7 +49,7 @@ export interface GenerationState { horizontalSymmetrySteps: number; verticalSymmetrySteps: number; model: ModelParam; - vae: VAEParam; + vae: ModelParam; seamlessXAxis: boolean; seamlessYAxis: boolean; clipSkip: number; @@ -84,8 +83,8 @@ export const initialGenerationState: GenerationState = { shouldUseSymmetry: false, horizontalSymmetrySteps: 0, verticalSymmetrySteps: 0, - model: '', - vae: '', + model: null, + vae: null, seamlessXAxis: false, seamlessYAxis: false, clipSkip: 0, @@ -216,16 +215,17 @@ export const generationSlice = createSlice({ state.initialImage = { imageName: image_name, width, height }; }, modelSelected: (state, action: PayloadAction) => { - state.model = action.payload; + const [base_model, type, name] = action.payload.split('/'); // Clamp ClipSkip Based On Selected Model - const clipSkipMax = - clipSkipMap[action.payload.split('/')[0] as keyof typeof clipSkipMap] - .maxClip; - state.clipSkip = clamp(state.clipSkip, 0, clipSkipMax); + const { maxClip } = clipSkipMap[base_model as keyof typeof clipSkipMap]; + state.clipSkip = clamp(state.clipSkip, 0, maxClip); + + state.model = { id: action.payload, base_model, name, type }; }, vaeSelected: (state, action: PayloadAction) => { - state.vae = action.payload; + const [base_model, type, name] = action.payload.split('/'); + state.vae = { id: action.payload, base_model, name, type }; }, setClipSkip: (state, action: PayloadAction) => { state.clipSkip = action.payload; @@ -235,7 +235,13 @@ export const generationSlice = createSlice({ builder.addCase(configChanged, (state, action) => { const defaultModel = action.payload.sd?.defaultModel; if (defaultModel && !state.model) { - state.model = defaultModel; + const [base_model, model_type, model_name] = defaultModel.split('/'); + state.model = { + id: defaultModel, + name: model_name, + type: model_type, + base_model: base_model, + }; } }); builder.addCase(setShouldShowAdvancedOptions, (state, action) => { diff --git a/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts b/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts index 12d77beeb9..996031a06e 100644 --- a/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts +++ b/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts @@ -130,20 +130,21 @@ export const isValidHeight = (val: unknown): val is HeightParam => * Zod schema for model parameter * TODO: Make this a dynamically generated enum? */ -export const zModel = z.string(); +const zModel = z.object({ + id: z.string(), + name: z.string(), + type: z.string(), + base_model: z.string(), +}); + /** * Type alias for model parameter, inferred from its zod schema */ -export type ModelParam = z.infer; -/** - * Zod schema for VAE parameter - * TODO: Make this a dynamically generated enum? - */ -export const zVAE = z.string(); +export type ModelParam = z.infer | null; /** * Type alias for model parameter, inferred from its zod schema */ -export type VAEParam = z.infer; +export type VAEParam = z.infer | null; /** * Validates/type-guards a value as a model parameter */ diff --git a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx index 4eeee3e4c6..4336792858 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx @@ -19,7 +19,7 @@ const ModelSelect = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const selectedModelId = useAppSelector( + const currentModel = useAppSelector( (state: RootState) => state.generation.model ); @@ -48,8 +48,8 @@ const ModelSelect = () => { }, [mainModels]); const selectedModel = useMemo( - () => mainModels?.entities[selectedModelId], - [mainModels?.entities, selectedModelId] + () => mainModels?.entities[currentModel?.id || ''], + [mainModels?.entities, currentModel] ); const handleChangeModel = useCallback( @@ -63,10 +63,6 @@ const ModelSelect = () => { ); useEffect(() => { - if (selectedModelId && mainModels?.ids.includes(selectedModelId)) { - return; - } - const firstModel = mainModels?.ids[0]; if (!isString(firstModel)) { @@ -74,7 +70,7 @@ const ModelSelect = () => { } handleChangeModel(firstModel); - }, [handleChangeModel, mainModels?.ids, selectedModelId]); + }, [handleChangeModel, mainModels?.ids]); return isLoading ? ( { 0 ? 'Select a model' : 'No models detected!'} data={data} error={data.length === 0} diff --git a/invokeai/frontend/web/src/features/system/components/VAESelect.tsx b/invokeai/frontend/web/src/features/system/components/VAESelect.tsx index 33901b5bef..bc96783271 100644 --- a/invokeai/frontend/web/src/features/system/components/VAESelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/VAESelect.tsx @@ -18,7 +18,7 @@ const VAESelect = () => { const { data: vaeModels } = useGetVaeModelsQuery(); - const selectedModelId = useAppSelector( + const currentModel = useAppSelector( (state: RootState) => state.generation.vae ); @@ -51,8 +51,8 @@ const VAESelect = () => { }, [vaeModels]); const selectedModel = useMemo( - () => vaeModels?.entities[selectedModelId], - [vaeModels?.entities, selectedModelId] + () => vaeModels?.entities[currentModel?.id || ''], + [vaeModels?.entities, currentModel] ); const handleChangeModel = useCallback( @@ -66,17 +66,17 @@ const VAESelect = () => { ); useEffect(() => { - if (selectedModelId && vaeModels?.ids.includes(selectedModelId)) { + if (currentModel?.id && vaeModels?.ids.includes(currentModel?.id)) { return; } handleChangeModel('auto'); - }, [handleChangeModel, vaeModels?.ids, selectedModelId]); + }, [handleChangeModel, vaeModels?.ids, currentModel?.id]); return ( Date: Thu, 6 Jul 2023 14:40:51 -0400 Subject: [PATCH 12/21] disable submodels that have incompatible base models --- .../components/IAIMantineMultiSelect.tsx | 2 +- .../IAIMantineSelectItemWithTooltip.tsx | 40 ++++++++++++++++ .../components/ParamEmbeddingPopover.tsx | 47 +++++-------------- .../lora/components/ParamLoraSelect.tsx | 43 +++++------------ .../parameters/store/generationSlice.ts | 7 ++- .../parameters/store/parameterZodSchemas.ts | 7 ++- .../features/system/components/VAESelect.tsx | 27 +++++++---- 7 files changed, 94 insertions(+), 79 deletions(-) create mode 100644 invokeai/frontend/web/src/common/components/IAIMantineSelectItemWithTooltip.tsx diff --git a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx index 9a0bc865a4..6cb2e4a504 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx @@ -32,7 +32,7 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => { const { colorMode } = useColorMode(); return ( - + { + label: string; + description?: string; + tooltip?: string; +} + +const IAIMantineSelectItemWithTooltip = forwardRef( + ({ label, tooltip, description, ...others }: ItemProps, ref) => ( +
+ {tooltip ? ( + +
+ {label} + {description && ( + + {description} + + )} +
+
+ ) : ( +
+ {label} + {description && ( + + {description} + + )} +
+ )} +
+ ) +); + +IAIMantineSelectItemWithTooltip.displayName = 'IAIMantineSelectItemWithTooltip'; + +export default memo(IAIMantineSelectItemWithTooltip); diff --git a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx index 3c2ded0166..0138d7b225 100644 --- a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx +++ b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx @@ -8,15 +8,12 @@ import { } from '@chakra-ui/react'; import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect'; import { forEach } from 'lodash-es'; -import { - PropsWithChildren, - forwardRef, - useCallback, - useMemo, - useRef, -} from 'react'; +import { PropsWithChildren, useCallback, useMemo, useRef } from 'react'; import { useGetTextualInversionModelsQuery } from 'services/api/endpoints/models'; import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants'; +import { RootState } from '../../../app/store/store'; +import { useAppSelector } from '../../../app/store/storeHooks'; +import IAIMantineSelectItemWithTooltip from '../../../common/components/IAIMantineSelectItemWithTooltip'; type EmbeddingSelectItem = { label: string; @@ -35,6 +32,10 @@ const ParamEmbeddingPopover = (props: Props) => { const { data: embeddingQueryData } = useGetTextualInversionModelsQuery(); const inputRef = useRef(null); + const currentMainModel = useAppSelector( + (state: RootState) => state.generation.model + ); + const data = useMemo(() => { if (!embeddingQueryData) { return []; @@ -49,11 +50,14 @@ const ParamEmbeddingPopover = (props: Props) => { value: embedding.name, label: embedding.name, description: embedding.description, + ...(currentMainModel?.base_model !== embedding.base_model + ? { disabled: true, tooltip: 'Incompatible base model' } + : {}), }); }); return data; - }, [embeddingQueryData]); + }, [embeddingQueryData, currentMainModel?.base_model]); const handleChange = useCallback( (v: string[]) => { @@ -108,7 +112,7 @@ const ParamEmbeddingPopover = (props: Props) => { data={data} maxDropdownHeight={400} nothingFound="No Matching Embeddings" - itemComponent={SelectItem} + itemComponent={IAIMantineSelectItemWithTooltip} disabled={data.length === 0} filter={(value, selected, item: EmbeddingSelectItem) => item.label.toLowerCase().includes(value.toLowerCase().trim()) || @@ -124,28 +128,3 @@ const ParamEmbeddingPopover = (props: Props) => { }; export default ParamEmbeddingPopover; - -interface ItemProps extends React.ComponentPropsWithoutRef<'div'> { - value: string; - label: string; - description?: string; -} - -const SelectItem = forwardRef( - ({ label, description, ...others }: ItemProps, ref) => { - return ( -
-
- {label} - {description && ( - - {description} - - )} -
-
- ); - } -); - -SelectItem.displayName = 'SelectItem'; diff --git a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx index 9168814f35..4d5aa81738 100644 --- a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx +++ b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx @@ -1,13 +1,14 @@ import { Flex, Text } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; +import { RootState, stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect'; import { forEach } from 'lodash-es'; -import { forwardRef, useCallback, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { useGetLoRAModelsQuery } from 'services/api/endpoints/models'; import { loraAdded } from '../store/loraSlice'; +import IAIMantineSelectItemWithTooltip from '../../../common/components/IAIMantineSelectItemWithTooltip'; type LoraSelectItem = { label: string; @@ -28,6 +29,10 @@ const ParamLoraSelect = () => { const { loras } = useAppSelector(selector); const { data: lorasQueryData } = useGetLoRAModelsQuery(); + const currentMainModel = useAppSelector( + (state: RootState) => state.generation.model + ); + const data = useMemo(() => { if (!lorasQueryData) { return []; @@ -43,12 +48,15 @@ const ParamLoraSelect = () => { data.push({ value: id, label: lora.name, - description: lora.description, + description: 'This is a lora', + ...(currentMainModel?.base_model !== lora.base_model + ? { disabled: true, tooltip: 'Incompatible base model' } + : {}), }); }); return data; - }, [loras, lorasQueryData]); + }, [loras, lorasQueryData, currentMainModel?.base_model]); const handleChange = useCallback( (v: string[]) => { @@ -78,7 +86,7 @@ const ParamLoraSelect = () => { data={data} maxDropdownHeight={400} nothingFound="No matching LoRAs" - itemComponent={SelectItem} + itemComponent={IAIMantineSelectItemWithTooltip} disabled={data.length === 0} filter={(value, selected, item: LoraSelectItem) => item.label.toLowerCase().includes(value.toLowerCase().trim()) || @@ -89,29 +97,4 @@ const ParamLoraSelect = () => { ); }; -interface ItemProps extends React.ComponentPropsWithoutRef<'div'> { - value: string; - label: string; - description?: string; -} - -const SelectItem = forwardRef( - ({ label, description, ...others }: ItemProps, ref) => { - return ( -
-
- {label} - {description && ( - - {description} - - )} -
-
- ); - } -); - -SelectItem.displayName = 'SelectItem'; - export default ParamLoraSelect; diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 753f46ea7d..4f93cf43e5 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -49,7 +49,7 @@ export interface GenerationState { horizontalSymmetrySteps: number; verticalSymmetrySteps: number; model: ModelParam; - vae: ModelParam; + vae: VAEParam; seamlessXAxis: boolean; seamlessYAxis: boolean; clipSkip: number; @@ -84,7 +84,7 @@ export const initialGenerationState: GenerationState = { horizontalSymmetrySteps: 0, verticalSymmetrySteps: 0, model: null, - vae: null, + vae: '', seamlessXAxis: false, seamlessYAxis: false, clipSkip: 0, @@ -224,8 +224,7 @@ export const generationSlice = createSlice({ state.model = { id: action.payload, base_model, name, type }; }, vaeSelected: (state, action: PayloadAction) => { - const [base_model, type, name] = action.payload.split('/'); - state.vae = { id: action.payload, base_model, name, type }; + state.vae = action.payload; }, setClipSkip: (state, action: PayloadAction) => { state.clipSkip = action.payload; diff --git a/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts b/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts index 996031a06e..582b9a3428 100644 --- a/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts +++ b/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts @@ -141,10 +141,15 @@ const zModel = z.object({ * Type alias for model parameter, inferred from its zod schema */ export type ModelParam = z.infer | null; +/** + * Zod schema for VAE parameter + * TODO: Make this a dynamically generated enum? + */ +export const zVAE = z.string(); /** * Type alias for model parameter, inferred from its zod schema */ -export type VAEParam = z.infer | null; +export type VAEParam = z.infer; /** * Validates/type-guards a value as a model parameter */ diff --git a/invokeai/frontend/web/src/features/system/components/VAESelect.tsx b/invokeai/frontend/web/src/features/system/components/VAESelect.tsx index bc96783271..05eaf224f8 100644 --- a/invokeai/frontend/web/src/features/system/components/VAESelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/VAESelect.tsx @@ -11,6 +11,7 @@ import { useGetVaeModelsQuery } from 'services/api/endpoints/models'; import { RootState } from 'app/store/store'; import { vaeSelected } from 'features/parameters/store/generationSlice'; import { MODEL_TYPE_MAP } from './ModelSelect'; +import IAIMantineSelectItemWithTooltip from '../../../common/components/IAIMantineSelectItemWithTooltip'; const VAESelect = () => { const dispatch = useAppDispatch(); @@ -18,7 +19,11 @@ const VAESelect = () => { const { data: vaeModels } = useGetVaeModelsQuery(); - const currentModel = useAppSelector( + const currentMainModel = useAppSelector( + (state: RootState) => state.generation.model + ); + + const selectedVae = useAppSelector( (state: RootState) => state.generation.vae ); @@ -44,15 +49,18 @@ const VAESelect = () => { value: id, label: model.name, group: MODEL_TYPE_MAP[model.base_model], + ...(currentMainModel?.base_model !== model.base_model + ? { disabled: true, tooltip: 'Incompatible base model' } + : {}), }); }); return data; - }, [vaeModels]); + }, [vaeModels, currentMainModel?.base_model]); - const selectedModel = useMemo( - () => vaeModels?.entities[currentModel?.id || ''], - [vaeModels?.entities, currentModel] + const selectedVaeModel = useMemo( + () => vaeModels?.entities[selectedVae], + [vaeModels?.entities, selectedVae] ); const handleChangeModel = useCallback( @@ -66,17 +74,18 @@ const VAESelect = () => { ); useEffect(() => { - if (currentModel?.id && vaeModels?.ids.includes(currentModel?.id)) { + if (selectedVae && vaeModels?.ids.includes(selectedVae)) { return; } handleChangeModel('auto'); - }, [handleChangeModel, vaeModels?.ids, currentModel?.id]); + }, [handleChangeModel, vaeModels?.ids, selectedVae]); return ( Date: Thu, 6 Jul 2023 15:07:47 -0400 Subject: [PATCH 13/21] add modelSelected middleware to clear submodels on base_model change --- .../middleware/listenerMiddleware/index.ts | 4 +++ .../listeners/modelSelected.ts | 35 +++++++++++++++++++ .../lora/components/ParamLoraSelect.tsx | 2 +- .../web/src/features/lora/store/loraSlice.ts | 12 +++++-- .../parameters/hooks/useRecallParameters.ts | 5 ++- .../src/features/parameters/store/actions.ts | 2 ++ .../parameters/store/generationSlice.ts | 5 ++- .../system/components/ModelSelect.tsx | 2 +- 8 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.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 900fabfee9..59fa48a9b7 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -86,6 +86,7 @@ import { addRequestedBoardImageDeletionListener } from './listeners/boardImagesD import { addSelectionAddedToBatchListener } from './listeners/selectionAddedToBatch'; import { addImageDroppedListener } from './listeners/imageDropped'; import { addImageToDeleteSelectedListener } from './listeners/imageToDeleteSelected'; +import { addModelSelectedListener } from './listeners/modelSelected'; export const listenerMiddleware = createListenerMiddleware(); @@ -220,3 +221,6 @@ addSelectionAddedToBatchListener(); // DND addImageDroppedListener(); + +// Models +addModelSelectedListener(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts new file mode 100644 index 0000000000..d10a4e25e2 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts @@ -0,0 +1,35 @@ +import { + modelChanged, + vaeSelected, +} from 'features/parameters/store/generationSlice'; +import { addToast } from 'features/system/store/systemSlice'; +import { startAppListening } from '..'; +import { modelSelected } from 'features/parameters/store/actions'; +import { makeToast } from 'app/components/Toaster'; +import { lorasCleared } from '../../../../../features/lora/store/loraSlice'; + +export const addModelSelectedListener = () => { + startAppListening({ + actionCreator: modelSelected, + effect: (action, { getState, dispatch }) => { + const state = getState(); + const [base_model, type, name] = action.payload.split('/'); + + if (state.generation.model?.base_model !== base_model) { + dispatch( + addToast( + makeToast({ + title: 'Base model changed, clearing submodels', + status: 'warning', + }) + ) + ); + dispatch(vaeSelected('auto')); + dispatch(lorasCleared()); + // TODO: controlnet cleared + } + + dispatch(modelChanged({ id: action.payload, base_model, name, type })); + }, + }); +}; diff --git a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx index 4d5aa81738..b2455ed706 100644 --- a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx +++ b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx @@ -48,7 +48,7 @@ const ParamLoraSelect = () => { data.push({ value: id, label: lora.name, - description: 'This is a lora', + description: lora.description, ...(currentMainModel?.base_model !== lora.base_model ? { disabled: true, tooltip: 'Incompatible base model' } : {}), diff --git a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts index 7da6018e58..bab6f2f7e1 100644 --- a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts +++ b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts @@ -31,6 +31,9 @@ export const loraSlice = createSlice({ const id = action.payload; delete state.loras[id]; }, + lorasCleared: (state, action: PayloadAction<>) => { + state.loras = {}; + }, loraWeightChanged: ( state, action: PayloadAction<{ id: string; weight: number }> @@ -45,7 +48,12 @@ export const loraSlice = createSlice({ }, }); -export const { loraAdded, loraRemoved, loraWeightChanged, loraWeightReset } = - loraSlice.actions; +export const { + loraAdded, + loraRemoved, + loraWeightChanged, + loraWeightReset, + lorasCleared, +} = loraSlice.actions; export default loraSlice.reducer; diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index f504c62ed6..71c054c40d 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -2,7 +2,6 @@ import { useAppDispatch } from 'app/store/storeHooks'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { - modelSelected, setCfgScale, setHeight, setImg2imgStrength, @@ -14,7 +13,7 @@ import { setWidth, } from '../store/generationSlice'; import { isImageField } from 'services/api/guards'; -import { initialImageSelected } from '../store/actions'; +import { initialImageSelected, modelSelected } from '../store/actions'; import { useAppToaster } from 'app/components/Toaster'; import { ImageDTO } from 'services/api/types'; import { @@ -163,7 +162,7 @@ export const useRecallParameters = () => { parameterNotSetToast(); return; } - dispatch(modelSelected(model)); + dispatch(modelSelected(model?.id || '')); parameterSetToast(); }, [dispatch, parameterSetToast, parameterNotSetToast] diff --git a/invokeai/frontend/web/src/features/parameters/store/actions.ts b/invokeai/frontend/web/src/features/parameters/store/actions.ts index 2fb56c0883..a74a2f633d 100644 --- a/invokeai/frontend/web/src/features/parameters/store/actions.ts +++ b/invokeai/frontend/web/src/features/parameters/store/actions.ts @@ -4,3 +4,5 @@ import { ImageDTO } from 'services/api/types'; export const initialImageSelected = createAction( 'generation/initialImageSelected' ); + +export const modelSelected = createAction('generation/modelSelected'); diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 4f93cf43e5..83262d3aa8 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -223,6 +223,9 @@ export const generationSlice = createSlice({ state.model = { id: action.payload, base_model, name, type }; }, + modelChanged: (state, action: PayloadAction) => { + state.model = action.payload; + }, vaeSelected: (state, action: PayloadAction) => { state.vae = action.payload; }, @@ -282,7 +285,7 @@ export const { setHorizontalSymmetrySteps, setVerticalSymmetrySteps, initialImageChanged, - modelSelected, + modelChanged, vaeSelected, setShouldUseNoiseSettings, setSeamlessXAxis, diff --git a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx index 4336792858..0e49cae1df 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx @@ -3,12 +3,12 @@ import { useTranslation } from 'react-i18next'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIMantineSelect from 'common/components/IAIMantineSelect'; -import { modelSelected } from 'features/parameters/store/generationSlice'; import { SelectItem } from '@mantine/core'; import { RootState } from 'app/store/store'; import { forEach, isString } from 'lodash-es'; import { useGetMainModelsQuery } from 'services/api/endpoints/models'; +import { modelSelected } from '../../parameters/store/actions'; export const MODEL_TYPE_MAP = { 'sd-1': 'Stable Diffusion 1.x', From 8457fcf7d3230b58a16823dd32582026146a877b Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 7 Jul 2023 21:23:03 +1000 Subject: [PATCH 14/21] feat(ui): finalize base model compatibility for lora, ti, vae --- .../listeners/modelSelected.ts | 15 +++-- .../components/IAIMantineMultiSelect.tsx | 5 ++ .../common/components/IAIMantineSelect.tsx | 5 ++ .../IAIMantineSelectItemWithTooltip.tsx | 29 ++++------ .../components/ParamEmbeddingPopover.tsx | 39 +++++++------ .../lora/components/ParamLoraSelect.tsx | 31 +++++----- .../web/src/features/lora/store/loraSlice.ts | 13 +++-- .../nodes/util/graphBuilders/addVAEToGraph.ts | 2 +- .../parameters/hooks/useRecallParameters.ts | 14 ++--- .../parameters/store/generationSlice.ts | 29 ++++++---- .../parameters/store/parameterZodSchemas.ts | 58 ++++++++++++++----- .../system/components/ModelSelect.tsx | 14 ++++- .../features/system/components/VAESelect.tsx | 46 ++++++++++----- 13 files changed, 187 insertions(+), 113 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts index d10a4e25e2..934581d02a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts @@ -1,11 +1,12 @@ +import { makeToast } from 'app/components/Toaster'; +import { modelSelected } from 'features/parameters/store/actions'; import { modelChanged, vaeSelected, } from 'features/parameters/store/generationSlice'; +import { zMainModel } from 'features/parameters/store/parameterZodSchemas'; import { addToast } from 'features/system/store/systemSlice'; import { startAppListening } from '..'; -import { modelSelected } from 'features/parameters/store/actions'; -import { makeToast } from 'app/components/Toaster'; import { lorasCleared } from '../../../../../features/lora/store/loraSlice'; export const addModelSelectedListener = () => { @@ -24,12 +25,18 @@ export const addModelSelectedListener = () => { }) ) ); - dispatch(vaeSelected('auto')); + dispatch(vaeSelected(null)); dispatch(lorasCleared()); // TODO: controlnet cleared } - dispatch(modelChanged({ id: action.payload, base_model, name, type })); + const newModel = zMainModel.parse({ + id: action.payload, + base_model, + name, + }); + + dispatch(modelChanged(newModel)); }, }); }; diff --git a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx index 6cb2e4a504..04bab3717a 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineMultiSelect.tsx @@ -66,6 +66,7 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => { '&[data-disabled]': { backgroundColor: mode(base300, base700)(colorMode), color: mode(base600, base400)(colorMode), + cursor: 'not-allowed', }, }, value: { @@ -108,6 +109,10 @@ const IAIMantineMultiSelect = (props: IAIMultiSelectProps) => { color: mode('white', base50)(colorMode), }, }, + '&[data-disabled]': { + color: mode(base500, base600)(colorMode), + cursor: 'not-allowed', + }, }, rightSection: { width: 24, diff --git a/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx b/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx index 585dc106a8..8469af8fc8 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineSelect.tsx @@ -67,6 +67,7 @@ const IAIMantineSelect = (props: IAISelectProps) => { '&[data-disabled]': { backgroundColor: mode(base300, base700)(colorMode), color: mode(base600, base400)(colorMode), + cursor: 'not-allowed', }, }, value: { @@ -109,6 +110,10 @@ const IAIMantineSelect = (props: IAISelectProps) => { color: mode('white', base50)(colorMode), }, }, + '&[data-disabled]': { + color: mode(base500, base600)(colorMode), + cursor: 'not-allowed', + }, }, rightSection: { width: 32, diff --git a/invokeai/frontend/web/src/common/components/IAIMantineSelectItemWithTooltip.tsx b/invokeai/frontend/web/src/common/components/IAIMantineSelectItemWithTooltip.tsx index 3bbafc9044..65ba4020c8 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineSelectItemWithTooltip.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineSelectItemWithTooltip.tsx @@ -1,37 +1,28 @@ -import { Tooltip, Text } from '@mantine/core'; +import { Box, Tooltip } from '@chakra-ui/react'; +import { Text } from '@mantine/core'; import { forwardRef, memo } from 'react'; interface ItemProps extends React.ComponentPropsWithoutRef<'div'> { label: string; description?: string; tooltip?: string; + disabled?: boolean; } const IAIMantineSelectItemWithTooltip = forwardRef( - ({ label, tooltip, description, ...others }: ItemProps, ref) => ( -
- {tooltip ? ( - -
- {label} - {description && ( - - {description} - - )} -
-
- ) : ( -
+ ({ label, tooltip, description, disabled, ...others }: ItemProps, ref) => ( + + + {label} {description && ( {description} )} -
- )} -
+ + +
) ); diff --git a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx index 0138d7b225..b5e96b6c92 100644 --- a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx +++ b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx @@ -6,20 +6,16 @@ import { PopoverTrigger, Text, } from '@chakra-ui/react'; +import { SelectItem } from '@mantine/core'; +import { RootState } from 'app/store/store'; +import { useAppSelector } from 'app/store/storeHooks'; import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect'; +import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip'; +import { MODEL_TYPE_MAP } from 'features/system/components/ModelSelect'; import { forEach } from 'lodash-es'; import { PropsWithChildren, useCallback, useMemo, useRef } from 'react'; import { useGetTextualInversionModelsQuery } from 'services/api/endpoints/models'; import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants'; -import { RootState } from '../../../app/store/store'; -import { useAppSelector } from '../../../app/store/storeHooks'; -import IAIMantineSelectItemWithTooltip from '../../../common/components/IAIMantineSelectItemWithTooltip'; - -type EmbeddingSelectItem = { - label: string; - value: string; - description?: string; -}; type Props = PropsWithChildren & { onSelect: (v: string) => void; @@ -41,22 +37,27 @@ const ParamEmbeddingPopover = (props: Props) => { return []; } - const data: EmbeddingSelectItem[] = []; + const data: SelectItem[] = []; forEach(embeddingQueryData.entities, (embedding, _) => { - if (!embedding) return; + if (!embedding) { + return; + } + + const disabled = currentMainModel?.base_model !== embedding.base_model; data.push({ value: embedding.name, label: embedding.name, - description: embedding.description, - ...(currentMainModel?.base_model !== embedding.base_model - ? { disabled: true, tooltip: 'Incompatible base model' } - : {}), + group: MODEL_TYPE_MAP[embedding.base_model], + disabled, + tooltip: disabled + ? `Incompatible base model: ${embedding.base_model}` + : undefined, }); }); - return data; + return data.sort((a, b) => (a.disabled && !b.disabled ? 1 : -1)); }, [embeddingQueryData, currentMainModel?.base_model]); const handleChange = useCallback( @@ -114,8 +115,10 @@ const ParamEmbeddingPopover = (props: Props) => { nothingFound="No Matching Embeddings" itemComponent={IAIMantineSelectItemWithTooltip} disabled={data.length === 0} - filter={(value, selected, item: EmbeddingSelectItem) => - item.label.toLowerCase().includes(value.toLowerCase().trim()) || + filter={(value, selected, item: SelectItem) => + item.label + ?.toLowerCase() + .includes(value.toLowerCase().trim()) || item.value.toLowerCase().includes(value.toLowerCase().trim()) } onChange={handleChange} diff --git a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx index b2455ed706..a87f481496 100644 --- a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx +++ b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx @@ -1,20 +1,16 @@ import { Flex, Text } from '@chakra-ui/react'; +import { SelectItem } from '@mantine/core'; import { createSelector } from '@reduxjs/toolkit'; import { RootState, stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect'; +import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip'; +import { loraAdded } from 'features/lora/store/loraSlice'; +import { MODEL_TYPE_MAP } from 'features/system/components/ModelSelect'; import { forEach } from 'lodash-es'; import { useCallback, useMemo } from 'react'; import { useGetLoRAModelsQuery } from 'services/api/endpoints/models'; -import { loraAdded } from '../store/loraSlice'; -import IAIMantineSelectItemWithTooltip from '../../../common/components/IAIMantineSelectItemWithTooltip'; - -type LoraSelectItem = { - label: string; - value: string; - description?: string; -}; const selector = createSelector( stateSelector, @@ -38,24 +34,27 @@ const ParamLoraSelect = () => { return []; } - const data: LoraSelectItem[] = []; + const data: SelectItem[] = []; forEach(lorasQueryData.entities, (lora, id) => { if (!lora || Boolean(id in loras)) { return; } + const disabled = currentMainModel?.base_model !== lora.base_model; + data.push({ value: id, label: lora.name, - description: lora.description, - ...(currentMainModel?.base_model !== lora.base_model - ? { disabled: true, tooltip: 'Incompatible base model' } - : {}), + disabled, + group: MODEL_TYPE_MAP[lora.base_model], + tooltip: disabled + ? `Incompatible base model: ${lora.base_model}` + : undefined, }); }); - return data; + return data.sort((a, b) => (a.disabled && !b.disabled ? 1 : -1)); }, [loras, lorasQueryData, currentMainModel?.base_model]); const handleChange = useCallback( @@ -88,8 +87,8 @@ const ParamLoraSelect = () => { nothingFound="No matching LoRAs" itemComponent={IAIMantineSelectItemWithTooltip} disabled={data.length === 0} - filter={(value, selected, item: LoraSelectItem) => - item.label.toLowerCase().includes(value.toLowerCase().trim()) || + filter={(value, selected, item: SelectItem) => + item.label?.toLowerCase().includes(value.toLowerCase().trim()) || item.value.toLowerCase().includes(value.toLowerCase().trim()) } onChange={handleChange} diff --git a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts index bab6f2f7e1..6fe6109c4d 100644 --- a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts +++ b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts @@ -1,18 +1,21 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit'; +import { LoRAModelParam } from 'features/parameters/store/parameterZodSchemas'; import { LoRAModelConfigEntity } from 'services/api/endpoints/models'; +import { BaseModelType } from 'services/api/types'; export type Lora = { id: string; + base_model: BaseModelType; name: string; weight: number; }; -export const defaultLoRAConfig: Omit = { +export const defaultLoRAConfig = { weight: 0.75, }; export type LoraState = { - loras: Record; + loras: Record; }; export const intialLoraState: LoraState = { @@ -24,14 +27,14 @@ export const loraSlice = createSlice({ initialState: intialLoraState, reducers: { loraAdded: (state, action: PayloadAction) => { - const { name, id } = action.payload; - state.loras[id] = { id, name, ...defaultLoRAConfig }; + const { name, id, base_model } = action.payload; + state.loras[id] = { id, name, base_model, ...defaultLoRAConfig }; }, loraRemoved: (state, action: PayloadAction) => { const id = action.payload; delete state.loras[id]; }, - lorasCleared: (state, action: PayloadAction<>) => { + lorasCleared: (state) => { state.loras = {}; }, loraWeightChanged: ( diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts index e710a642ed..9de8f6e99d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addVAEToGraph.ts @@ -19,7 +19,7 @@ export const addVAEToGraph = ( const { vae } = state.generation; const vae_model = modelIdToVAEModelField(vae?.id || ''); - const isAutoVae = vae?.id === 'auto'; + const isAutoVae = !vae; if (!isAutoVae) { graph.nodes[VAE_LOADER] = { diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts index 71c054c40d..721b44d329 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/useRecallParameters.ts @@ -1,6 +1,10 @@ +import { useAppToaster } from 'app/components/Toaster'; import { useAppDispatch } from 'app/store/storeHooks'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; +import { isImageField } from 'services/api/guards'; +import { ImageDTO } from 'services/api/types'; +import { initialImageSelected, modelSelected } from '../store/actions'; import { setCfgScale, setHeight, @@ -12,14 +16,10 @@ import { setSteps, setWidth, } from '../store/generationSlice'; -import { isImageField } from 'services/api/guards'; -import { initialImageSelected, modelSelected } from '../store/actions'; -import { useAppToaster } from 'app/components/Toaster'; -import { ImageDTO } from 'services/api/types'; import { isValidCfgScale, isValidHeight, - isValidModel, + isValidMainModel, isValidNegativePrompt, isValidPositivePrompt, isValidScheduler, @@ -158,7 +158,7 @@ export const useRecallParameters = () => { */ const recallModel = useCallback( (model: unknown) => { - if (!isValidModel(model)) { + if (!isValidMainModel(model)) { parameterNotSetToast(); return; } @@ -295,7 +295,7 @@ export const useRecallParameters = () => { if (isValidCfgScale(cfg_scale)) { dispatch(setCfgScale(cfg_scale)); } - if (isValidModel(model)) { + if (isValidMainModel(model)) { dispatch(modelSelected(model)); } if (isValidPositivePrompt(positive_conditioning)) { diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index 83262d3aa8..f6b08e8c95 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -9,14 +9,16 @@ import { clipSkipMap } from '../components/Parameters/Advanced/ParamClipSkip'; import { CfgScaleParam, HeightParam, - ModelParam, + MainModelParam, NegativePromptParam, PositivePromptParam, SchedulerParam, SeedParam, StepsParam, StrengthParam, + VaeModelParam, WidthParam, + zMainModel, } from './parameterZodSchemas'; export interface GenerationState { @@ -48,8 +50,8 @@ export interface GenerationState { shouldUseSymmetry: boolean; horizontalSymmetrySteps: number; verticalSymmetrySteps: number; - model: ModelParam; - vae: VAEParam; + model: MainModelParam | null; + vae: VaeModelParam | null; seamlessXAxis: boolean; seamlessYAxis: boolean; clipSkip: number; @@ -84,7 +86,7 @@ export const initialGenerationState: GenerationState = { horizontalSymmetrySteps: 0, verticalSymmetrySteps: 0, model: null, - vae: '', + vae: null, seamlessXAxis: false, seamlessYAxis: false, clipSkip: 0, @@ -221,12 +223,17 @@ export const generationSlice = createSlice({ const { maxClip } = clipSkipMap[base_model as keyof typeof clipSkipMap]; state.clipSkip = clamp(state.clipSkip, 0, maxClip); - state.model = { id: action.payload, base_model, name, type }; + state.model = zMainModel.parse({ + id: action.payload, + base_model, + name, + type, + }); }, - modelChanged: (state, action: PayloadAction) => { + modelChanged: (state, action: PayloadAction) => { state.model = action.payload; }, - vaeSelected: (state, action: PayloadAction) => { + vaeSelected: (state, action: PayloadAction) => { state.vae = action.payload; }, setClipSkip: (state, action: PayloadAction) => { @@ -236,14 +243,14 @@ export const generationSlice = createSlice({ extraReducers: (builder) => { builder.addCase(configChanged, (state, action) => { const defaultModel = action.payload.sd?.defaultModel; + if (defaultModel && !state.model) { const [base_model, model_type, model_name] = defaultModel.split('/'); - state.model = { + state.model = zMainModel.parse({ id: defaultModel, name: model_name, - type: model_type, - base_model: base_model, - }; + base_model, + }); } }); builder.addCase(setShouldShowAdvancedOptions, (state, action) => { diff --git a/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts b/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts index 582b9a3428..074162e5ab 100644 --- a/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts +++ b/invokeai/frontend/web/src/features/parameters/store/parameterZodSchemas.ts @@ -126,35 +126,63 @@ export type HeightParam = z.infer; export const isValidHeight = (val: unknown): val is HeightParam => zHeight.safeParse(val).success; +const zBaseModel = z.enum(['sd-1', 'sd-2']); + +export type BaseModelParam = z.infer; + /** * Zod schema for model parameter * TODO: Make this a dynamically generated enum? */ -const zModel = z.object({ +export const zMainModel = z.object({ id: z.string(), name: z.string(), - type: z.string(), - base_model: z.string(), + base_model: zBaseModel, }); /** * Type alias for model parameter, inferred from its zod schema */ -export type ModelParam = z.infer | null; -/** - * Zod schema for VAE parameter - * TODO: Make this a dynamically generated enum? - */ -export const zVAE = z.string(); -/** - * Type alias for model parameter, inferred from its zod schema - */ -export type VAEParam = z.infer; +export type MainModelParam = z.infer; /** * Validates/type-guards a value as a model parameter */ -export const isValidModel = (val: unknown): val is ModelParam => - zModel.safeParse(val).success; +export const isValidMainModel = (val: unknown): val is MainModelParam => + zMainModel.safeParse(val).success; +/** + * Zod schema for VAE parameter + */ +export const zVaeModel = z.object({ + id: z.string(), + name: z.string(), + base_model: zBaseModel, +}); +/** + * Type alias for model parameter, inferred from its zod schema + */ +export type VaeModelParam = z.infer; +/** + * Validates/type-guards a value as a model parameter + */ +export const isValidVaeModel = (val: unknown): val is VaeModelParam => + zVaeModel.safeParse(val).success; +/** + * Zod schema for LoRA + */ +export const zLoRAModel = z.object({ + id: z.string(), + name: z.string(), + base_model: zBaseModel, +}); +/** + * Type alias for model parameter, inferred from its zod schema + */ +export type LoRAModelParam = z.infer; +/** + * Validates/type-guards a value as a model parameter + */ +export const isValidLoRAModel = (val: unknown): val is LoRAModelParam => + zLoRAModel.safeParse(val).success; /** * Zod schema for l2l strength parameter diff --git a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx index 0e49cae1df..6b5aa830d9 100644 --- a/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/ModelSelect.tsx @@ -6,9 +6,9 @@ import IAIMantineSelect from 'common/components/IAIMantineSelect'; import { SelectItem } from '@mantine/core'; import { RootState } from 'app/store/store'; +import { modelSelected } from 'features/parameters/store/actions'; import { forEach, isString } from 'lodash-es'; import { useGetMainModelsQuery } from 'services/api/endpoints/models'; -import { modelSelected } from '../../parameters/store/actions'; export const MODEL_TYPE_MAP = { 'sd-1': 'Stable Diffusion 1.x', @@ -63,6 +63,16 @@ const ModelSelect = () => { ); useEffect(() => { + if (isLoading) { + // return early here to avoid resetting model selection before we've loaded the available models + return; + } + + if (selectedModel && mainModels?.ids.includes(selectedModel?.id)) { + // the selected model is an available model, no need to change it + return; + } + const firstModel = mainModels?.ids[0]; if (!isString(firstModel)) { @@ -70,7 +80,7 @@ const ModelSelect = () => { } handleChangeModel(firstModel); - }, [handleChangeModel, mainModels?.ids]); + }, [handleChangeModel, isLoading, mainModels?.ids, selectedModel]); return isLoading ? ( { const dispatch = useAppDispatch(); @@ -34,8 +35,8 @@ const VAESelect = () => { const data: SelectItem[] = [ { - value: 'auto', - label: 'Automatic', + value: 'default', + label: 'Default', group: 'Default', }, ]; @@ -45,50 +46,65 @@ const VAESelect = () => { return; } + const disabled = currentMainModel?.base_model !== model.base_model; + data.push({ value: id, label: model.name, group: MODEL_TYPE_MAP[model.base_model], - ...(currentMainModel?.base_model !== model.base_model - ? { disabled: true, tooltip: 'Incompatible base model' } - : {}), + disabled, + tooltip: disabled + ? `Incompatible base model: ${model.base_model}` + : undefined, }); }); - return data; + return data.sort((a, b) => (a.disabled && !b.disabled ? 1 : -1)); }, [vaeModels, currentMainModel?.base_model]); const selectedVaeModel = useMemo( - () => vaeModels?.entities[selectedVae], + () => (selectedVae?.id ? vaeModels?.entities[selectedVae?.id] : null), [vaeModels?.entities, selectedVae] ); const handleChangeModel = useCallback( (v: string | null) => { - if (!v) { + if (!v || v === 'default') { + dispatch(vaeSelected(null)); return; } - dispatch(vaeSelected(v)); + + const [base_model, type, name] = v.split('/'); + + const model = zVaeModel.parse({ + id: v, + name, + base_model, + }); + + dispatch(vaeSelected(model)); }, [dispatch] ); useEffect(() => { - if (selectedVae && vaeModels?.ids.includes(selectedVae)) { + if (selectedVae && vaeModels?.ids.includes(selectedVae.id)) { return; } - handleChangeModel('auto'); - }, [handleChangeModel, vaeModels?.ids, selectedVae]); + dispatch(vaeSelected(null)); + }, [handleChangeModel, vaeModels?.ids, selectedVae, dispatch]); return ( ); }; From 2cbe98b1b1681695fb2dba477ec2c75eb3ec629d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 7 Jul 2023 22:48:25 +1000 Subject: [PATCH 15/21] fix(ui): resolve merge conflicts --- .../Parameters/Advanced/ParamClipSkip.tsx | 26 +++++++++++++------ .../parameters/store/generationSlice.ts | 8 +++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx index a4f9d37f3c..8965bc9f0c 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Advanced/ParamClipSkip.tsx @@ -2,7 +2,7 @@ import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAISlider from 'common/components/IAISlider'; import { setClipSkip } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; export const clipSkipMap = { @@ -21,9 +21,7 @@ export default function ParamClipSkip() { (state: RootState) => state.generation.clipSkip ); - const selectedModelId = useAppSelector( - (state: RootState) => state.generation.model - ).split('/')[0]; + const { model } = useAppSelector((state: RootState) => state.generation); const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -39,19 +37,31 @@ export default function ParamClipSkip() { dispatch(setClipSkip(0)); }, [dispatch]); + const max = useMemo(() => { + if (!model) { + return clipSkipMap['sd-1'].maxClip; + } + return clipSkipMap[model.base_model].maxClip; + }, [model]); + + const sliderMarks = useMemo(() => { + if (!model) { + return clipSkipMap['sd-1'].markers; + } + return clipSkipMap[model.base_model].markers; + }, [model]); + return ( ) => { const [base_model, type, name] = action.payload.split('/'); - // Clamp ClipSkip Based On Selected Model - const { maxClip } = clipSkipMap[base_model as keyof typeof clipSkipMap]; - state.clipSkip = clamp(state.clipSkip, 0, maxClip); - state.model = zMainModel.parse({ id: action.payload, base_model, name, type, }); + + // Clamp ClipSkip Based On Selected Model + const { maxClip } = clipSkipMap[state.model.base_model]; + state.clipSkip = clamp(state.clipSkip, 0, maxClip); }, modelChanged: (state, action: PayloadAction) => { state.model = action.payload; From 78bcaec4da62852ffb985c06ff7c50a4dcae9fd3 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 7 Jul 2023 23:14:31 +1000 Subject: [PATCH 16/21] feat(ui): improve embed button styles --- .../embedding/components/AddEmbeddingButton.tsx | 17 +++++++++++++---- .../Core/ParamPositiveConditioning.tsx | 17 +++++++++-------- .../ui/components/PinParametersPanelButton.tsx | 13 +++++++++++-- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/invokeai/frontend/web/src/features/embedding/components/AddEmbeddingButton.tsx b/invokeai/frontend/web/src/features/embedding/components/AddEmbeddingButton.tsx index 1dae6f56e6..94ddc08315 100644 --- a/invokeai/frontend/web/src/features/embedding/components/AddEmbeddingButton.tsx +++ b/invokeai/frontend/web/src/features/embedding/components/AddEmbeddingButton.tsx @@ -1,6 +1,6 @@ import IAIIconButton from 'common/components/IAIIconButton'; import { memo } from 'react'; -import { BiCode } from 'react-icons/bi'; +import { FaCode } from 'react-icons/fa'; type Props = { onClick: () => void; @@ -13,15 +13,24 @@ const AddEmbeddingButton = (props: Props) => { size="sm" aria-label="Add Embedding" tooltip="Add Embedding" - icon={} + icon={} sx={{ p: 2, - color: 'base.700', + color: 'base.500', _hover: { - color: 'base.550', + color: 'base.600', }, _active: { + color: 'base.700', + }, + _dark: { color: 'base.500', + _hover: { + color: 'base.400', + }, + _active: { + color: 'base.300', + }, }, }} variant="link" diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx index cbff29e89c..77f4c6af79 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx @@ -1,11 +1,10 @@ import { Box, FormControl, useDisclosure } from '@chakra-ui/react'; -import { RootState } from 'app/store/store'; +import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { ChangeEvent, KeyboardEvent, useCallback, useRef } from 'react'; import { createSelector } from '@reduxjs/toolkit'; import { - GenerationState, clampSymmetrySteps, setPositivePrompt, } from 'features/parameters/store/generationSlice'; @@ -22,10 +21,11 @@ import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; const promptInputSelector = createSelector( - [(state: RootState) => state.generation, activeTabNameSelector], - (parameters: GenerationState, activeTabName) => { + [stateSelector, activeTabNameSelector], + ({ generation, ui }, activeTabName) => { return { - prompt: parameters.positivePrompt, + shouldPinParametersPanel: ui.shouldPinParametersPanel, + prompt: generation.positivePrompt, activeTabName, }; }, @@ -41,7 +41,8 @@ const promptInputSelector = createSelector( */ const ParamPositiveConditioning = () => { const dispatch = useAppDispatch(); - const { prompt, activeTabName } = useAppSelector(promptInputSelector); + const { prompt, shouldPinParametersPanel, activeTabName } = + useAppSelector(promptInputSelector); const isReady = useIsReadyToInvoke(); const promptRef = useRef(null); const { isOpen, onClose, onOpen } = useDisclosure(); @@ -120,7 +121,7 @@ const ParamPositiveConditioning = () => { // }; return ( - + { diff --git a/invokeai/frontend/web/src/features/ui/components/PinParametersPanelButton.tsx b/invokeai/frontend/web/src/features/ui/components/PinParametersPanelButton.tsx index 30cc1d2158..5d4cc4b9d7 100644 --- a/invokeai/frontend/web/src/features/ui/components/PinParametersPanelButton.tsx +++ b/invokeai/frontend/web/src/features/ui/components/PinParametersPanelButton.tsx @@ -33,12 +33,21 @@ const PinParametersPanelButton = (props: PinParametersPanelButtonProps) => { variant="ghost" size="sm" sx={{ - color: 'base.700', + color: 'base.500', _hover: { - color: 'base.550', + color: 'base.600', }, _active: { + color: 'base.700', + }, + _dark: { color: 'base.500', + _hover: { + color: 'base.400', + }, + _active: { + color: 'base.300', + }, }, ...sx, }} From 888c47d37b8e3de56fc59e0420d0afba1be1587d Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Fri, 7 Jul 2023 11:13:42 -0400 Subject: [PATCH 17/21] add ability to disable lora, ti, dynamic prompts, vae selection --- .../frontend/web/src/app/types/invokeai.ts | 8 +++-- .../ParamDynamicPromptsCollapse.tsx | 8 +++++ .../components/ParamEmbeddingPopover.tsx | 4 ++- .../lora/components/ParamLoraCollapse.tsx | 7 +++++ .../Parameters/Core/ParamModelandVAE.tsx | 19 ------------ .../Core/ParamModelandVAEandScheduler.tsx | 31 +++++++++++++++++++ .../Core/ParamNegativeConditioning.tsx | 7 +++-- .../Core/ParamPositiveConditioning.tsx | 7 +++-- .../ImageToImageTabCoreParameters.tsx | 8 ++--- .../TextToImageTabCoreParameters.tsx | 8 ++--- .../UnifiedCanvasCoreParameters.tsx | 8 ++--- 11 files changed, 74 insertions(+), 41 deletions(-) delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAE.tsx create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler.tsx diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index a89ba01130..bea87e238e 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -93,7 +93,8 @@ export type AppFeature = | 'discordLink' | 'bugLink' | 'localization' - | 'consoleLogging'; + | 'consoleLogging' + | 'dynamicPrompting'; /** * A disable-able Stable Diffusion feature @@ -104,7 +105,10 @@ export type SDFeature = | 'variation' | 'symmetry' | 'seamless' - | 'hires'; + | 'hires' + | 'lora' + | 'tiEmbedding' + | 'vae'; /** * Configuration options for the InvokeAI UI. diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx index 0e41fad994..36d8795615 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsCollapse.tsx @@ -7,6 +7,7 @@ import IAICollapse from 'common/components/IAICollapse'; import ParamDynamicPromptsCombinatorial from './ParamDynamicPromptsCombinatorial'; import ParamDynamicPromptsToggle from './ParamDynamicPromptsEnabled'; import ParamDynamicPromptsMaxPrompts from './ParamDynamicPromptsMaxPrompts'; +import { useFeatureStatus } from '../../system/hooks/useFeatureStatus'; const selector = createSelector( stateSelector, @@ -21,6 +22,13 @@ const selector = createSelector( const ParamDynamicPromptsCollapse = () => { const { activeLabel } = useAppSelector(selector); + const isDynamicPromptingEnabled = + useFeatureStatus('dynamicPrompting').isFeatureEnabled; + + if (!isDynamicPromptingEnabled) { + return null; + } + return ( diff --git a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx index b5e96b6c92..774c67c09f 100644 --- a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx +++ b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx @@ -16,15 +16,17 @@ import { forEach } from 'lodash-es'; import { PropsWithChildren, useCallback, useMemo, useRef } from 'react'; import { useGetTextualInversionModelsQuery } from 'services/api/endpoints/models'; import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants'; +import { useFeatureStatus } from '../../system/hooks/useFeatureStatus'; type Props = PropsWithChildren & { onSelect: (v: string) => void; isOpen: boolean; onClose: () => void; + enabled?: boolean; }; const ParamEmbeddingPopover = (props: Props) => { - const { onSelect, isOpen, onClose, children } = props; + const { onSelect, isOpen, onClose, children, enabled } = props; const { data: embeddingQueryData } = useGetTextualInversionModelsQuery(); const inputRef = useRef(null); diff --git a/invokeai/frontend/web/src/features/lora/components/ParamLoraCollapse.tsx b/invokeai/frontend/web/src/features/lora/components/ParamLoraCollapse.tsx index 6e69f036df..436c32f46b 100644 --- a/invokeai/frontend/web/src/features/lora/components/ParamLoraCollapse.tsx +++ b/invokeai/frontend/web/src/features/lora/components/ParamLoraCollapse.tsx @@ -8,6 +8,7 @@ import { size } from 'lodash-es'; import { memo } from 'react'; import ParamLoraList from './ParamLoraList'; import ParamLoraSelect from './ParamLoraSelect'; +import { useFeatureStatus } from '../../system/hooks/useFeatureStatus'; const selector = createSelector( stateSelector, @@ -23,6 +24,12 @@ const selector = createSelector( const ParamLoraCollapse = () => { const { activeLabel } = useAppSelector(selector); + const isLoraEnabled = useFeatureStatus('lora').isFeatureEnabled; + + if (!isLoraEnabled) { + return null; + } + return ( diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAE.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAE.tsx deleted file mode 100644 index 1c704a86ef..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAE.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Box, Flex } from '@chakra-ui/react'; -import ModelSelect from 'features/system/components/ModelSelect'; -import VAESelect from 'features/system/components/VAESelect'; -import { memo } from 'react'; - -const ParamModelandVAE = () => { - return ( - - - - - - - - - ); -}; - -export default memo(ParamModelandVAE); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler.tsx new file mode 100644 index 0000000000..a7ae38adc7 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler.tsx @@ -0,0 +1,31 @@ +import { Box, Flex } from '@chakra-ui/react'; +import ModelSelect from 'features/system/components/ModelSelect'; +import VAESelect from 'features/system/components/VAESelect'; +import { memo } from 'react'; +import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus'; +import ParamScheduler from './ParamScheduler'; + +const ParamModelandVAEandScheduler = () => { + const isVaeEnabled = useFeatureStatus('vae').isFeatureEnabled; + + return ( + + + + + + + {isVaeEnabled && ( + + + + )} + + + + + + ); +}; + +export default memo(ParamModelandVAEandScheduler); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx index 3e5320ad47..1c849a7f2d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx @@ -8,6 +8,7 @@ import { setNegativePrompt } from 'features/parameters/store/generationSlice'; import { ChangeEvent, KeyboardEvent, useCallback, useRef } from 'react'; import { flushSync } from 'react-dom'; import { useTranslation } from 'react-i18next'; +import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus'; const ParamNegativeConditioning = () => { const negativePrompt = useAppSelector( @@ -71,6 +72,8 @@ const ParamNegativeConditioning = () => { [dispatch, onClose, negativePrompt] ); + const isTiEmbeddingEnabled = useFeatureStatus('tiEmbedding').isFeatureEnabled; + return ( { value={negativePrompt} placeholder={t('parameters.negativePromptPlaceholder')} onChange={handleChangePrompt} - onKeyDown={handleKeyDown} resize="vertical" fontSize="sm" minH={16} + {...(isTiEmbeddingEnabled && { onKeyDown: handleKeyDown })} /> - {!isOpen && ( + {!isOpen && isTiEmbeddingEnabled && ( state.generation, activeTabNameSelector], @@ -114,6 +115,8 @@ const ParamPositiveConditioning = () => { [isReady, dispatch, activeTabName, onOpen] ); + const isTiEmbeddingEnabled = useFeatureStatus('tiEmbedding').isFeatureEnabled; + // const handleSelect = (e: MouseEvent) => { // const target = e.target as HTMLTextAreaElement; // setCaret({ start: target.selectionStart, end: target.selectionEnd }); @@ -134,13 +137,13 @@ const ParamPositiveConditioning = () => { value={prompt} placeholder={t('parameters.positivePromptPlaceholder')} onChange={handleChangePrompt} - onKeyDown={handleKeyDown} resize="vertical" minH={32} + {...(isTiEmbeddingEnabled && { onKeyDown: handleKeyDown })} /> - {!isOpen && ( + {!isOpen && isTiEmbeddingEnabled && ( { > {shouldUseSliders ? ( <> - + @@ -65,8 +64,7 @@ const ImageToImageTabCoreParameters = () => { - - + diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx index 9211e095ba..b007497db2 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabCoreParameters.tsx @@ -7,8 +7,7 @@ import IAICollapse from 'common/components/IAICollapse'; import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale'; import ParamHeight from 'features/parameters/components/Parameters/Core/ParamHeight'; import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations'; -import ParamModelandVAE from 'features/parameters/components/Parameters/Core/ParamModelandVAE'; -import ParamScheduler from 'features/parameters/components/Parameters/Core/ParamScheduler'; +import ParamModelandVAEandScheduler from 'features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler'; import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps'; import ParamWidth from 'features/parameters/components/Parameters/Core/ParamWidth'; import ParamSeedFull from 'features/parameters/components/Parameters/Seed/ParamSeedFull'; @@ -44,7 +43,7 @@ const TextToImageTabCoreParameters = () => { > {shouldUseSliders ? ( <> - + @@ -61,8 +60,7 @@ const TextToImageTabCoreParameters = () => { - - + diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx index 330cd8b31e..ecce61c218 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx @@ -8,8 +8,7 @@ import ParamBoundingBoxHeight from 'features/parameters/components/Parameters/Ca import ParamBoundingBoxWidth from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxWidth'; import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale'; import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations'; -import ParamModelandVAE from 'features/parameters/components/Parameters/Core/ParamModelandVAE'; -import ParamScheduler from 'features/parameters/components/Parameters/Core/ParamScheduler'; +import ParamModelandVAEandScheduler from 'features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler'; import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps'; import ImageToImageStrength from 'features/parameters/components/Parameters/ImageToImage/ImageToImageStrength'; import ParamSeedFull from 'features/parameters/components/Parameters/Seed/ParamSeedFull'; @@ -45,7 +44,7 @@ const UnifiedCanvasCoreParameters = () => { > {shouldUseSliders ? ( <> - + @@ -62,8 +61,7 @@ const UnifiedCanvasCoreParameters = () => { - - + From 2933d811189c804bd25f94f571bf77d9f7c234fe Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Fri, 7 Jul 2023 11:16:23 -0400 Subject: [PATCH 18/21] cleanup --- .../features/embedding/components/ParamEmbeddingPopover.tsx | 4 +--- .../Parameters/Core/ParamPositiveConditioning.tsx | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx index 774c67c09f..b5e96b6c92 100644 --- a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx +++ b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx @@ -16,17 +16,15 @@ import { forEach } from 'lodash-es'; import { PropsWithChildren, useCallback, useMemo, useRef } from 'react'; import { useGetTextualInversionModelsQuery } from 'services/api/endpoints/models'; import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants'; -import { useFeatureStatus } from '../../system/hooks/useFeatureStatus'; type Props = PropsWithChildren & { onSelect: (v: string) => void; isOpen: boolean; onClose: () => void; - enabled?: boolean; }; const ParamEmbeddingPopover = (props: Props) => { - const { onSelect, isOpen, onClose, children, enabled } = props; + const { onSelect, isOpen, onClose, children } = props; const { data: embeddingQueryData } = useGetTextualInversionModelsQuery(); const inputRef = useRef(null); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx index 2480d39e1a..a41be1e19c 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx @@ -108,11 +108,11 @@ const ParamPositiveConditioning = () => { dispatch(clampSymmetrySteps()); dispatch(userInvoked(activeTabName)); } - if (e.key === '<') { + if (isTiEmbeddingEnabled && e.key === '<') { onOpen(); } }, - [isReady, dispatch, activeTabName, onOpen] + [isReady, dispatch, activeTabName, onOpen, isTiEmbeddingEnabled] ); const isTiEmbeddingEnabled = useFeatureStatus('tiEmbedding').isFeatureEnabled; @@ -137,9 +137,9 @@ const ParamPositiveConditioning = () => { value={prompt} placeholder={t('parameters.positivePromptPlaceholder')} onChange={handleChangePrompt} + onKeyDown={handleKeyDown} resize="vertical" minH={32} - {...(isTiEmbeddingEnabled && { onKeyDown: handleKeyDown })} /> From d9acb0eea627ca46cd25c83596a084d4862f183c Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Fri, 7 Jul 2023 11:44:58 -0400 Subject: [PATCH 19/21] fix bug --- .../components/Parameters/Core/ParamPositiveConditioning.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx index a41be1e19c..fd24f0ba23 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamPositiveConditioning.tsx @@ -101,6 +101,8 @@ const ParamPositiveConditioning = () => { [dispatch, onClose, prompt] ); + const isTiEmbeddingEnabled = useFeatureStatus('tiEmbedding').isFeatureEnabled; + const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (e.key === 'Enter' && e.shiftKey === false && isReady) { @@ -115,8 +117,6 @@ const ParamPositiveConditioning = () => { [isReady, dispatch, activeTabName, onOpen, isTiEmbeddingEnabled] ); - const isTiEmbeddingEnabled = useFeatureStatus('tiEmbedding').isFeatureEnabled; - // const handleSelect = (e: MouseEvent) => { // const target = e.target as HTMLTextAreaElement; // setCaret({ start: target.selectionStart, end: target.selectionEnd }); From 294336b0467854c75f839d83ebaa483f5e688639 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Fri, 7 Jul 2023 13:58:07 -0400 Subject: [PATCH 20/21] switch wording to embeddings --- invokeai/frontend/web/src/app/types/invokeai.ts | 2 +- .../Parameters/Core/ParamNegativeConditioning.tsx | 6 +++--- .../Parameters/Core/ParamPositiveConditioning.tsx | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index bea87e238e..0fd0120ce8 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -107,7 +107,7 @@ export type SDFeature = | 'seamless' | 'hires' | 'lora' - | 'tiEmbedding' + | 'embedding' | 'vae'; /** diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx index 1c849a7f2d..513ab64930 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamNegativeConditioning.tsx @@ -72,7 +72,7 @@ const ParamNegativeConditioning = () => { [dispatch, onClose, negativePrompt] ); - const isTiEmbeddingEnabled = useFeatureStatus('tiEmbedding').isFeatureEnabled; + const isEmbeddingEnabled = useFeatureStatus('embedding').isFeatureEnabled; return ( @@ -91,10 +91,10 @@ const ParamNegativeConditioning = () => { resize="vertical" fontSize="sm" minH={16} - {...(isTiEmbeddingEnabled && { onKeyDown: handleKeyDown })} + {...(isEmbeddingEnabled && { onKeyDown: handleKeyDown })} /> - {!isOpen && isTiEmbeddingEnabled && ( + {!isOpen && isEmbeddingEnabled && ( { [dispatch, onClose, prompt] ); - const isTiEmbeddingEnabled = useFeatureStatus('tiEmbedding').isFeatureEnabled; + const isEmbeddingEnabled = useFeatureStatus('embedding').isFeatureEnabled; const handleKeyDown = useCallback( (e: KeyboardEvent) => { @@ -111,11 +111,11 @@ const ParamPositiveConditioning = () => { dispatch(clampSymmetrySteps()); dispatch(userInvoked(activeTabName)); } - if (isTiEmbeddingEnabled && e.key === '<') { + if (isEmbeddingEnabled && e.key === '<') { onOpen(); } }, - [isReady, dispatch, activeTabName, onOpen, isTiEmbeddingEnabled] + [isReady, dispatch, activeTabName, onOpen, isEmbeddingEnabled] ); // const handleSelect = (e: MouseEvent) => { @@ -144,7 +144,7 @@ const ParamPositiveConditioning = () => { /> - {!isOpen && isTiEmbeddingEnabled && ( + {!isOpen && isEmbeddingEnabled && ( Date: Fri, 7 Jul 2023 14:03:37 -0400 Subject: [PATCH 21/21] prop to hide toggle for advanced settings --- .../SettingsModal/SettingsModal.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx index c8eb9cc34a..43a487ba5a 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -90,6 +90,7 @@ type ConfigOptions = { shouldShowDeveloperSettings: boolean; shouldShowResetWebUiText: boolean; shouldShowBetaLayout: boolean; + shouldShowAdvancedOptionsSettings: boolean; }; type SettingsModalProps = { @@ -106,6 +107,8 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { const shouldShowDeveloperSettings = config?.shouldShowDeveloperSettings ?? true; const shouldShowResetWebUiText = config?.shouldShowResetWebUiText ?? true; + const shouldShowAdvancedOptionsSettings = + config?.shouldShowAdvancedOptionsSettings ?? true; useEffect(() => { if (!shouldShowDeveloperSettings) { @@ -193,13 +196,15 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { dispatch(setShouldConfirmOnDelete(e.target.checked)) } /> - ) => - dispatch(setShouldShowAdvancedOptions(e.target.checked)) - } - /> + {shouldShowAdvancedOptionsSettings && ( + ) => + dispatch(setShouldShowAdvancedOptions(e.target.checked)) + } + /> + )}