diff --git a/docker/Dockerfile b/docker/Dockerfile
index 864bc5eb60..7ea078af0d 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -55,6 +55,7 @@ RUN --mount=type=cache,target=/root/.cache/pip \
FROM node:20-slim AS web-builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
+RUN corepack use pnpm@8.x
RUN corepack enable
WORKDIR /build
diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py
index f73b7a86b1..d19b028cb6 100644
--- a/invokeai/app/api/routers/model_manager.py
+++ b/invokeai/app/api/routers/model_manager.py
@@ -6,7 +6,7 @@ import pathlib
import traceback
from copy import deepcopy
from tempfile import TemporaryDirectory
-from typing import Any, Dict, List, Optional, Type
+from typing import List, Optional, Type
from fastapi import Body, Path, Query, Response, UploadFile
from fastapi.responses import FileResponse, HTMLResponse
@@ -430,13 +430,11 @@ async def delete_model_image(
async def install_model(
source: str = Query(description="Model source to install, can be a local path, repo_id, or remote URL"),
inplace: Optional[bool] = Query(description="Whether or not to install a local model in place", default=False),
- # TODO(MM2): Can we type this?
- config: Optional[Dict[str, Any]] = Body(
- description="Dict of fields that override auto-probed values in the model config record, such as name, description and prediction_type ",
- default=None,
+ access_token: Optional[str] = Query(description="access token for the remote resource", default=None),
+ config: ModelRecordChanges = Body(
+ description="Object containing fields that override auto-probed values in the model config record, such as name, description and prediction_type ",
example={"name": "string", "description": "string"},
),
- access_token: Optional[str] = None,
) -> ModelInstallJob:
"""Install a model using a string identifier.
@@ -451,8 +449,9 @@ async def install_model(
- model/name:fp16:path/to/model.safetensors
- model/name::path/to/model.safetensors
- `config` is an optional dict containing model configuration values that will override
- the ones that are probed automatically.
+ `config` is a ModelRecordChanges object. Fields in this object will override
+ the ones that are probed automatically. Pass an empty object to accept
+ all the defaults.
`access_token` is an optional access token for use with Urls that require
authentication.
@@ -737,7 +736,7 @@ async def convert_model(
# write the converted file to the convert path
raw_model = converted_model.model
assert hasattr(raw_model, "save_pretrained")
- raw_model.save_pretrained(convert_path)
+ raw_model.save_pretrained(convert_path) # type: ignore
assert convert_path.exists()
# temporarily rename the original safetensors file so that there is no naming conflict
@@ -750,12 +749,12 @@ async def convert_model(
try:
new_key = installer.install_path(
convert_path,
- config={
- "name": original_name,
- "description": model_config.description,
- "hash": model_config.hash,
- "source": model_config.source,
- },
+ config=ModelRecordChanges(
+ name=original_name,
+ description=model_config.description,
+ hash=model_config.hash,
+ source=model_config.source,
+ ),
)
except Exception as e:
logger.error(str(e))
diff --git a/invokeai/app/invocations/denoise_latents.py b/invokeai/app/invocations/denoise_latents.py
index c502234e5e..560bc9003c 100644
--- a/invokeai/app/invocations/denoise_latents.py
+++ b/invokeai/app/invocations/denoise_latents.py
@@ -39,7 +39,7 @@ from invokeai.backend.ip_adapter.ip_adapter import IPAdapter
from invokeai.backend.lora import LoRAModelRaw
from invokeai.backend.model_manager import BaseModelType, ModelVariantType
from invokeai.backend.model_patcher import ModelPatcher
-from invokeai.backend.stable_diffusion import PipelineIntermediateState, set_seamless
+from invokeai.backend.stable_diffusion import PipelineIntermediateState
from invokeai.backend.stable_diffusion.denoise_context import DenoiseContext, DenoiseInputs
from invokeai.backend.stable_diffusion.diffusers_pipeline import (
ControlNetData,
@@ -58,9 +58,14 @@ from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
from invokeai.backend.stable_diffusion.diffusion.custom_atttention import CustomAttnProcessor2_0
from invokeai.backend.stable_diffusion.diffusion_backend import StableDiffusionBackend
from invokeai.backend.stable_diffusion.extension_callback_type import ExtensionCallbackType
+from invokeai.backend.stable_diffusion.extensions.controlnet import ControlNetExt
+from invokeai.backend.stable_diffusion.extensions.freeu import FreeUExt
from invokeai.backend.stable_diffusion.extensions.inpaint import InpaintExt
from invokeai.backend.stable_diffusion.extensions.inpaint_model import InpaintModelExt
from invokeai.backend.stable_diffusion.extensions.preview import PreviewExt
+from invokeai.backend.stable_diffusion.extensions.rescale_cfg import RescaleCFGExt
+from invokeai.backend.stable_diffusion.extensions.seamless import SeamlessExt
+from invokeai.backend.stable_diffusion.extensions.t2i_adapter import T2IAdapterExt
from invokeai.backend.stable_diffusion.extensions_manager import ExtensionsManager
from invokeai.backend.stable_diffusion.schedulers import SCHEDULER_MAP
from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES
@@ -465,6 +470,65 @@ class DenoiseLatentsInvocation(BaseInvocation):
return controlnet_data
+ @staticmethod
+ def parse_controlnet_field(
+ exit_stack: ExitStack,
+ context: InvocationContext,
+ control_input: ControlField | list[ControlField] | None,
+ ext_manager: ExtensionsManager,
+ ) -> None:
+ # Normalize control_input to a list.
+ control_list: list[ControlField]
+ if isinstance(control_input, ControlField):
+ control_list = [control_input]
+ elif isinstance(control_input, list):
+ control_list = control_input
+ elif control_input is None:
+ control_list = []
+ else:
+ raise ValueError(f"Unexpected control_input type: {type(control_input)}")
+
+ for control_info in control_list:
+ model = exit_stack.enter_context(context.models.load(control_info.control_model))
+ ext_manager.add_extension(
+ ControlNetExt(
+ model=model,
+ image=context.images.get_pil(control_info.image.image_name),
+ weight=control_info.control_weight,
+ begin_step_percent=control_info.begin_step_percent,
+ end_step_percent=control_info.end_step_percent,
+ control_mode=control_info.control_mode,
+ resize_mode=control_info.resize_mode,
+ )
+ )
+
+ @staticmethod
+ def parse_t2i_adapter_field(
+ exit_stack: ExitStack,
+ context: InvocationContext,
+ t2i_adapters: Optional[Union[T2IAdapterField, list[T2IAdapterField]]],
+ ext_manager: ExtensionsManager,
+ ) -> None:
+ if t2i_adapters is None:
+ return
+
+ # Handle the possibility that t2i_adapters could be a list or a single T2IAdapterField.
+ if isinstance(t2i_adapters, T2IAdapterField):
+ t2i_adapters = [t2i_adapters]
+
+ for t2i_adapter_field in t2i_adapters:
+ ext_manager.add_extension(
+ T2IAdapterExt(
+ node_context=context,
+ model_id=t2i_adapter_field.t2i_adapter_model,
+ image=context.images.get_pil(t2i_adapter_field.image.image_name),
+ weight=t2i_adapter_field.weight,
+ begin_step_percent=t2i_adapter_field.begin_step_percent,
+ end_step_percent=t2i_adapter_field.end_step_percent,
+ resize_mode=t2i_adapter_field.resize_mode,
+ )
+ )
+
def prep_ip_adapter_image_prompts(
self,
context: InvocationContext,
@@ -773,6 +837,18 @@ class DenoiseLatentsInvocation(BaseInvocation):
ext_manager.add_extension(PreviewExt(step_callback))
+ ### cfg rescale
+ if self.cfg_rescale_multiplier > 0:
+ ext_manager.add_extension(RescaleCFGExt(self.cfg_rescale_multiplier))
+
+ ### freeu
+ if self.unet.freeu_config:
+ ext_manager.add_extension(FreeUExt(self.unet.freeu_config))
+
+ ### seamless
+ if self.unet.seamless_axes:
+ ext_manager.add_extension(SeamlessExt(self.unet.seamless_axes))
+
### inpaint
mask, masked_latents, is_gradient_mask = self.prep_inpaint_mask(context, latents)
# NOTE: We used to identify inpainting models by inpecting the shape of the loaded UNet model weights. Now we
@@ -788,7 +864,6 @@ class DenoiseLatentsInvocation(BaseInvocation):
latents = latents.to(device=device, dtype=dtype)
if noise is not None:
noise = noise.to(device=device, dtype=dtype)
-
denoise_ctx = DenoiseContext(
inputs=DenoiseInputs(
orig_latents=latents,
@@ -804,22 +879,31 @@ class DenoiseLatentsInvocation(BaseInvocation):
scheduler=scheduler,
)
- # ext: t2i/ip adapter
- ext_manager.run_callback(ExtensionCallbackType.SETUP, denoise_ctx)
+ # context for loading additional models
+ with ExitStack() as exit_stack:
+ # later should be smth like:
+ # for extension_field in self.extensions:
+ # ext = extension_field.to_extension(exit_stack, context, ext_manager)
+ # ext_manager.add_extension(ext)
+ self.parse_controlnet_field(exit_stack, context, self.control, ext_manager)
+ self.parse_t2i_adapter_field(exit_stack, context, self.t2i_adapter, ext_manager)
- unet_info = context.models.load(self.unet.unet)
- assert isinstance(unet_info.model, UNet2DConditionModel)
- with (
- unet_info.model_on_device() as (model_state_dict, unet),
- ModelPatcher.patch_unet_attention_processor(unet, denoise_ctx.inputs.attention_processor_cls),
- # ext: controlnet
- ext_manager.patch_extensions(unet),
- # ext: freeu, seamless, ip adapter, lora
- ext_manager.patch_unet(model_state_dict, unet),
- ):
- sd_backend = StableDiffusionBackend(unet, scheduler)
- denoise_ctx.unet = unet
- result_latents = sd_backend.latents_from_embeddings(denoise_ctx, ext_manager)
+ # ext: t2i/ip adapter
+ ext_manager.run_callback(ExtensionCallbackType.SETUP, denoise_ctx)
+
+ unet_info = context.models.load(self.unet.unet)
+ assert isinstance(unet_info.model, UNet2DConditionModel)
+ with (
+ unet_info.model_on_device() as (cached_weights, unet),
+ ModelPatcher.patch_unet_attention_processor(unet, denoise_ctx.inputs.attention_processor_cls),
+ # ext: controlnet
+ ext_manager.patch_extensions(denoise_ctx),
+ # ext: freeu, seamless, ip adapter, lora
+ ext_manager.patch_unet(unet, cached_weights),
+ ):
+ sd_backend = StableDiffusionBackend(unet, scheduler)
+ denoise_ctx.unet = unet
+ result_latents = sd_backend.latents_from_embeddings(denoise_ctx, ext_manager)
# https://discuss.huggingface.co/t/memory-usage-by-later-pipeline-stages/23699
result_latents = result_latents.detach().to("cpu")
@@ -882,7 +966,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
ExitStack() as exit_stack,
unet_info.model_on_device() as (model_state_dict, unet),
ModelPatcher.apply_freeu(unet, self.unet.freeu_config),
- set_seamless(unet, self.unet.seamless_axes), # FIXME
+ SeamlessExt.static_patch_model(unet, self.unet.seamless_axes), # FIXME
# Apply the LoRA after unet has been moved to its target device for faster patching.
ModelPatcher.apply_lora_unet(
unet,
diff --git a/invokeai/app/invocations/latents_to_image.py b/invokeai/app/invocations/latents_to_image.py
index cc8a9c44a3..35b8483f2c 100644
--- a/invokeai/app/invocations/latents_to_image.py
+++ b/invokeai/app/invocations/latents_to_image.py
@@ -24,7 +24,7 @@ from invokeai.app.invocations.fields import (
from invokeai.app.invocations.model import VAEField
from invokeai.app.invocations.primitives import ImageOutput
from invokeai.app.services.shared.invocation_context import InvocationContext
-from invokeai.backend.stable_diffusion import set_seamless
+from invokeai.backend.stable_diffusion.extensions.seamless import SeamlessExt
from invokeai.backend.stable_diffusion.vae_tiling import patch_vae_tiling_params
from invokeai.backend.util.devices import TorchDevice
@@ -59,7 +59,7 @@ class LatentsToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
vae_info = context.models.load(self.vae.vae)
assert isinstance(vae_info.model, (AutoencoderKL, AutoencoderTiny))
- with set_seamless(vae_info.model, self.vae.seamless_axes), vae_info as vae:
+ with SeamlessExt.static_patch_model(vae_info.model, self.vae.seamless_axes), vae_info as vae:
assert isinstance(vae, (AutoencoderKL, AutoencoderTiny))
latents = latents.to(vae.device)
if self.fp32:
diff --git a/invokeai/app/invocations/spandrel_image_to_image.py b/invokeai/app/invocations/spandrel_image_to_image.py
index bbe31af644..ae4f48ef77 100644
--- a/invokeai/app/invocations/spandrel_image_to_image.py
+++ b/invokeai/app/invocations/spandrel_image_to_image.py
@@ -1,3 +1,5 @@
+from typing import Callable
+
import numpy as np
import torch
from PIL import Image
@@ -21,7 +23,7 @@ from invokeai.backend.tiles.tiles import calc_tiles_min_overlap
from invokeai.backend.tiles.utils import TBLR, Tile
-@invocation("spandrel_image_to_image", title="Image-to-Image", tags=["upscale"], category="upscale", version="1.1.0")
+@invocation("spandrel_image_to_image", title="Image-to-Image", tags=["upscale"], category="upscale", version="1.3.0")
class SpandrelImageToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
"""Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel)."""
@@ -35,7 +37,8 @@ class SpandrelImageToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
default=512, description="The tile size for tiled image-to-image. Set to 0 to disable tiling."
)
- def _scale_tile(self, tile: Tile, scale: int) -> Tile:
+ @classmethod
+ def scale_tile(cls, tile: Tile, scale: int) -> Tile:
return Tile(
coords=TBLR(
top=tile.coords.top * scale,
@@ -51,20 +54,22 @@ class SpandrelImageToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
),
)
- @torch.inference_mode()
- def invoke(self, context: InvocationContext) -> ImageOutput:
- # Images are converted to RGB, because most models don't support an alpha channel. In the future, we may want to
- # revisit this.
- image = context.images.get_pil(self.image.image_name, mode="RGB")
-
+ @classmethod
+ def upscale_image(
+ cls,
+ image: Image.Image,
+ tile_size: int,
+ spandrel_model: SpandrelImageToImageModel,
+ is_canceled: Callable[[], bool],
+ ) -> Image.Image:
# Compute the image tiles.
- if self.tile_size > 0:
+ if tile_size > 0:
min_overlap = 20
tiles = calc_tiles_min_overlap(
image_height=image.height,
image_width=image.width,
- tile_height=self.tile_size,
- tile_width=self.tile_size,
+ tile_height=tile_size,
+ tile_width=tile_size,
min_overlap=min_overlap,
)
else:
@@ -85,60 +90,164 @@ class SpandrelImageToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
# Prepare input image for inference.
image_tensor = SpandrelImageToImageModel.pil_to_tensor(image)
- # Load the model.
- spandrel_model_info = context.models.load(self.image_to_image_model)
+ # Scale the tiles for re-assembling the final image.
+ scale = spandrel_model.scale
+ scaled_tiles = [cls.scale_tile(tile, scale=scale) for tile in tiles]
+
+ # Prepare the output tensor.
+ _, channels, height, width = image_tensor.shape
+ output_tensor = torch.zeros(
+ (height * scale, width * scale, channels), dtype=torch.uint8, device=torch.device("cpu")
+ )
+
+ image_tensor = image_tensor.to(device=spandrel_model.device, dtype=spandrel_model.dtype)
# Run the model on each tile.
- with spandrel_model_info as spandrel_model:
- assert isinstance(spandrel_model, SpandrelImageToImageModel)
+ for tile, scaled_tile in tqdm(list(zip(tiles, scaled_tiles, strict=True)), desc="Upscaling Tiles"):
+ # Exit early if the invocation has been canceled.
+ if is_canceled():
+ raise CanceledException
- # Scale the tiles for re-assembling the final image.
- scale = spandrel_model.scale
- scaled_tiles = [self._scale_tile(tile, scale=scale) for tile in tiles]
+ # Extract the current tile from the input tensor.
+ input_tile = image_tensor[
+ :, :, tile.coords.top : tile.coords.bottom, tile.coords.left : tile.coords.right
+ ].to(device=spandrel_model.device, dtype=spandrel_model.dtype)
- # Prepare the output tensor.
- _, channels, height, width = image_tensor.shape
- output_tensor = torch.zeros(
- (height * scale, width * scale, channels), dtype=torch.uint8, device=torch.device("cpu")
- )
+ # Run the model on the tile.
+ output_tile = spandrel_model.run(input_tile)
- image_tensor = image_tensor.to(device=spandrel_model.device, dtype=spandrel_model.dtype)
+ # Convert the output tile into the output tensor's format.
+ # (N, C, H, W) -> (C, H, W)
+ output_tile = output_tile.squeeze(0)
+ # (C, H, W) -> (H, W, C)
+ output_tile = output_tile.permute(1, 2, 0)
+ output_tile = output_tile.clamp(0, 1)
+ output_tile = (output_tile * 255).to(dtype=torch.uint8, device=torch.device("cpu"))
- for tile, scaled_tile in tqdm(list(zip(tiles, scaled_tiles, strict=True)), desc="Upscaling Tiles"):
- # Exit early if the invocation has been canceled.
- if context.util.is_canceled():
- raise CanceledException
-
- # Extract the current tile from the input tensor.
- input_tile = image_tensor[
- :, :, tile.coords.top : tile.coords.bottom, tile.coords.left : tile.coords.right
- ].to(device=spandrel_model.device, dtype=spandrel_model.dtype)
-
- # Run the model on the tile.
- output_tile = spandrel_model.run(input_tile)
-
- # Convert the output tile into the output tensor's format.
- # (N, C, H, W) -> (C, H, W)
- output_tile = output_tile.squeeze(0)
- # (C, H, W) -> (H, W, C)
- output_tile = output_tile.permute(1, 2, 0)
- output_tile = output_tile.clamp(0, 1)
- output_tile = (output_tile * 255).to(dtype=torch.uint8, device=torch.device("cpu"))
-
- # Merge the output tile into the output tensor.
- # We only keep half of the overlap on the top and left side of the tile. We do this in case there are
- # edge artifacts. We don't bother with any 'blending' in the current implementation - for most upscalers
- # it seems unnecessary, but we may find a need in the future.
- top_overlap = scaled_tile.overlap.top // 2
- left_overlap = scaled_tile.overlap.left // 2
- output_tensor[
- scaled_tile.coords.top + top_overlap : scaled_tile.coords.bottom,
- scaled_tile.coords.left + left_overlap : scaled_tile.coords.right,
- :,
- ] = output_tile[top_overlap:, left_overlap:, :]
+ # Merge the output tile into the output tensor.
+ # We only keep half of the overlap on the top and left side of the tile. We do this in case there are
+ # edge artifacts. We don't bother with any 'blending' in the current implementation - for most upscalers
+ # it seems unnecessary, but we may find a need in the future.
+ top_overlap = scaled_tile.overlap.top // 2
+ left_overlap = scaled_tile.overlap.left // 2
+ output_tensor[
+ scaled_tile.coords.top + top_overlap : scaled_tile.coords.bottom,
+ scaled_tile.coords.left + left_overlap : scaled_tile.coords.right,
+ :,
+ ] = output_tile[top_overlap:, left_overlap:, :]
# Convert the output tensor to a PIL image.
np_image = output_tensor.detach().numpy().astype(np.uint8)
pil_image = Image.fromarray(np_image)
+
+ return pil_image
+
+ @torch.inference_mode()
+ def invoke(self, context: InvocationContext) -> ImageOutput:
+ # Images are converted to RGB, because most models don't support an alpha channel. In the future, we may want to
+ # revisit this.
+ image = context.images.get_pil(self.image.image_name, mode="RGB")
+
+ # Load the model.
+ spandrel_model_info = context.models.load(self.image_to_image_model)
+
+ # Do the upscaling.
+ with spandrel_model_info as spandrel_model:
+ assert isinstance(spandrel_model, SpandrelImageToImageModel)
+
+ # Upscale the image
+ pil_image = self.upscale_image(image, self.tile_size, spandrel_model, context.util.is_canceled)
+
+ image_dto = context.images.save(image=pil_image)
+ return ImageOutput.build(image_dto)
+
+
+@invocation(
+ "spandrel_image_to_image_autoscale",
+ title="Image-to-Image (Autoscale)",
+ tags=["upscale"],
+ category="upscale",
+ version="1.0.0",
+)
+class SpandrelImageToImageAutoscaleInvocation(SpandrelImageToImageInvocation):
+ """Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel) until the target scale is reached."""
+
+ scale: float = InputField(
+ default=4.0,
+ gt=0.0,
+ le=16.0,
+ description="The final scale of the output image. If the model does not upscale the image, this will be ignored.",
+ )
+ fit_to_multiple_of_8: bool = InputField(
+ default=False,
+ description="If true, the output image will be resized to the nearest multiple of 8 in both dimensions.",
+ )
+
+ @torch.inference_mode()
+ def invoke(self, context: InvocationContext) -> ImageOutput:
+ # Images are converted to RGB, because most models don't support an alpha channel. In the future, we may want to
+ # revisit this.
+ image = context.images.get_pil(self.image.image_name, mode="RGB")
+
+ # Load the model.
+ spandrel_model_info = context.models.load(self.image_to_image_model)
+
+ # The target size of the image, determined by the provided scale. We'll run the upscaler until we hit this size.
+ # Later, we may mutate this value if the model doesn't upscale the image or if the user requested a multiple of 8.
+ target_width = int(image.width * self.scale)
+ target_height = int(image.height * self.scale)
+
+ # Do the upscaling.
+ with spandrel_model_info as spandrel_model:
+ assert isinstance(spandrel_model, SpandrelImageToImageModel)
+
+ # First pass of upscaling. Note: `pil_image` will be mutated.
+ pil_image = self.upscale_image(image, self.tile_size, spandrel_model, context.util.is_canceled)
+
+ # Some models don't upscale the image, but we have no way to know this in advance. We'll check if the model
+ # upscaled the image and run the loop below if it did. We'll require the model to upscale both dimensions
+ # to be considered an upscale model.
+ is_upscale_model = pil_image.width > image.width and pil_image.height > image.height
+
+ if is_upscale_model:
+ # This is an upscale model, so we should keep upscaling until we reach the target size.
+ iterations = 1
+ while pil_image.width < target_width or pil_image.height < target_height:
+ pil_image = self.upscale_image(pil_image, self.tile_size, spandrel_model, context.util.is_canceled)
+ iterations += 1
+
+ # Sanity check to prevent excessive or infinite loops. All known upscaling models are at least 2x.
+ # Our max scale is 16x, so with a 2x model, we should never exceed 16x == 2^4 -> 4 iterations.
+ # We'll allow one extra iteration "just in case" and bail at 5 upscaling iterations. In practice,
+ # we should never reach this limit.
+ if iterations >= 5:
+ context.logger.warning(
+ "Upscale loop reached maximum iteration count of 5, stopping upscaling early."
+ )
+ break
+ else:
+ # This model doesn't upscale the image. We should ignore the scale parameter, modifying the output size
+ # to be the same as the processed image size.
+
+ # The output size is now the size of the processed image.
+ target_width = pil_image.width
+ target_height = pil_image.height
+
+ # Warn the user if they requested a scale greater than 1.
+ if self.scale > 1:
+ context.logger.warning(
+ "Model does not increase the size of the image, but a greater scale than 1 was requested. Image will not be scaled."
+ )
+
+ # We may need to resize the image to a multiple of 8. Use floor division to ensure we don't scale the image up
+ # in the final resize
+ if self.fit_to_multiple_of_8:
+ target_width = int(target_width // 8 * 8)
+ target_height = int(target_height // 8 * 8)
+
+ # Final resize. Per PIL documentation, Lanczos provides the best quality for both upscale and downscale.
+ # See: https://pillow.readthedocs.io/en/stable/handbook/concepts.html#filters-comparison-table
+ pil_image = pil_image.resize((target_width, target_height), resample=Image.Resampling.LANCZOS)
+
image_dto = context.images.save(image=pil_image)
return ImageOutput.build(image_dto)
diff --git a/invokeai/app/services/model_install/model_install_base.py b/invokeai/app/services/model_install/model_install_base.py
index 20afaeaa50..27578dd5d6 100644
--- a/invokeai/app/services/model_install/model_install_base.py
+++ b/invokeai/app/services/model_install/model_install_base.py
@@ -3,7 +3,7 @@
from abc import ABC, abstractmethod
from pathlib import Path
-from typing import Any, Dict, List, Optional, Union
+from typing import List, Optional, Union
from pydantic.networks import AnyHttpUrl
@@ -12,7 +12,7 @@ from invokeai.app.services.download import DownloadQueueServiceBase
from invokeai.app.services.events.events_base import EventServiceBase
from invokeai.app.services.invoker import Invoker
from invokeai.app.services.model_install.model_install_common import ModelInstallJob, ModelSource
-from invokeai.app.services.model_records import ModelRecordServiceBase
+from invokeai.app.services.model_records import ModelRecordChanges, ModelRecordServiceBase
from invokeai.backend.model_manager import AnyModelConfig
@@ -64,7 +64,7 @@ class ModelInstallServiceBase(ABC):
def register_path(
self,
model_path: Union[Path, str],
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
) -> str:
"""
Probe and register the model at model_path.
@@ -72,7 +72,7 @@ class ModelInstallServiceBase(ABC):
This keeps the model in its current location.
:param model_path: Filesystem Path to the model.
- :param config: Dict of attributes that will override autoassigned values.
+ :param config: ModelRecordChanges object that will override autoassigned model record values.
:returns id: The string ID of the registered model.
"""
@@ -92,7 +92,7 @@ class ModelInstallServiceBase(ABC):
def install_path(
self,
model_path: Union[Path, str],
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
) -> str:
"""
Probe, register and install the model in the models directory.
@@ -101,7 +101,7 @@ class ModelInstallServiceBase(ABC):
the models directory handled by InvokeAI.
:param model_path: Filesystem Path to the model.
- :param config: Dict of attributes that will override autoassigned values.
+ :param config: ModelRecordChanges object that will override autoassigned model record values.
:returns id: The string ID of the registered model.
"""
@@ -109,14 +109,14 @@ class ModelInstallServiceBase(ABC):
def heuristic_import(
self,
source: str,
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
access_token: Optional[str] = None,
inplace: Optional[bool] = False,
) -> ModelInstallJob:
r"""Install the indicated model using heuristics to interpret user intentions.
:param source: String source
- :param config: Optional dict. Any fields in this dict
+ :param config: Optional ModelRecordChanges object. Any fields in this object
will override corresponding autoassigned probe fields in the
model's config record as described in `import_model()`.
:param access_token: Optional access token for remote sources.
@@ -147,7 +147,7 @@ class ModelInstallServiceBase(ABC):
def import_model(
self,
source: ModelSource,
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
) -> ModelInstallJob:
"""Install the indicated model.
diff --git a/invokeai/app/services/model_install/model_install_common.py b/invokeai/app/services/model_install/model_install_common.py
index c1538f543d..8cbe31597f 100644
--- a/invokeai/app/services/model_install/model_install_common.py
+++ b/invokeai/app/services/model_install/model_install_common.py
@@ -2,13 +2,14 @@ import re
import traceback
from enum import Enum
from pathlib import Path
-from typing import Any, Dict, Literal, Optional, Set, Union
+from typing import Literal, Optional, Set, Union
from pydantic import BaseModel, Field, PrivateAttr, field_validator
from pydantic.networks import AnyHttpUrl
from typing_extensions import Annotated
from invokeai.app.services.download import DownloadJob, MultiFileDownloadJob
+from invokeai.app.services.model_records import ModelRecordChanges
from invokeai.backend.model_manager import AnyModelConfig, ModelRepoVariant
from invokeai.backend.model_manager.config import ModelSourceType
from invokeai.backend.model_manager.metadata import AnyModelRepoMetadata
@@ -133,8 +134,9 @@ class ModelInstallJob(BaseModel):
id: int = Field(description="Unique ID for this job")
status: InstallStatus = Field(default=InstallStatus.WAITING, description="Current status of install process")
error_reason: Optional[str] = Field(default=None, description="Information about why the job failed")
- config_in: Dict[str, Any] = Field(
- default_factory=dict, description="Configuration information (e.g. 'description') to apply to model."
+ config_in: ModelRecordChanges = Field(
+ default_factory=ModelRecordChanges,
+ description="Configuration information (e.g. 'description') to apply to model.",
)
config_out: Optional[AnyModelConfig] = Field(
default=None, description="After successful installation, this will hold the configuration object."
diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py
index 5e19a349ad..e1d784f5bf 100644
--- a/invokeai/app/services/model_install/model_install_default.py
+++ b/invokeai/app/services/model_install/model_install_default.py
@@ -163,26 +163,27 @@ class ModelInstallService(ModelInstallServiceBase):
def register_path(
self,
model_path: Union[Path, str],
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
) -> str: # noqa D102
model_path = Path(model_path)
- config = config or {}
- if not config.get("source"):
- config["source"] = model_path.resolve().as_posix()
- config["source_type"] = ModelSourceType.Path
+ config = config or ModelRecordChanges()
+ if not config.source:
+ config.source = model_path.resolve().as_posix()
+ config.source_type = ModelSourceType.Path
return self._register(model_path, config)
def install_path(
self,
model_path: Union[Path, str],
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
) -> str: # noqa D102
model_path = Path(model_path)
- config = config or {}
+ config = config or ModelRecordChanges()
+ info: AnyModelConfig = ModelProbe.probe(
+ Path(model_path), config.model_dump(), hash_algo=self._app_config.hashing_algorithm
+ ) # type: ignore
- info: AnyModelConfig = ModelProbe.probe(Path(model_path), config, hash_algo=self._app_config.hashing_algorithm)
-
- if preferred_name := config.get("name"):
+ if preferred_name := config.name:
preferred_name = Path(preferred_name).with_suffix(model_path.suffix)
dest_path = (
@@ -204,7 +205,7 @@ class ModelInstallService(ModelInstallServiceBase):
def heuristic_import(
self,
source: str,
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
access_token: Optional[str] = None,
inplace: Optional[bool] = False,
) -> ModelInstallJob:
@@ -216,7 +217,7 @@ class ModelInstallService(ModelInstallServiceBase):
source_obj.access_token = access_token
return self.import_model(source_obj, config)
- def import_model(self, source: ModelSource, config: Optional[Dict[str, Any]] = None) -> ModelInstallJob: # noqa D102
+ def import_model(self, source: ModelSource, config: Optional[ModelRecordChanges] = None) -> ModelInstallJob: # noqa D102
similar_jobs = [x for x in self.list_jobs() if x.source == source and not x.in_terminal_state]
if similar_jobs:
self._logger.warning(f"There is already an active install job for {source}. Not enqueuing.")
@@ -318,16 +319,17 @@ class ModelInstallService(ModelInstallServiceBase):
model_path = self._app_config.models_path / model_path
model_path = model_path.resolve()
- config: dict[str, Any] = {}
- config["name"] = model_name
- config["description"] = stanza.get("description")
+ config = ModelRecordChanges(
+ name=model_name,
+ description=stanza.get("description"),
+ )
legacy_config_path = stanza.get("config")
if legacy_config_path:
# In v3, these paths were relative to the root. Migrate them to be relative to the legacy_conf_dir.
legacy_config_path = self._app_config.root_path / legacy_config_path
if legacy_config_path.is_relative_to(self._app_config.legacy_conf_path):
legacy_config_path = legacy_config_path.relative_to(self._app_config.legacy_conf_path)
- config["config_path"] = str(legacy_config_path)
+ config.config_path = str(legacy_config_path)
try:
id = self.register_path(model_path=model_path, config=config)
self._logger.info(f"Migrated {model_name} with id {id}")
@@ -500,11 +502,11 @@ class ModelInstallService(ModelInstallServiceBase):
job.total_bytes = self._stat_size(job.local_path)
job.bytes = job.total_bytes
self._signal_job_running(job)
- job.config_in["source"] = str(job.source)
- job.config_in["source_type"] = MODEL_SOURCE_TO_TYPE_MAP[job.source.__class__]
+ job.config_in.source = str(job.source)
+ job.config_in.source_type = MODEL_SOURCE_TO_TYPE_MAP[job.source.__class__]
# enter the metadata, if there is any
if isinstance(job.source_metadata, (HuggingFaceMetadata)):
- job.config_in["source_api_response"] = job.source_metadata.api_response
+ job.config_in.source_api_response = job.source_metadata.api_response
if job.inplace:
key = self.register_path(job.local_path, job.config_in)
@@ -639,11 +641,11 @@ class ModelInstallService(ModelInstallServiceBase):
return new_path
def _register(
- self, model_path: Path, config: Optional[Dict[str, Any]] = None, info: Optional[AnyModelConfig] = None
+ self, model_path: Path, config: Optional[ModelRecordChanges] = None, info: Optional[AnyModelConfig] = None
) -> str:
- config = config or {}
+ config = config or ModelRecordChanges()
- info = info or ModelProbe.probe(model_path, config, hash_algo=self._app_config.hashing_algorithm)
+ info = info or ModelProbe.probe(model_path, config.model_dump(), hash_algo=self._app_config.hashing_algorithm) # type: ignore
model_path = model_path.resolve()
@@ -674,11 +676,13 @@ class ModelInstallService(ModelInstallServiceBase):
precision = TorchDevice.choose_torch_dtype()
return ModelRepoVariant.FP16 if precision == torch.float16 else None
- def _import_local_model(self, source: LocalModelSource, config: Optional[Dict[str, Any]]) -> ModelInstallJob:
+ def _import_local_model(
+ self, source: LocalModelSource, config: Optional[ModelRecordChanges] = None
+ ) -> ModelInstallJob:
return ModelInstallJob(
id=self._next_id(),
source=source,
- config_in=config or {},
+ config_in=config or ModelRecordChanges(),
local_path=Path(source.path),
inplace=source.inplace or False,
)
@@ -686,7 +690,7 @@ class ModelInstallService(ModelInstallServiceBase):
def _import_from_hf(
self,
source: HFModelSource,
- config: Optional[Dict[str, Any]] = None,
+ config: Optional[ModelRecordChanges] = None,
) -> ModelInstallJob:
# Add user's cached access token to HuggingFace requests
if source.access_token is None:
@@ -702,7 +706,7 @@ class ModelInstallService(ModelInstallServiceBase):
def _import_from_url(
self,
source: URLModelSource,
- config: Optional[Dict[str, Any]],
+ config: Optional[ModelRecordChanges] = None,
) -> ModelInstallJob:
remote_files, metadata = self._remote_files_from_source(source)
return self._import_remote_model(
@@ -717,7 +721,7 @@ class ModelInstallService(ModelInstallServiceBase):
source: HFModelSource | URLModelSource,
remote_files: List[RemoteModelFile],
metadata: Optional[AnyModelRepoMetadata],
- config: Optional[Dict[str, Any]],
+ config: Optional[ModelRecordChanges],
) -> ModelInstallJob:
if len(remote_files) == 0:
raise ValueError(f"{source}: No downloadable files found")
@@ -730,7 +734,7 @@ class ModelInstallService(ModelInstallServiceBase):
install_job = ModelInstallJob(
id=self._next_id(),
source=source,
- config_in=config or {},
+ config_in=config or ModelRecordChanges(),
source_metadata=metadata,
local_path=destdir, # local path may change once the download has started due to content-disposition handling
bytes=0,
diff --git a/invokeai/app/services/model_records/model_records_base.py b/invokeai/app/services/model_records/model_records_base.py
index 57531cf3c1..46d11d4ddf 100644
--- a/invokeai/app/services/model_records/model_records_base.py
+++ b/invokeai/app/services/model_records/model_records_base.py
@@ -18,6 +18,7 @@ from invokeai.backend.model_manager.config import (
ControlAdapterDefaultSettings,
MainModelDefaultSettings,
ModelFormat,
+ ModelSourceType,
ModelType,
ModelVariantType,
SchedulerPredictionType,
@@ -66,10 +67,16 @@ class ModelRecordChanges(BaseModelExcludeNull):
"""A set of changes to apply to a model."""
# Changes applicable to all models
+ source: Optional[str] = Field(description="original source of the model", default=None)
+ source_type: Optional[ModelSourceType] = Field(description="type of model source", default=None)
+ source_api_response: Optional[str] = Field(description="metadata from remote source", default=None)
name: Optional[str] = Field(description="Name of the model.", default=None)
path: Optional[str] = Field(description="Path to the model.", default=None)
description: Optional[str] = Field(description="Model description", default=None)
base: Optional[BaseModelType] = Field(description="The base model.", default=None)
+ type: Optional[ModelType] = Field(description="Type of model", default=None)
+ key: Optional[str] = Field(description="Database ID for this model", default=None)
+ hash: Optional[str] = Field(description="hash of model file", default=None)
trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None)
default_settings: Optional[MainModelDefaultSettings | ControlAdapterDefaultSettings] = Field(
description="Default settings for this model", default=None
diff --git a/invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SD1.5.json b/invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SD1.5.json
new file mode 100644
index 0000000000..7bc2810c75
--- /dev/null
+++ b/invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SD1.5.json
@@ -0,0 +1,1430 @@
+{
+ "name": "MultiDiffusion SD1.5",
+ "author": "Invoke",
+ "description": "A workflow to upscale an input image with tiled upscaling, using SD1.5 based models.",
+ "version": "1.0.0",
+ "contact": "invoke@invoke.ai",
+ "tags": "tiled, upscaling, sdxl",
+ "notes": "",
+ "exposedFields": [
+ {
+ "nodeId": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "fieldName": "image"
+ },
+ {
+ "nodeId": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "fieldName": "scale"
+ },
+ {
+ "nodeId": "c3b60a50-8039-4924-90e3-8c608e1fecb5",
+ "fieldName": "board"
+ },
+ {
+ "nodeId": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
+ "fieldName": "a"
+ },
+ {
+ "nodeId": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
+ "fieldName": "a"
+ },
+ {
+ "nodeId": "14469dfe-9f49-4a13-89a7-eb4d45794b2b",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "33fe76a0-5efd-4482-a7f0-e2abf1223dc2",
+ "fieldName": "prompt"
+ },
+ {
+ "nodeId": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "fieldName": "model"
+ },
+ {
+ "nodeId": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "fieldName": "image_to_image_model"
+ },
+ {
+ "nodeId": "f936ebb3-6902-4df9-a775-6a68bac2da70",
+ "fieldName": "model"
+ }
+ ],
+ "meta": {
+ "version": "3.0.0",
+ "category": "default"
+ },
+ "id": "e5b5fb01-8906-463a-963a-402dbc42f79b",
+ "nodes": [
+ {
+ "id": "33fe76a0-5efd-4482-a7f0-e2abf1223dc2",
+ "type": "invocation",
+ "data": {
+ "id": "33fe76a0-5efd-4482-a7f0-e2abf1223dc2",
+ "type": "compel",
+ "version": "1.2.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "Negative Prompt (Optional)",
+ "value": "blurry painting, art, sketch"
+ },
+ "clip": {
+ "name": "clip",
+ "label": ""
+ },
+ "mask": {
+ "name": "mask",
+ "label": ""
+ }
+ }
+ },
+ "position": {
+ "x": -3550,
+ "y": -2725
+ }
+ },
+ {
+ "id": "14469dfe-9f49-4a13-89a7-eb4d45794b2b",
+ "type": "invocation",
+ "data": {
+ "id": "14469dfe-9f49-4a13-89a7-eb4d45794b2b",
+ "type": "compel",
+ "version": "1.2.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "prompt": {
+ "name": "prompt",
+ "label": "Positive Prompt (Optional)",
+ "value": "high quality studio lighting, photo"
+ },
+ "clip": {
+ "name": "clip",
+ "label": ""
+ },
+ "mask": {
+ "name": "mask",
+ "label": ""
+ }
+ }
+ },
+ "position": {
+ "x": -3550,
+ "y": -3025
+ }
+ },
+ {
+ "id": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "type": "invocation",
+ "data": {
+ "id": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "type": "main_model_loader",
+ "version": "1.0.3",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "",
+ "value": {
+ "key": "e7b402e5-62e5-4acb-8c39-bee6bdb758ab",
+ "hash": "c8659e796168d076368256b57edbc1b48d6dafc1712f1bb37cc57c7c06889a6b",
+ "name": "526mix",
+ "base": "sd-1",
+ "type": "main"
+ }
+ }
+ }
+ },
+ "position": {
+ "x": -4025,
+ "y": -3050
+ }
+ },
+ {
+ "id": "71a116e1-c631-48b3-923d-acea4753b887",
+ "type": "invocation",
+ "data": {
+ "id": "71a116e1-c631-48b3-923d-acea4753b887",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.3
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1550
+ }
+ },
+ {
+ "id": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "type": "invocation",
+ "data": {
+ "id": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.025
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1575
+ }
+ },
+ {
+ "id": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "type": "invocation",
+ "data": {
+ "id": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.45
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1200
+ }
+ },
+ {
+ "id": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "type": "invocation",
+ "data": {
+ "id": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.15
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1225
+ }
+ },
+ {
+ "id": "1ed88043-3519-41d5-a895-07944f03de70",
+ "type": "invocation",
+ "data": {
+ "id": "1ed88043-3519-41d5-a895-07944f03de70",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.3
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1650
+ }
+ },
+ {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "type": "invocation",
+ "data": {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.032
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1850
+ }
+ },
+ {
+ "id": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "type": "invocation",
+ "data": {
+ "id": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "type": "spandrel_image_to_image_autoscale",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": ""
+ },
+ "image": {
+ "name": "image",
+ "label": "Image to Upscale",
+ "value": {
+ "image_name": "ee7009f7-a35d-488b-a2a6-21237ef5ae05.png"
+ }
+ },
+ "image_to_image_model": {
+ "name": "image_to_image_model",
+ "label": "",
+ "value": {
+ "key": "38bb1a29-8ede-42ba-b77f-64b3478896eb",
+ "hash": "blake3:e52fdbee46a484ebe9b3b20ea0aac0a35a453ab6d0d353da00acfd35ce7a91ed",
+ "name": "4xNomosWebPhoto_esrgan",
+ "base": "sdxl",
+ "type": "spandrel_image_to_image"
+ }
+ },
+ "tile_size": {
+ "name": "tile_size",
+ "label": "",
+ "value": 512
+ },
+ "scale": {
+ "name": "scale",
+ "label": "Scale (2x, 4x, 8x, 16x)",
+ "value": 2
+ },
+ "fit_to_multiple_of_8": {
+ "name": "fit_to_multiple_of_8",
+ "label": "",
+ "value": true
+ }
+ }
+ },
+ "position": {
+ "x": -4750,
+ "y": -2125
+ }
+ },
+ {
+ "id": "f936ebb3-6902-4df9-a775-6a68bac2da70",
+ "type": "invocation",
+ "data": {
+ "id": "f936ebb3-6902-4df9-a775-6a68bac2da70",
+ "type": "model_identifier",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "model": {
+ "name": "model",
+ "label": "ControlNet Model - Choose a Tile ControlNet",
+ "value": {
+ "key": "20645e4d-ef97-4c5a-9243-b834a3483925",
+ "hash": "f0812e13758f91baf4e54b7dbb707b70642937d3b2098cd2b94cc36d3eba308e",
+ "name": "tile",
+ "base": "sd-1",
+ "type": "controlnet"
+ }
+ }
+ }
+ },
+ "position": {
+ "x": -3450,
+ "y": -1450
+ }
+ },
+ {
+ "id": "00239057-20d4-4cd2-a010-28727b256ea2",
+ "type": "invocation",
+ "data": {
+ "id": "00239057-20d4-4cd2-a010-28727b256ea2",
+ "type": "rand_int",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": false,
+ "inputs": {
+ "low": {
+ "name": "low",
+ "label": "",
+ "value": 0
+ },
+ "high": {
+ "name": "high",
+ "label": "",
+ "value": 2147483647
+ }
+ }
+ },
+ "position": {
+ "x": -4025,
+ "y": -2075
+ }
+ },
+ {
+ "id": "094bc4ed-5c68-4342-84f4-51056c755796",
+ "type": "invocation",
+ "data": {
+ "id": "094bc4ed-5c68-4342-84f4-51056c755796",
+ "type": "boolean",
+ "version": "1.0.1",
+ "label": "Tiled Option",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "value": {
+ "name": "value",
+ "label": "Tiled VAE (Saves VRAM, Color Inconsistency)",
+ "value": true
+ }
+ }
+ },
+ "position": {
+ "x": -2675,
+ "y": -2475
+ }
+ },
+ {
+ "id": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
+ "type": "invocation",
+ "data": {
+ "id": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "Creativity Input",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "Creativity Control (-10 to 10)",
+ "value": 0
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": -1
+ }
+ }
+ },
+ "position": {
+ "x": -3500,
+ "y": -2350
+ }
+ },
+ {
+ "id": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03",
+ "type": "invocation",
+ "data": {
+ "id": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "DIV"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 100
+ }
+ }
+ },
+ "position": {
+ "x": -3500,
+ "y": -1975
+ }
+ },
+ {
+ "id": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c",
+ "type": "invocation",
+ "data": {
+ "id": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "A",
+ "value": 0
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 10
+ }
+ }
+ },
+ "position": {
+ "x": -3500,
+ "y": -2075
+ }
+ },
+ {
+ "id": "49a8cc12-aa19-48c5-b6b3-04e0b603b384",
+ "type": "invocation",
+ "data": {
+ "id": "49a8cc12-aa19-48c5-b6b3-04e0b603b384",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 4.99
+ }
+ }
+ },
+ "position": {
+ "x": -3500,
+ "y": -2025
+ }
+ },
+ {
+ "id": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
+ "type": "invocation",
+ "data": {
+ "id": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "Structural Input",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "Structural Control (-10 to 10)",
+ "value": 0
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 10
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -2100
+ }
+ },
+ {
+ "id": "6636a27a-f130-4a13-b3e5-50b44e4a566f",
+ "type": "invocation",
+ "data": {
+ "id": "6636a27a-f130-4a13-b3e5-50b44e4a566f",
+ "type": "collect",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "item": {
+ "name": "item",
+ "label": ""
+ }
+ }
+ },
+ "position": {
+ "x": -2275,
+ "y": -2075
+ }
+ },
+ {
+ "id": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "type": "invocation",
+ "data": {
+ "id": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "type": "controlnet",
+ "version": "1.1.2",
+ "label": "Initial Control (Use Tile)",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": ""
+ },
+ "control_model": {
+ "name": "control_model",
+ "label": ""
+ },
+ "control_weight": {
+ "name": "control_weight",
+ "label": "",
+ "value": 0.6
+ },
+ "begin_step_percent": {
+ "name": "begin_step_percent",
+ "label": "",
+ "value": 0
+ },
+ "end_step_percent": {
+ "name": "end_step_percent",
+ "label": "",
+ "value": 0.5
+ },
+ "control_mode": {
+ "name": "control_mode",
+ "label": "",
+ "value": "balanced"
+ },
+ "resize_mode": {
+ "name": "resize_mode",
+ "label": "",
+ "value": "just_resize"
+ }
+ }
+ },
+ "position": {
+ "x": -2675,
+ "y": -1775
+ }
+ },
+ {
+ "id": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "type": "invocation",
+ "data": {
+ "id": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "type": "unsharp_mask",
+ "version": "1.2.2",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": ""
+ },
+ "image": {
+ "name": "image",
+ "label": ""
+ },
+ "radius": {
+ "name": "radius",
+ "label": "",
+ "value": 2
+ },
+ "strength": {
+ "name": "strength",
+ "label": "",
+ "value": 50
+ }
+ }
+ },
+ "position": {
+ "x": -4400,
+ "y": -2125
+ }
+ },
+ {
+ "id": "117f982a-03da-49b1-bf9f-29711160ac02",
+ "type": "invocation",
+ "data": {
+ "id": "117f982a-03da-49b1-bf9f-29711160ac02",
+ "type": "i2l",
+ "version": "1.1.0",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": ""
+ },
+ "tiled": {
+ "name": "tiled",
+ "label": "",
+ "value": false
+ },
+ "tile_size": {
+ "name": "tile_size",
+ "label": "",
+ "value": 0
+ },
+ "fp32": {
+ "name": "fp32",
+ "label": "",
+ "value": false
+ }
+ }
+ },
+ "position": {
+ "x": -4025,
+ "y": -2125
+ }
+ },
+ {
+ "id": "c3b60a50-8039-4924-90e3-8c608e1fecb5",
+ "type": "invocation",
+ "data": {
+ "id": "c3b60a50-8039-4924-90e3-8c608e1fecb5",
+ "type": "l2i",
+ "version": "1.3.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": false,
+ "useCache": true,
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": "Output Board"
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": ""
+ },
+ "vae": {
+ "name": "vae",
+ "label": ""
+ },
+ "tiled": {
+ "name": "tiled",
+ "label": "",
+ "value": false
+ },
+ "tile_size": {
+ "name": "tile_size",
+ "label": "",
+ "value": 0
+ },
+ "fp32": {
+ "name": "fp32",
+ "label": "",
+ "value": false
+ }
+ }
+ },
+ "position": {
+ "x": -2675,
+ "y": -2825
+ }
+ },
+ {
+ "id": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "type": "invocation",
+ "data": {
+ "id": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "type": "tiled_multi_diffusion_denoise_latents",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "positive_conditioning": {
+ "name": "positive_conditioning",
+ "label": ""
+ },
+ "negative_conditioning": {
+ "name": "negative_conditioning",
+ "label": ""
+ },
+ "noise": {
+ "name": "noise",
+ "label": ""
+ },
+ "latents": {
+ "name": "latents",
+ "label": ""
+ },
+ "tile_height": {
+ "name": "tile_height",
+ "label": "",
+ "value": 768
+ },
+ "tile_width": {
+ "name": "tile_width",
+ "label": "",
+ "value": 768
+ },
+ "tile_overlap": {
+ "name": "tile_overlap",
+ "label": "",
+ "value": 128
+ },
+ "steps": {
+ "name": "steps",
+ "label": "",
+ "value": 25
+ },
+ "cfg_scale": {
+ "name": "cfg_scale",
+ "label": "",
+ "value": 5
+ },
+ "denoising_start": {
+ "name": "denoising_start",
+ "label": "",
+ "value": 0.6
+ },
+ "denoising_end": {
+ "name": "denoising_end",
+ "label": "",
+ "value": 1
+ },
+ "scheduler": {
+ "name": "scheduler",
+ "label": "",
+ "value": "kdpm_2"
+ },
+ "unet": {
+ "name": "unet",
+ "label": ""
+ },
+ "cfg_rescale_multiplier": {
+ "name": "cfg_rescale_multiplier",
+ "label": "",
+ "value": 0
+ },
+ "control": {
+ "name": "control",
+ "label": ""
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -2825
+ }
+ },
+ {
+ "id": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "type": "invocation",
+ "data": {
+ "id": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "type": "controlnet",
+ "version": "1.1.2",
+ "label": "Second Phase Control (Use Tile)",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "image": {
+ "name": "image",
+ "label": ""
+ },
+ "control_model": {
+ "name": "control_model",
+ "label": ""
+ },
+ "control_weight": {
+ "name": "control_weight",
+ "label": "",
+ "value": 0.25
+ },
+ "begin_step_percent": {
+ "name": "begin_step_percent",
+ "label": "",
+ "value": 0.5
+ },
+ "end_step_percent": {
+ "name": "end_step_percent",
+ "label": "",
+ "value": 0.85
+ },
+ "control_mode": {
+ "name": "control_mode",
+ "label": "Control Mode",
+ "value": "balanced"
+ },
+ "resize_mode": {
+ "name": "resize_mode",
+ "label": "",
+ "value": "just_resize"
+ }
+ }
+ },
+ "position": {
+ "x": -2675,
+ "y": -1325
+ }
+ },
+ {
+ "id": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "type": "invocation",
+ "data": {
+ "id": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "type": "noise",
+ "version": "1.0.2",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "seed": {
+ "name": "seed",
+ "label": "",
+ "value": 3
+ },
+ "width": {
+ "name": "width",
+ "label": "",
+ "value": 512
+ },
+ "height": {
+ "name": "height",
+ "label": "",
+ "value": 512
+ },
+ "use_cpu": {
+ "name": "use_cpu",
+ "label": "",
+ "value": true
+ }
+ }
+ },
+ "position": {
+ "x": -4025,
+ "y": -2025
+ }
+ }
+ ],
+ "edges": [
+ {
+ "id": "reactflow__edge-009b38e3-4e17-4ac5-958c-14891991ae28vae-117f982a-03da-49b1-bf9f-29711160ac02vae",
+ "type": "default",
+ "source": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "target": "117f982a-03da-49b1-bf9f-29711160ac02",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-009b38e3-4e17-4ac5-958c-14891991ae28vae-c3b60a50-8039-4924-90e3-8c608e1fecb5vae",
+ "type": "default",
+ "source": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "target": "c3b60a50-8039-4924-90e3-8c608e1fecb5",
+ "sourceHandle": "vae",
+ "targetHandle": "vae"
+ },
+ {
+ "id": "reactflow__edge-33fe76a0-5efd-4482-a7f0-e2abf1223dc2conditioning-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7anegative_conditioning",
+ "type": "default",
+ "source": "33fe76a0-5efd-4482-a7f0-e2abf1223dc2",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "conditioning",
+ "targetHandle": "negative_conditioning"
+ },
+ {
+ "id": "reactflow__edge-009b38e3-4e17-4ac5-958c-14891991ae28clip-33fe76a0-5efd-4482-a7f0-e2abf1223dc2clip",
+ "type": "default",
+ "source": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "target": "33fe76a0-5efd-4482-a7f0-e2abf1223dc2",
+ "sourceHandle": "clip",
+ "targetHandle": "clip"
+ },
+ {
+ "id": "reactflow__edge-14469dfe-9f49-4a13-89a7-eb4d45794b2bconditioning-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7apositive_conditioning",
+ "type": "default",
+ "source": "14469dfe-9f49-4a13-89a7-eb4d45794b2b",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "conditioning",
+ "targetHandle": "positive_conditioning"
+ },
+ {
+ "id": "reactflow__edge-009b38e3-4e17-4ac5-958c-14891991ae28clip-14469dfe-9f49-4a13-89a7-eb4d45794b2bclip",
+ "type": "default",
+ "source": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "target": "14469dfe-9f49-4a13-89a7-eb4d45794b2b",
+ "sourceHandle": "clip",
+ "targetHandle": "clip"
+ },
+ {
+ "id": "reactflow__edge-009b38e3-4e17-4ac5-958c-14891991ae28unet-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7aunet",
+ "type": "default",
+ "source": "009b38e3-4e17-4ac5-958c-14891991ae28",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "unet",
+ "targetHandle": "unet"
+ },
+ {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21-75a89685-0f82-40ed-9b88-e583673be9fc-collapsed",
+ "type": "collapsed",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "75a89685-0f82-40ed-9b88-e583673be9fc"
+ },
+ {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21-1ed88043-3519-41d5-a895-07944f03de70-collapsed",
+ "type": "collapsed",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "1ed88043-3519-41d5-a895-07944f03de70"
+ },
+ {
+ "id": "49a8cc12-aa19-48c5-b6b3-04e0b603b384-c8f5c671-8c87-4d96-a75e-a9937ac6bc03-collapsed",
+ "type": "collapsed",
+ "source": "49a8cc12-aa19-48c5-b6b3-04e0b603b384",
+ "target": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03"
+ },
+ {
+ "id": "reactflow__edge-c8f5c671-8c87-4d96-a75e-a9937ac6bc03value-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7adenoising_start",
+ "type": "default",
+ "source": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "value",
+ "targetHandle": "denoising_start"
+ },
+ {
+ "id": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c-49a8cc12-aa19-48c5-b6b3-04e0b603b384-collapsed",
+ "type": "collapsed",
+ "source": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c",
+ "target": "49a8cc12-aa19-48c5-b6b3-04e0b603b384"
+ },
+ {
+ "id": "75a89685-0f82-40ed-9b88-e583673be9fc-96e1bcd0-326b-4b67-8b14-239da2440aec-collapsed",
+ "type": "collapsed",
+ "source": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "target": "96e1bcd0-326b-4b67-8b14-239da2440aec"
+ },
+ {
+ "id": "00e2c587-f047-4413-ad15-bd31ea53ce22-71a116e1-c631-48b3-923d-acea4753b887-collapsed",
+ "type": "collapsed",
+ "source": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "target": "71a116e1-c631-48b3-923d-acea4753b887"
+ },
+ {
+ "id": "reactflow__edge-71a116e1-c631-48b3-923d-acea4753b887value-be4082d6-e238-40ea-a9df-fc0d725e8895begin_step_percent",
+ "type": "default",
+ "source": "71a116e1-c631-48b3-923d-acea4753b887",
+ "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "sourceHandle": "value",
+ "targetHandle": "begin_step_percent"
+ },
+ {
+ "id": "reactflow__edge-71a116e1-c631-48b3-923d-acea4753b887value-b78f53b6-2eae-4956-97b4-7e73768d1491end_step_percent",
+ "type": "default",
+ "source": "71a116e1-c631-48b3-923d-acea4753b887",
+ "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "sourceHandle": "value",
+ "targetHandle": "end_step_percent"
+ },
+ {
+ "id": "reactflow__edge-00e2c587-f047-4413-ad15-bd31ea53ce22value-71a116e1-c631-48b3-923d-acea4753b887a",
+ "type": "default",
+ "source": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "target": "71a116e1-c631-48b3-923d-acea4753b887",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-bd094e2f-41e5-4b61-9f7b-56cf337d53favalue-00e2c587-f047-4413-ad15-bd31ea53ce22a",
+ "type": "default",
+ "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
+ "target": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "sourceHandle": "value",
+ "targetHandle": "a"
+ },
+ {
+ "id": "reactflow__edge-96e1bcd0-326b-4b67-8b14-239da2440aecvalue-be4082d6-e238-40ea-a9df-fc0d725e8895control_weight",
+ "type": "default",
+ "source": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "sourceHandle": "value",
+ "targetHandle": "control_weight"
+ },
+ {
+ "id": "reactflow__edge-75a89685-0f82-40ed-9b88-e583673be9fcvalue-96e1bcd0-326b-4b67-8b14-239da2440aeca",
+ "type": "default",
+ "source": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "target": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-9b281506-4079-4a3d-ab40-b386156fcd21value-75a89685-0f82-40ed-9b88-e583673be9fca",
+ "type": "default",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-1ed88043-3519-41d5-a895-07944f03de70value-b78f53b6-2eae-4956-97b4-7e73768d1491control_weight",
+ "type": "default",
+ "source": "1ed88043-3519-41d5-a895-07944f03de70",
+ "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "sourceHandle": "value",
+ "targetHandle": "control_weight"
+ },
+ {
+ "id": "reactflow__edge-9b281506-4079-4a3d-ab40-b386156fcd21value-1ed88043-3519-41d5-a895-07944f03de70a",
+ "type": "default",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "1ed88043-3519-41d5-a895-07944f03de70",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-bd094e2f-41e5-4b61-9f7b-56cf337d53favalue-9b281506-4079-4a3d-ab40-b386156fcd21a",
+ "type": "default",
+ "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
+ "target": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "sourceHandle": "value",
+ "targetHandle": "a"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeheight-8923451b-5a27-4395-b7f2-dce875fca6f5height",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebewidth-8923451b-5a27-4395-b7f2-dce875fca6f5width",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage-b78f53b6-2eae-4956-97b4-7e73768d1491image",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage-be4082d6-e238-40ea-a9df-fc0d725e8895image",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage-117f982a-03da-49b1-bf9f-29711160ac02image",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "117f982a-03da-49b1-bf9f-29711160ac02",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-011039f6-04cf-4607-8eb1-3304eb819c8cimage-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage",
+ "type": "default",
+ "source": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "target": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-f936ebb3-6902-4df9-a775-6a68bac2da70model-be4082d6-e238-40ea-a9df-fc0d725e8895control_model",
+ "type": "default",
+ "source": "f936ebb3-6902-4df9-a775-6a68bac2da70",
+ "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "sourceHandle": "model",
+ "targetHandle": "control_model"
+ },
+ {
+ "id": "reactflow__edge-f936ebb3-6902-4df9-a775-6a68bac2da70model-b78f53b6-2eae-4956-97b4-7e73768d1491control_model",
+ "type": "default",
+ "source": "f936ebb3-6902-4df9-a775-6a68bac2da70",
+ "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "sourceHandle": "model",
+ "targetHandle": "control_model"
+ },
+ {
+ "id": "reactflow__edge-00239057-20d4-4cd2-a010-28727b256ea2value-8923451b-5a27-4395-b7f2-dce875fca6f5seed",
+ "type": "default",
+ "source": "00239057-20d4-4cd2-a010-28727b256ea2",
+ "target": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "sourceHandle": "value",
+ "targetHandle": "seed"
+ },
+ {
+ "id": "reactflow__edge-094bc4ed-5c68-4342-84f4-51056c755796value-c3b60a50-8039-4924-90e3-8c608e1fecb5tiled",
+ "type": "default",
+ "source": "094bc4ed-5c68-4342-84f4-51056c755796",
+ "target": "c3b60a50-8039-4924-90e3-8c608e1fecb5",
+ "sourceHandle": "value",
+ "targetHandle": "tiled"
+ },
+ {
+ "id": "reactflow__edge-094bc4ed-5c68-4342-84f4-51056c755796value-117f982a-03da-49b1-bf9f-29711160ac02tiled",
+ "type": "default",
+ "source": "094bc4ed-5c68-4342-84f4-51056c755796",
+ "target": "117f982a-03da-49b1-bf9f-29711160ac02",
+ "sourceHandle": "value",
+ "targetHandle": "tiled"
+ },
+ {
+ "id": "reactflow__edge-1dd915a3-6756-48ed-b68b-ee3b4bd06c1dvalue-14e65dbe-4249-4b25-9a63-3a10cfaeb61ca",
+ "type": "default",
+ "source": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
+ "target": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c",
+ "sourceHandle": "value",
+ "targetHandle": "a"
+ },
+ {
+ "id": "reactflow__edge-49a8cc12-aa19-48c5-b6b3-04e0b603b384value-c8f5c671-8c87-4d96-a75e-a9937ac6bc03a",
+ "type": "default",
+ "source": "49a8cc12-aa19-48c5-b6b3-04e0b603b384",
+ "target": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-14e65dbe-4249-4b25-9a63-3a10cfaeb61cvalue-49a8cc12-aa19-48c5-b6b3-04e0b603b384a",
+ "type": "default",
+ "source": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c",
+ "target": "49a8cc12-aa19-48c5-b6b3-04e0b603b384",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-6636a27a-f130-4a13-b3e5-50b44e4a566fcollection-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7acontrol",
+ "type": "default",
+ "source": "6636a27a-f130-4a13-b3e5-50b44e4a566f",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "collection",
+ "targetHandle": "control"
+ },
+ {
+ "id": "reactflow__edge-b78f53b6-2eae-4956-97b4-7e73768d1491control-6636a27a-f130-4a13-b3e5-50b44e4a566fitem",
+ "type": "default",
+ "source": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "target": "6636a27a-f130-4a13-b3e5-50b44e4a566f",
+ "sourceHandle": "control",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-be4082d6-e238-40ea-a9df-fc0d725e8895control-6636a27a-f130-4a13-b3e5-50b44e4a566fitem",
+ "type": "default",
+ "source": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "target": "6636a27a-f130-4a13-b3e5-50b44e4a566f",
+ "sourceHandle": "control",
+ "targetHandle": "item"
+ },
+ {
+ "id": "reactflow__edge-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7alatents-c3b60a50-8039-4924-90e3-8c608e1fecb5latents",
+ "type": "default",
+ "source": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "target": "c3b60a50-8039-4924-90e3-8c608e1fecb5",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-117f982a-03da-49b1-bf9f-29711160ac02latents-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7alatents",
+ "type": "default",
+ "source": "117f982a-03da-49b1-bf9f-29711160ac02",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "latents",
+ "targetHandle": "latents"
+ },
+ {
+ "id": "reactflow__edge-8923451b-5a27-4395-b7f2-dce875fca6f5noise-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7anoise",
+ "type": "default",
+ "source": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "noise",
+ "targetHandle": "noise"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SDXL (Beta).json b/invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SDXL.json
similarity index 63%
rename from invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SDXL (Beta).json
rename to invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SDXL.json
index b2842315c4..876ca6f8e6 100644
--- a/invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SDXL (Beta).json
+++ b/invokeai/app/services/workflow_records/default_workflows/MultiDiffusion SDXL.json
@@ -1,44 +1,32 @@
{
- "name": "MultiDiffusion SDXL (Beta)",
+ "name": "MultiDiffusion SDXL",
"author": "Invoke",
"description": "A workflow to upscale an input image with tiled upscaling, using SDXL based models.",
- "version": "1.0.0",
+ "version": "1.1.0",
"contact": "invoke@invoke.ai",
"tags": "tiled, upscaling, sdxl",
"notes": "",
"exposedFields": [
{
- "nodeId": "1ba845a6-eb88-49a1-a490-5fe6754f3ec9",
- "fieldName": "value"
+ "nodeId": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "fieldName": "image"
+ },
+ {
+ "nodeId": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "fieldName": "scale"
},
{
"nodeId": "c3b60a50-8039-4924-90e3-8c608e1fecb5",
"fieldName": "board"
},
- {
- "nodeId": "5ca87ace-edf9-49c7-a424-cd42416b86a7",
- "fieldName": "image"
- },
{
"nodeId": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
"fieldName": "a"
},
- {
- "nodeId": "696de0e1-cdd2-42e8-abeb-57a926bc6df6",
- "fieldName": "a"
- },
{
"nodeId": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
"fieldName": "a"
},
- {
- "nodeId": "e277e4b7-01cd-4daa-86ab-7bfa3cdcd9fd",
- "fieldName": "model"
- },
- {
- "nodeId": "f0cd0d2f-9614-43f7-9944-a75b8d5ccd65",
- "fieldName": "model_name"
- },
{
"nodeId": "c26bff37-4f12-482f-ba45-3a5d729b4c4f",
"fieldName": "value"
@@ -48,13 +36,17 @@
"fieldName": "value"
},
{
- "nodeId": "094bc4ed-5c68-4342-84f4-51056c755796",
- "fieldName": "value"
+ "nodeId": "e277e4b7-01cd-4daa-86ab-7bfa3cdcd9fd",
+ "fieldName": "model"
},
{
"nodeId": "100b3143-b3fb-4ff3-bb3c-8d4d3f89ae3a",
"fieldName": "vae_model"
},
+ {
+ "nodeId": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "fieldName": "image_to_image_model"
+ },
{
"nodeId": "f936ebb3-6902-4df9-a775-6a68bac2da70",
"fieldName": "model"
@@ -64,7 +56,276 @@
"version": "3.0.0",
"category": "default"
},
+ "id": "dd607062-9e1b-48b9-89ad-9762cdfbb8f4",
"nodes": [
+ {
+ "id": "71a116e1-c631-48b3-923d-acea4753b887",
+ "type": "invocation",
+ "data": {
+ "id": "71a116e1-c631-48b3-923d-acea4753b887",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.3
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1550
+ }
+ },
+ {
+ "id": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "type": "invocation",
+ "data": {
+ "id": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.025
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1575
+ }
+ },
+ {
+ "id": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "type": "invocation",
+ "data": {
+ "id": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.45
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1200
+ }
+ },
+ {
+ "id": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "type": "invocation",
+ "data": {
+ "id": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.15
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1225
+ }
+ },
+ {
+ "id": "1ed88043-3519-41d5-a895-07944f03de70",
+ "type": "invocation",
+ "data": {
+ "id": "1ed88043-3519-41d5-a895-07944f03de70",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "ADD"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.3
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1650
+ }
+ },
+ {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "type": "invocation",
+ "data": {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "type": "float_math",
+ "version": "1.0.1",
+ "label": "",
+ "notes": "",
+ "isOpen": false,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "operation": {
+ "name": "operation",
+ "label": "",
+ "value": "MUL"
+ },
+ "a": {
+ "name": "a",
+ "label": "",
+ "value": 1
+ },
+ "b": {
+ "name": "b",
+ "label": "",
+ "value": 0.032
+ }
+ }
+ },
+ "position": {
+ "x": -3050,
+ "y": -1850
+ }
+ },
+ {
+ "id": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "type": "invocation",
+ "data": {
+ "id": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "type": "spandrel_image_to_image_autoscale",
+ "version": "1.0.0",
+ "label": "",
+ "notes": "",
+ "isOpen": true,
+ "isIntermediate": true,
+ "useCache": true,
+ "inputs": {
+ "board": {
+ "name": "board",
+ "label": ""
+ },
+ "metadata": {
+ "name": "metadata",
+ "label": ""
+ },
+ "image": {
+ "name": "image",
+ "label": "Image to Upscale"
+ },
+ "image_to_image_model": {
+ "name": "image_to_image_model",
+ "label": "",
+ "value": {
+ "key": "38bb1a29-8ede-42ba-b77f-64b3478896eb",
+ "hash": "blake3:e52fdbee46a484ebe9b3b20ea0aac0a35a453ab6d0d353da00acfd35ce7a91ed",
+ "name": "4xNomosWebPhoto_esrgan",
+ "base": "sdxl",
+ "type": "spandrel_image_to_image"
+ }
+ },
+ "tile_size": {
+ "name": "tile_size",
+ "label": "",
+ "value": 512
+ },
+ "scale": {
+ "name": "scale",
+ "label": "Scale (2x, 4x, 8x, 16x)",
+ "value": 2
+ },
+ "fit_to_multiple_of_8": {
+ "name": "fit_to_multiple_of_8",
+ "label": "",
+ "value": true
+ }
+ }
+ },
+ "position": {
+ "x": -4750,
+ "y": -2125
+ }
+ },
{
"id": "f936ebb3-6902-4df9-a775-6a68bac2da70",
"type": "invocation",
@@ -80,10 +341,10 @@
"inputs": {
"model": {
"name": "model",
- "label": "ControlNet Model - choose xinsir's tile ControlNet",
+ "label": "ControlNet Model - Choose a Tile ControlNet",
"value": {
- "key": "845b6959-1657-4164-be33-fe0f63ad1752",
- "hash": "random:3b602344599a53b4e4c80a2259362e122543e6f9e8e428be76ab910f9368704b",
+ "key": "74f4651f-0ace-4b7b-b616-e98360257797",
+ "hash": "blake3:167a5b84583aaed3e5c8d660b45830e82e1c602743c689d3c27773c6c8b85b4a",
"name": "controlnet-tile-sdxl-1.0",
"base": "sdxl",
"type": "controlnet"
@@ -92,8 +353,8 @@
}
},
"position": {
- "x": -3983.6167650620723,
- "y": -1329.1431151846386
+ "x": -3450,
+ "y": -1450
}
},
{
@@ -122,8 +383,8 @@
}
},
"position": {
- "x": -4000,
- "y": -1800
+ "x": -4025,
+ "y": -2075
}
},
{
@@ -142,13 +403,13 @@
"value": {
"name": "value",
"label": "Tiled VAE (Saves VRAM, Color Inconsistency)",
- "value": false
+ "value": true
}
}
},
"position": {
- "x": -2746.0467136971292,
- "y": -2219.070070545694
+ "x": -2675,
+ "y": -2475
}
},
{
@@ -172,7 +433,7 @@
}
},
"position": {
- "x": -3525,
+ "x": -3500,
"y": -2525
}
},
@@ -197,115 +458,10 @@
}
},
"position": {
- "x": -3525,
+ "x": -3500,
"y": -2825
}
},
- {
- "id": "6daa9526-382b-491d-964f-f53fc308664f",
- "type": "invocation",
- "data": {
- "id": "6daa9526-382b-491d-964f-f53fc308664f",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "ADD"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 0.35
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 100
- }
- }
- },
- "position": {
- "x": -3500,
- "y": -1450
- }
- },
- {
- "id": "f1afd295-860f-48b6-a76a-90609bf2cc11",
- "type": "invocation",
- "data": {
- "id": "f1afd295-860f-48b6-a76a-90609bf2cc11",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "MUL"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 1
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 0.013
- }
- }
- },
- "position": {
- "x": -3500,
- "y": -1550
- }
- },
- {
- "id": "88ae723e-4933-4371-b52d-3ada52a59d36",
- "type": "invocation",
- "data": {
- "id": "88ae723e-4933-4371-b52d-3ada52a59d36",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "ADD"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 0
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 100
- }
- }
- },
- "position": {
- "x": -3500,
- "y": -1500
- }
- },
{
"id": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
"type": "invocation",
@@ -327,7 +483,7 @@
"a": {
"name": "a",
"label": "Creativity Control (-10 to 10)",
- "value": 5
+ "value": 0
},
"b": {
"name": "b",
@@ -446,146 +602,6 @@
"y": -2025
}
},
- {
- "id": "e4d5ca7c-8fcf-4c59-9c58-67194c80dc73",
- "type": "invocation",
- "data": {
- "id": "e4d5ca7c-8fcf-4c59-9c58-67194c80dc73",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "ADD"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 0
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 1
- }
- }
- },
- "position": {
- "x": -3500,
- "y": -1925
- }
- },
- {
- "id": "696de0e1-cdd2-42e8-abeb-57a926bc6df6",
- "type": "invocation",
- "data": {
- "id": "696de0e1-cdd2-42e8-abeb-57a926bc6df6",
- "type": "float_math",
- "version": "1.0.1",
- "label": "Sharpness Input",
- "notes": "",
- "isOpen": true,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "ADD"
- },
- "a": {
- "name": "a",
- "label": "Sharpness Control (-10 to 10)",
- "value": 0
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 10
- }
- }
- },
- "position": {
- "x": -4750,
- "y": -2275
- }
- },
- {
- "id": "79390b60-4077-4f94-ad0a-4229cc73ddb2",
- "type": "invocation",
- "data": {
- "id": "79390b60-4077-4f94-ad0a-4229cc73ddb2",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "MUL"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 1
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 3.75
- }
- }
- },
- "position": {
- "x": -4750,
- "y": -2000
- }
- },
- {
- "id": "4950132a-2d06-4571-b2c0-55cb37a31e9b",
- "type": "invocation",
- "data": {
- "id": "4950132a-2d06-4571-b2c0-55cb37a31e9b",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "ADD"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 25
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 1
- }
- }
- },
- "position": {
- "x": -4750,
- "y": -1950
- }
- },
{
"id": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
"type": "invocation",
@@ -595,7 +611,7 @@
"version": "1.0.1",
"label": "Structural Input",
"notes": "",
- "isOpen": false,
+ "isOpen": true,
"isIntermediate": true,
"useCache": true,
"inputs": {
@@ -617,78 +633,8 @@
}
},
"position": {
- "x": -3500,
- "y": -1700
- }
- },
- {
- "id": "bc53651f-208b-440c-be30-f93f72ae700e",
- "type": "invocation",
- "data": {
- "id": "bc53651f-208b-440c-be30-f93f72ae700e",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "MUL"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 1
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 0.025
- }
- }
- },
- "position": {
- "x": -3500,
- "y": -1650
- }
- },
- {
- "id": "67346654-cac0-446a-8cde-9af4b5a029a6",
- "type": "invocation",
- "data": {
- "id": "67346654-cac0-446a-8cde-9af4b5a029a6",
- "type": "float_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "ADD"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 0.3
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 1
- }
- }
- },
- "position": {
- "x": -3500,
- "y": -1600
+ "x": -3050,
+ "y": -2100
}
},
{
@@ -711,8 +657,8 @@
}
},
"position": {
- "x": -3125,
- "y": -1500
+ "x": -2275,
+ "y": -2075
}
},
{
@@ -722,7 +668,7 @@
"id": "b78f53b6-2eae-4956-97b4-7e73768d1491",
"type": "controlnet",
"version": "1.1.2",
- "label": "ControlNet (use xinsir's tile ControlNet)",
+ "label": "Initial Control (Use Tile)",
"notes": "",
"isOpen": true,
"isIntermediate": true,
@@ -764,8 +710,8 @@
}
},
"position": {
- "x": -3493.4229674963885,
- "y": -1359.2223984776113
+ "x": -2675,
+ "y": -1775
}
},
{
@@ -836,7 +782,7 @@
}
},
"position": {
- "x": -3525,
+ "x": -3500,
"y": -2300
}
},
@@ -857,9 +803,9 @@
"name": "vae_model",
"label": "",
"value": {
- "key": "4bc2bddf-94d9-4efe-a8e2-5eda28710f4c",
- "hash": "random:67e47a77a1fcef9c0f5cd5d889d71c191f07383a0bf587f1849b2bc3f359440a",
- "name": "sdxl-vae-fp16-fix",
+ "key": "ff926845-090e-4d46-b81e-30289ee47474",
+ "hash": "9705ab1c31fa96b308734214fb7571a958621c7a9247eed82b7d277145f8d9fa",
+ "name": "VAEFix",
"base": "sdxl",
"type": "vae"
}
@@ -867,7 +813,7 @@
}
},
"position": {
- "x": -4000,
+ "x": -4025,
"y": -2575
}
},
@@ -886,12 +832,19 @@
"inputs": {
"model": {
"name": "model",
- "label": "SDXL Model"
+ "label": "SDXL Model",
+ "value": {
+ "key": "ab191f73-68d2-492c-8aec-b438a8cf0f45",
+ "hash": "blake3:2d50e940627e3bf555f015280ec0976d5c1fa100f7bc94e95ffbfc770e98b6fe",
+ "name": "CustomXLv7",
+ "base": "sdxl",
+ "type": "main"
+ }
}
}
},
"position": {
- "x": -4000,
+ "x": -4025,
"y": -2825
}
},
@@ -963,7 +916,7 @@
}
},
"position": {
- "x": -3525,
+ "x": -3500,
"y": -2600
}
},
@@ -976,7 +929,7 @@
"version": "1.2.2",
"label": "",
"notes": "",
- "isOpen": false,
+ "isOpen": true,
"isIntermediate": true,
"useCache": true,
"inputs": {
@@ -1006,49 +959,7 @@
},
"position": {
"x": -4400,
- "y": -1875
- }
- },
- {
- "id": "53c2d5fd-863d-4950-93e0-628f3d61b493",
- "type": "invocation",
- "data": {
- "id": "53c2d5fd-863d-4950-93e0-628f3d61b493",
- "type": "unsharp_mask",
- "version": "1.2.2",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "board": {
- "name": "board",
- "label": ""
- },
- "metadata": {
- "name": "metadata",
- "label": ""
- },
- "image": {
- "name": "image",
- "label": ""
- },
- "radius": {
- "name": "radius",
- "label": "",
- "value": 2
- },
- "strength": {
- "name": "strength",
- "label": "",
- "value": 50
- }
- }
- },
- "position": {
- "x": -4750,
- "y": -1875
+ "y": -2125
}
},
{
@@ -1090,8 +1001,8 @@
}
},
"position": {
- "x": -4000,
- "y": -1875
+ "x": -4025,
+ "y": -2125
}
},
{
@@ -1141,8 +1052,8 @@
}
},
"position": {
- "x": -2750,
- "y": -2575
+ "x": -2675,
+ "y": -2825
}
},
{
@@ -1230,150 +1141,8 @@
}
},
"position": {
- "x": -3125,
- "y": -2575
- }
- },
- {
- "id": "1ba845a6-eb88-49a1-a490-5fe6754f3ec9",
- "type": "invocation",
- "data": {
- "id": "1ba845a6-eb88-49a1-a490-5fe6754f3ec9",
- "type": "integer",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": true,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "value": {
- "name": "value",
- "label": "Scale (2x, 4x)",
- "value": 2
- }
- }
- },
- "position": {
- "x": -4400,
- "y": -2175
- }
- },
- {
- "id": "d350feac-9686-4e0d-bd46-a96bd2630818",
- "type": "invocation",
- "data": {
- "id": "d350feac-9686-4e0d-bd46-a96bd2630818",
- "type": "integer_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "MUL"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 1
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 1
- }
- }
- },
- "position": {
- "x": -4400,
- "y": -1950
- }
- },
- {
- "id": "5b256f14-caab-40ff-b8f0-9679cd542163",
- "type": "invocation",
- "data": {
- "id": "5b256f14-caab-40ff-b8f0-9679cd542163",
- "type": "integer_math",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "operation": {
- "name": "operation",
- "label": "",
- "value": "MUL"
- },
- "a": {
- "name": "a",
- "label": "",
- "value": 1
- },
- "b": {
- "name": "b",
- "label": "",
- "value": 1
- }
- }
- },
- "position": {
- "x": -4400,
- "y": -2000
- }
- },
- {
- "id": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "type": "invocation",
- "data": {
- "id": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "type": "img_resize",
- "version": "1.2.2",
- "label": "",
- "notes": "",
- "isOpen": true,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "board": {
- "name": "board",
- "label": ""
- },
- "metadata": {
- "name": "metadata",
- "label": ""
- },
- "image": {
- "name": "image",
- "label": ""
- },
- "width": {
- "name": "width",
- "label": "",
- "value": 512
- },
- "height": {
- "name": "height",
- "label": "",
- "value": 512
- },
- "resample_mode": {
- "name": "resample_mode",
- "label": "",
- "value": "lanczos"
- }
- }
- },
- "position": {
- "x": -4375,
- "y": -1825
+ "x": -3050,
+ "y": -2825
}
},
{
@@ -1383,7 +1152,7 @@
"id": "be4082d6-e238-40ea-a9df-fc0d725e8895",
"type": "controlnet",
"version": "1.1.2",
- "label": "ControlNet (use xinsir's tile ControlNet)",
+ "label": "Second Phase Control (Use Tile)",
"notes": "",
"isOpen": true,
"isIntermediate": true,
@@ -1410,7 +1179,7 @@
"end_step_percent": {
"name": "end_step_percent",
"label": "",
- "value": 0.8
+ "value": 0.85
},
"control_mode": {
"name": "control_mode",
@@ -1425,8 +1194,8 @@
}
},
"position": {
- "x": -3131.577032503611,
- "y": -1392.1075609956667
+ "x": -2675,
+ "y": -1325
}
},
{
@@ -1465,148 +1234,194 @@
}
},
"position": {
- "x": -4000,
- "y": -1750
- }
- },
- {
- "id": "f0cd0d2f-9614-43f7-9944-a75b8d5ccd65",
- "type": "invocation",
- "data": {
- "id": "f0cd0d2f-9614-43f7-9944-a75b8d5ccd65",
- "type": "esrgan",
- "version": "1.3.2",
- "label": "",
- "notes": "",
- "isOpen": true,
- "isIntermediate": true,
- "useCache": true,
- "inputs": {
- "board": {
- "name": "board",
- "label": ""
- },
- "metadata": {
- "name": "metadata",
- "label": ""
- },
- "image": {
- "name": "image",
- "label": ""
- },
- "model_name": {
- "name": "model_name",
- "label": "Upscaling Model",
- "value": "RealESRGAN_x4plus.pth"
- },
- "tile_size": {
- "name": "tile_size",
- "label": "",
- "value": 500
- }
- }
- },
- "position": {
- "x": -4750,
- "y": -1825
- }
- },
- {
- "id": "7dbb756b-7d79-431c-a46d-d8f7b082c127",
- "type": "invocation",
- "data": {
- "id": "7dbb756b-7d79-431c-a46d-d8f7b082c127",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "type": "float_to_int",
- "inputs": {
- "value": {
- "name": "value",
- "label": "",
- "value": 0
- },
- "multiple": {
- "name": "multiple",
- "label": "",
- "value": 8
- },
- "method": {
- "name": "method",
- "label": "",
- "value": "Floor"
- }
- },
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true
- },
- "position": {
- "x": -4000,
- "y": -1950
- }
- },
- {
- "id": "5ca87ace-edf9-49c7-a424-cd42416b86a7",
- "type": "invocation",
- "data": {
- "id": "5ca87ace-edf9-49c7-a424-cd42416b86a7",
- "version": "1.0.2",
- "label": "",
- "notes": "",
- "type": "image",
- "inputs": {
- "image": {
- "name": "image",
- "label": "Image to Upscale"
- }
- },
- "isOpen": true,
- "isIntermediate": true,
- "useCache": true
- },
- "position": {
- "x": -4750,
- "y": -2850
- }
- },
- {
- "id": "f5d9bf3b-2646-4b17-9894-20fd2b4218ea",
- "type": "invocation",
- "data": {
- "id": "f5d9bf3b-2646-4b17-9894-20fd2b4218ea",
- "version": "1.0.1",
- "label": "",
- "notes": "",
- "type": "float_to_int",
- "inputs": {
- "value": {
- "name": "value",
- "label": "",
- "value": 8
- },
- "multiple": {
- "name": "multiple",
- "label": "",
- "value": 8
- },
- "method": {
- "name": "method",
- "label": "",
- "value": "Floor"
- }
- },
- "isOpen": false,
- "isIntermediate": true,
- "useCache": true
- },
- "position": {
- "x": -4000,
- "y": -2000
+ "x": -4025,
+ "y": -2025
}
}
],
"edges": [
+ {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21-75a89685-0f82-40ed-9b88-e583673be9fc-collapsed",
+ "type": "collapsed",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "75a89685-0f82-40ed-9b88-e583673be9fc"
+ },
+ {
+ "id": "9b281506-4079-4a3d-ab40-b386156fcd21-1ed88043-3519-41d5-a895-07944f03de70-collapsed",
+ "type": "collapsed",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "1ed88043-3519-41d5-a895-07944f03de70"
+ },
+ {
+ "id": "49a8cc12-aa19-48c5-b6b3-04e0b603b384-c8f5c671-8c87-4d96-a75e-a9937ac6bc03-collapsed",
+ "type": "collapsed",
+ "source": "49a8cc12-aa19-48c5-b6b3-04e0b603b384",
+ "target": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03"
+ },
+ {
+ "id": "reactflow__edge-c8f5c671-8c87-4d96-a75e-a9937ac6bc03value-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7adenoising_start",
+ "type": "default",
+ "source": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03",
+ "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
+ "sourceHandle": "value",
+ "targetHandle": "denoising_start"
+ },
+ {
+ "id": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c-49a8cc12-aa19-48c5-b6b3-04e0b603b384-collapsed",
+ "type": "collapsed",
+ "source": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c",
+ "target": "49a8cc12-aa19-48c5-b6b3-04e0b603b384"
+ },
+ {
+ "id": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d-14e65dbe-4249-4b25-9a63-3a10cfaeb61c-collapsed",
+ "type": "collapsed",
+ "source": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
+ "target": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c"
+ },
+ {
+ "id": "75a89685-0f82-40ed-9b88-e583673be9fc-96e1bcd0-326b-4b67-8b14-239da2440aec-collapsed",
+ "type": "collapsed",
+ "source": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "target": "96e1bcd0-326b-4b67-8b14-239da2440aec"
+ },
+ {
+ "id": "00e2c587-f047-4413-ad15-bd31ea53ce22-71a116e1-c631-48b3-923d-acea4753b887-collapsed",
+ "type": "collapsed",
+ "source": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "target": "71a116e1-c631-48b3-923d-acea4753b887"
+ },
+ {
+ "id": "reactflow__edge-71a116e1-c631-48b3-923d-acea4753b887value-be4082d6-e238-40ea-a9df-fc0d725e8895begin_step_percent",
+ "type": "default",
+ "source": "71a116e1-c631-48b3-923d-acea4753b887",
+ "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "sourceHandle": "value",
+ "targetHandle": "begin_step_percent"
+ },
+ {
+ "id": "reactflow__edge-71a116e1-c631-48b3-923d-acea4753b887value-b78f53b6-2eae-4956-97b4-7e73768d1491end_step_percent",
+ "type": "default",
+ "source": "71a116e1-c631-48b3-923d-acea4753b887",
+ "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "sourceHandle": "value",
+ "targetHandle": "end_step_percent"
+ },
+ {
+ "id": "reactflow__edge-00e2c587-f047-4413-ad15-bd31ea53ce22value-71a116e1-c631-48b3-923d-acea4753b887a",
+ "type": "default",
+ "source": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "target": "71a116e1-c631-48b3-923d-acea4753b887",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-bd094e2f-41e5-4b61-9f7b-56cf337d53favalue-00e2c587-f047-4413-ad15-bd31ea53ce22a",
+ "type": "default",
+ "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
+ "target": "00e2c587-f047-4413-ad15-bd31ea53ce22",
+ "sourceHandle": "value",
+ "targetHandle": "a"
+ },
+ {
+ "id": "reactflow__edge-96e1bcd0-326b-4b67-8b14-239da2440aecvalue-be4082d6-e238-40ea-a9df-fc0d725e8895control_weight",
+ "type": "default",
+ "source": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "sourceHandle": "value",
+ "targetHandle": "control_weight"
+ },
+ {
+ "id": "reactflow__edge-75a89685-0f82-40ed-9b88-e583673be9fcvalue-96e1bcd0-326b-4b67-8b14-239da2440aeca",
+ "type": "default",
+ "source": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "target": "96e1bcd0-326b-4b67-8b14-239da2440aec",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-9b281506-4079-4a3d-ab40-b386156fcd21value-75a89685-0f82-40ed-9b88-e583673be9fca",
+ "type": "default",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "75a89685-0f82-40ed-9b88-e583673be9fc",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-1ed88043-3519-41d5-a895-07944f03de70value-b78f53b6-2eae-4956-97b4-7e73768d1491control_weight",
+ "type": "default",
+ "source": "1ed88043-3519-41d5-a895-07944f03de70",
+ "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "sourceHandle": "value",
+ "targetHandle": "control_weight"
+ },
+ {
+ "id": "reactflow__edge-9b281506-4079-4a3d-ab40-b386156fcd21value-1ed88043-3519-41d5-a895-07944f03de70a",
+ "type": "default",
+ "source": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "target": "1ed88043-3519-41d5-a895-07944f03de70",
+ "sourceHandle": "value",
+ "targetHandle": "a",
+ "hidden": true
+ },
+ {
+ "id": "reactflow__edge-bd094e2f-41e5-4b61-9f7b-56cf337d53favalue-9b281506-4079-4a3d-ab40-b386156fcd21a",
+ "type": "default",
+ "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
+ "target": "9b281506-4079-4a3d-ab40-b386156fcd21",
+ "sourceHandle": "value",
+ "targetHandle": "a"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeheight-8923451b-5a27-4395-b7f2-dce875fca6f5height",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "sourceHandle": "height",
+ "targetHandle": "height"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebewidth-8923451b-5a27-4395-b7f2-dce875fca6f5width",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "8923451b-5a27-4395-b7f2-dce875fca6f5",
+ "sourceHandle": "width",
+ "targetHandle": "width"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage-b78f53b6-2eae-4956-97b4-7e73768d1491image",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage-be4082d6-e238-40ea-a9df-fc0d725e8895image",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage-117f982a-03da-49b1-bf9f-29711160ac02image",
+ "type": "default",
+ "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "target": "117f982a-03da-49b1-bf9f-29711160ac02",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
+ {
+ "id": "reactflow__edge-011039f6-04cf-4607-8eb1-3304eb819c8cimage-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage",
+ "type": "default",
+ "source": "011039f6-04cf-4607-8eb1-3304eb819c8c",
+ "target": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
+ "sourceHandle": "image",
+ "targetHandle": "image"
+ },
{
"id": "reactflow__edge-f936ebb3-6902-4df9-a775-6a68bac2da70model-be4082d6-e238-40ea-a9df-fc0d725e8895control_model",
"type": "default",
@@ -1623,24 +1438,6 @@
"sourceHandle": "model",
"targetHandle": "control_model"
},
- {
- "id": "49a8cc12-aa19-48c5-b6b3-04e0b603b384-c8f5c671-8c87-4d96-a75e-a9937ac6bc03-collapsed",
- "type": "collapsed",
- "source": "49a8cc12-aa19-48c5-b6b3-04e0b603b384",
- "target": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03"
- },
- {
- "id": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c-49a8cc12-aa19-48c5-b6b3-04e0b603b384-collapsed",
- "type": "collapsed",
- "source": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c",
- "target": "49a8cc12-aa19-48c5-b6b3-04e0b603b384"
- },
- {
- "id": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d-14e65dbe-4249-4b25-9a63-3a10cfaeb61c-collapsed",
- "type": "collapsed",
- "source": "1dd915a3-6756-48ed-b68b-ee3b4bd06c1d",
- "target": "14e65dbe-4249-4b25-9a63-3a10cfaeb61c"
- },
{
"id": "reactflow__edge-00239057-20d4-4cd2-a010-28727b256ea2value-8923451b-5a27-4395-b7f2-dce875fca6f5seed",
"type": "default",
@@ -1665,36 +1462,6 @@
"sourceHandle": "value",
"targetHandle": "tiled"
},
- {
- "id": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03-e4d5ca7c-8fcf-4c59-9c58-67194c80dc73-collapsed",
- "type": "collapsed",
- "source": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03",
- "target": "e4d5ca7c-8fcf-4c59-9c58-67194c80dc73"
- },
- {
- "id": "d350feac-9686-4e0d-bd46-a96bd2630818-7dbb756b-7d79-431c-a46d-d8f7b082c127-collapsed",
- "type": "collapsed",
- "source": "d350feac-9686-4e0d-bd46-a96bd2630818",
- "target": "7dbb756b-7d79-431c-a46d-d8f7b082c127"
- },
- {
- "id": "5b256f14-caab-40ff-b8f0-9679cd542163-f5d9bf3b-2646-4b17-9894-20fd2b4218ea-collapsed",
- "type": "collapsed",
- "source": "5b256f14-caab-40ff-b8f0-9679cd542163",
- "target": "f5d9bf3b-2646-4b17-9894-20fd2b4218ea"
- },
- {
- "id": "4950132a-2d06-4571-b2c0-55cb37a31e9b-041c59cc-f9e4-4dc9-8b31-84648c5f3ebe-collapsed",
- "type": "collapsed",
- "source": "4950132a-2d06-4571-b2c0-55cb37a31e9b",
- "target": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe"
- },
- {
- "id": "4950132a-2d06-4571-b2c0-55cb37a31e9b-53c2d5fd-863d-4950-93e0-628f3d61b493-collapsed",
- "type": "collapsed",
- "source": "4950132a-2d06-4571-b2c0-55cb37a31e9b",
- "target": "53c2d5fd-863d-4950-93e0-628f3d61b493"
- },
{
"id": "reactflow__edge-f5ca24ee-21c5-4c8c-8d3c-371b5079b086value-27215391-b20e-412a-b854-7fa5927f5437style",
"type": "default",
@@ -1727,104 +1494,6 @@
"sourceHandle": "value",
"targetHandle": "prompt"
},
- {
- "id": "88ae723e-4933-4371-b52d-3ada52a59d36-6daa9526-382b-491d-964f-f53fc308664f-collapsed",
- "type": "collapsed",
- "source": "88ae723e-4933-4371-b52d-3ada52a59d36",
- "target": "6daa9526-382b-491d-964f-f53fc308664f"
- },
- {
- "id": "f1afd295-860f-48b6-a76a-90609bf2cc11-88ae723e-4933-4371-b52d-3ada52a59d36-collapsed",
- "type": "collapsed",
- "source": "f1afd295-860f-48b6-a76a-90609bf2cc11",
- "target": "88ae723e-4933-4371-b52d-3ada52a59d36"
- },
- {
- "id": "bc53651f-208b-440c-be30-f93f72ae700e-67346654-cac0-446a-8cde-9af4b5a029a6-collapsed",
- "type": "collapsed",
- "source": "bc53651f-208b-440c-be30-f93f72ae700e",
- "target": "67346654-cac0-446a-8cde-9af4b5a029a6"
- },
- {
- "id": "reactflow__edge-67346654-cac0-446a-8cde-9af4b5a029a6value-be4082d6-e238-40ea-a9df-fc0d725e8895begin_step_percent",
- "type": "default",
- "source": "67346654-cac0-446a-8cde-9af4b5a029a6",
- "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
- "sourceHandle": "value",
- "targetHandle": "begin_step_percent"
- },
- {
- "id": "reactflow__edge-67346654-cac0-446a-8cde-9af4b5a029a6value-b78f53b6-2eae-4956-97b4-7e73768d1491end_step_percent",
- "type": "default",
- "source": "67346654-cac0-446a-8cde-9af4b5a029a6",
- "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
- "sourceHandle": "value",
- "targetHandle": "end_step_percent"
- },
- {
- "id": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa-f1afd295-860f-48b6-a76a-90609bf2cc11-collapsed",
- "type": "collapsed",
- "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
- "target": "f1afd295-860f-48b6-a76a-90609bf2cc11"
- },
- {
- "id": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa-bc53651f-208b-440c-be30-f93f72ae700e-collapsed",
- "type": "collapsed",
- "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
- "target": "bc53651f-208b-440c-be30-f93f72ae700e"
- },
- {
- "id": "reactflow__edge-bc53651f-208b-440c-be30-f93f72ae700evalue-67346654-cac0-446a-8cde-9af4b5a029a6b",
- "type": "default",
- "source": "bc53651f-208b-440c-be30-f93f72ae700e",
- "target": "67346654-cac0-446a-8cde-9af4b5a029a6",
- "sourceHandle": "value",
- "targetHandle": "b",
- "hidden": true
- },
- {
- "id": "reactflow__edge-6daa9526-382b-491d-964f-f53fc308664fvalue-b78f53b6-2eae-4956-97b4-7e73768d1491control_weight",
- "type": "default",
- "source": "6daa9526-382b-491d-964f-f53fc308664f",
- "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
- "sourceHandle": "value",
- "targetHandle": "control_weight"
- },
- {
- "id": "reactflow__edge-88ae723e-4933-4371-b52d-3ada52a59d36value-6daa9526-382b-491d-964f-f53fc308664fb",
- "type": "default",
- "source": "88ae723e-4933-4371-b52d-3ada52a59d36",
- "target": "6daa9526-382b-491d-964f-f53fc308664f",
- "sourceHandle": "value",
- "targetHandle": "b",
- "hidden": true
- },
- {
- "id": "reactflow__edge-88ae723e-4933-4371-b52d-3ada52a59d36value-be4082d6-e238-40ea-a9df-fc0d725e8895control_weight",
- "type": "default",
- "source": "88ae723e-4933-4371-b52d-3ada52a59d36",
- "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
- "sourceHandle": "value",
- "targetHandle": "control_weight"
- },
- {
- "id": "reactflow__edge-f1afd295-860f-48b6-a76a-90609bf2cc11value-88ae723e-4933-4371-b52d-3ada52a59d36b",
- "type": "default",
- "source": "f1afd295-860f-48b6-a76a-90609bf2cc11",
- "target": "88ae723e-4933-4371-b52d-3ada52a59d36",
- "sourceHandle": "value",
- "targetHandle": "b",
- "hidden": true
- },
- {
- "id": "reactflow__edge-bd094e2f-41e5-4b61-9f7b-56cf337d53favalue-f1afd295-860f-48b6-a76a-90609bf2cc11a",
- "type": "default",
- "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
- "target": "f1afd295-860f-48b6-a76a-90609bf2cc11",
- "sourceHandle": "value",
- "targetHandle": "a",
- "hidden": true
- },
{
"id": "reactflow__edge-1dd915a3-6756-48ed-b68b-ee3b4bd06c1dvalue-14e65dbe-4249-4b25-9a63-3a10cfaeb61ca",
"type": "default",
@@ -1834,23 +1503,6 @@
"targetHandle": "a",
"hidden": true
},
- {
- "id": "reactflow__edge-e4d5ca7c-8fcf-4c59-9c58-67194c80dc73value-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7adenoising_start",
- "type": "default",
- "source": "e4d5ca7c-8fcf-4c59-9c58-67194c80dc73",
- "target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
- "sourceHandle": "value",
- "targetHandle": "denoising_start"
- },
- {
- "id": "reactflow__edge-c8f5c671-8c87-4d96-a75e-a9937ac6bc03value-e4d5ca7c-8fcf-4c59-9c58-67194c80dc73b",
- "type": "default",
- "source": "c8f5c671-8c87-4d96-a75e-a9937ac6bc03",
- "target": "e4d5ca7c-8fcf-4c59-9c58-67194c80dc73",
- "sourceHandle": "value",
- "targetHandle": "b",
- "hidden": true
- },
{
"id": "reactflow__edge-49a8cc12-aa19-48c5-b6b3-04e0b603b384value-c8f5c671-8c87-4d96-a75e-a9937ac6bc03a",
"type": "default",
@@ -1869,56 +1521,6 @@
"targetHandle": "a",
"hidden": true
},
- {
- "id": "79390b60-4077-4f94-ad0a-4229cc73ddb2-4950132a-2d06-4571-b2c0-55cb37a31e9b-collapsed",
- "type": "collapsed",
- "source": "79390b60-4077-4f94-ad0a-4229cc73ddb2",
- "target": "4950132a-2d06-4571-b2c0-55cb37a31e9b"
- },
- {
- "id": "reactflow__edge-4950132a-2d06-4571-b2c0-55cb37a31e9bvalue-041c59cc-f9e4-4dc9-8b31-84648c5f3ebestrength",
- "type": "default",
- "source": "4950132a-2d06-4571-b2c0-55cb37a31e9b",
- "target": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
- "sourceHandle": "value",
- "targetHandle": "strength",
- "hidden": true
- },
- {
- "id": "reactflow__edge-4950132a-2d06-4571-b2c0-55cb37a31e9bvalue-53c2d5fd-863d-4950-93e0-628f3d61b493strength",
- "type": "default",
- "source": "4950132a-2d06-4571-b2c0-55cb37a31e9b",
- "target": "53c2d5fd-863d-4950-93e0-628f3d61b493",
- "sourceHandle": "value",
- "targetHandle": "strength",
- "hidden": true
- },
- {
- "id": "reactflow__edge-79390b60-4077-4f94-ad0a-4229cc73ddb2value-4950132a-2d06-4571-b2c0-55cb37a31e9bb",
- "type": "default",
- "source": "79390b60-4077-4f94-ad0a-4229cc73ddb2",
- "target": "4950132a-2d06-4571-b2c0-55cb37a31e9b",
- "sourceHandle": "value",
- "targetHandle": "b",
- "hidden": true
- },
- {
- "id": "reactflow__edge-696de0e1-cdd2-42e8-abeb-57a926bc6df6value-79390b60-4077-4f94-ad0a-4229cc73ddb2a",
- "type": "default",
- "source": "696de0e1-cdd2-42e8-abeb-57a926bc6df6",
- "target": "79390b60-4077-4f94-ad0a-4229cc73ddb2",
- "sourceHandle": "value",
- "targetHandle": "a"
- },
- {
- "id": "reactflow__edge-bd094e2f-41e5-4b61-9f7b-56cf337d53favalue-bc53651f-208b-440c-be30-f93f72ae700ea",
- "type": "default",
- "source": "bd094e2f-41e5-4b61-9f7b-56cf337d53fa",
- "target": "bc53651f-208b-440c-be30-f93f72ae700e",
- "sourceHandle": "value",
- "targetHandle": "a",
- "hidden": true
- },
{
"id": "reactflow__edge-6636a27a-f130-4a13-b3e5-50b44e4a566fcollection-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7acontrol",
"type": "default",
@@ -1943,14 +1545,6 @@
"sourceHandle": "control",
"targetHandle": "item"
},
- {
- "id": "reactflow__edge-7671553a-cd4b-4e25-8332-9d5667e64493image-b78f53b6-2eae-4956-97b4-7e73768d1491image",
- "type": "default",
- "source": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "target": "b78f53b6-2eae-4956-97b4-7e73768d1491",
- "sourceHandle": "image",
- "targetHandle": "image"
- },
{
"id": "reactflow__edge-e277e4b7-01cd-4daa-86ab-7bfa3cdcd9fdclip2-27215391-b20e-412a-b854-7fa5927f5437clip2",
"type": "default",
@@ -2023,38 +1617,6 @@
"sourceHandle": "clip",
"targetHandle": "clip"
},
- {
- "id": "reactflow__edge-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage-7671553a-cd4b-4e25-8332-9d5667e64493image",
- "type": "default",
- "source": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
- "target": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "sourceHandle": "image",
- "targetHandle": "image"
- },
- {
- "id": "reactflow__edge-f0cd0d2f-9614-43f7-9944-a75b8d5ccd65image-041c59cc-f9e4-4dc9-8b31-84648c5f3ebeimage",
- "type": "default",
- "source": "f0cd0d2f-9614-43f7-9944-a75b8d5ccd65",
- "target": "041c59cc-f9e4-4dc9-8b31-84648c5f3ebe",
- "sourceHandle": "image",
- "targetHandle": "image"
- },
- {
- "id": "reactflow__edge-53c2d5fd-863d-4950-93e0-628f3d61b493image-f0cd0d2f-9614-43f7-9944-a75b8d5ccd65image",
- "type": "default",
- "source": "53c2d5fd-863d-4950-93e0-628f3d61b493",
- "target": "f0cd0d2f-9614-43f7-9944-a75b8d5ccd65",
- "sourceHandle": "image",
- "targetHandle": "image"
- },
- {
- "id": "reactflow__edge-5ca87ace-edf9-49c7-a424-cd42416b86a7image-53c2d5fd-863d-4950-93e0-628f3d61b493image",
- "type": "default",
- "source": "5ca87ace-edf9-49c7-a424-cd42416b86a7",
- "target": "53c2d5fd-863d-4950-93e0-628f3d61b493",
- "sourceHandle": "image",
- "targetHandle": "image"
- },
{
"id": "reactflow__edge-8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7alatents-c3b60a50-8039-4924-90e3-8c608e1fecb5latents",
"type": "default",
@@ -2078,104 +1640,6 @@
"target": "8dba0d37-cd2e-4fe5-ae9f-5464b85a8a7a",
"sourceHandle": "noise",
"targetHandle": "noise"
- },
- {
- "id": "reactflow__edge-d350feac-9686-4e0d-bd46-a96bd2630818value-7dbb756b-7d79-431c-a46d-d8f7b082c127value",
- "type": "default",
- "source": "d350feac-9686-4e0d-bd46-a96bd2630818",
- "target": "7dbb756b-7d79-431c-a46d-d8f7b082c127",
- "sourceHandle": "value",
- "targetHandle": "value",
- "hidden": true
- },
- {
- "id": "reactflow__edge-5b256f14-caab-40ff-b8f0-9679cd542163value-f5d9bf3b-2646-4b17-9894-20fd2b4218eavalue",
- "type": "default",
- "source": "5b256f14-caab-40ff-b8f0-9679cd542163",
- "target": "f5d9bf3b-2646-4b17-9894-20fd2b4218ea",
- "sourceHandle": "value",
- "targetHandle": "value",
- "hidden": true
- },
- {
- "id": "reactflow__edge-7671553a-cd4b-4e25-8332-9d5667e64493height-8923451b-5a27-4395-b7f2-dce875fca6f5height",
- "type": "default",
- "source": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "target": "8923451b-5a27-4395-b7f2-dce875fca6f5",
- "sourceHandle": "height",
- "targetHandle": "height"
- },
- {
- "id": "reactflow__edge-7671553a-cd4b-4e25-8332-9d5667e64493width-8923451b-5a27-4395-b7f2-dce875fca6f5width",
- "type": "default",
- "source": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "target": "8923451b-5a27-4395-b7f2-dce875fca6f5",
- "sourceHandle": "width",
- "targetHandle": "width"
- },
- {
- "id": "reactflow__edge-7671553a-cd4b-4e25-8332-9d5667e64493image-117f982a-03da-49b1-bf9f-29711160ac02image",
- "type": "default",
- "source": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "target": "117f982a-03da-49b1-bf9f-29711160ac02",
- "sourceHandle": "image",
- "targetHandle": "image"
- },
- {
- "id": "reactflow__edge-7671553a-cd4b-4e25-8332-9d5667e64493image-be4082d6-e238-40ea-a9df-fc0d725e8895image",
- "type": "default",
- "source": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "target": "be4082d6-e238-40ea-a9df-fc0d725e8895",
- "sourceHandle": "image",
- "targetHandle": "image"
- },
- {
- "id": "reactflow__edge-7dbb756b-7d79-431c-a46d-d8f7b082c127value-7671553a-cd4b-4e25-8332-9d5667e64493height",
- "type": "default",
- "source": "7dbb756b-7d79-431c-a46d-d8f7b082c127",
- "target": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "sourceHandle": "value",
- "targetHandle": "height"
- },
- {
- "id": "reactflow__edge-f5d9bf3b-2646-4b17-9894-20fd2b4218eavalue-7671553a-cd4b-4e25-8332-9d5667e64493width",
- "type": "default",
- "source": "f5d9bf3b-2646-4b17-9894-20fd2b4218ea",
- "target": "7671553a-cd4b-4e25-8332-9d5667e64493",
- "sourceHandle": "value",
- "targetHandle": "width"
- },
- {
- "id": "reactflow__edge-5ca87ace-edf9-49c7-a424-cd42416b86a7height-d350feac-9686-4e0d-bd46-a96bd2630818a",
- "type": "default",
- "source": "5ca87ace-edf9-49c7-a424-cd42416b86a7",
- "target": "d350feac-9686-4e0d-bd46-a96bd2630818",
- "sourceHandle": "height",
- "targetHandle": "a"
- },
- {
- "id": "reactflow__edge-1ba845a6-eb88-49a1-a490-5fe6754f3ec9value-d350feac-9686-4e0d-bd46-a96bd2630818b",
- "type": "default",
- "source": "1ba845a6-eb88-49a1-a490-5fe6754f3ec9",
- "target": "d350feac-9686-4e0d-bd46-a96bd2630818",
- "sourceHandle": "value",
- "targetHandle": "b"
- },
- {
- "id": "reactflow__edge-1ba845a6-eb88-49a1-a490-5fe6754f3ec9value-5b256f14-caab-40ff-b8f0-9679cd542163b",
- "type": "default",
- "source": "1ba845a6-eb88-49a1-a490-5fe6754f3ec9",
- "target": "5b256f14-caab-40ff-b8f0-9679cd542163",
- "sourceHandle": "value",
- "targetHandle": "b"
- },
- {
- "id": "reactflow__edge-5ca87ace-edf9-49c7-a424-cd42416b86a7width-5b256f14-caab-40ff-b8f0-9679cd542163a",
- "type": "default",
- "source": "5ca87ace-edf9-49c7-a424-cd42416b86a7",
- "target": "5b256f14-caab-40ff-b8f0-9679cd542163",
- "sourceHandle": "width",
- "targetHandle": "a"
}
]
-}
+}
\ No newline at end of file
diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py
index f6cc5929c8..332ac6c8fa 100644
--- a/invokeai/backend/model_manager/config.py
+++ b/invokeai/backend/model_manager/config.py
@@ -354,7 +354,7 @@ class CLIPVisionDiffusersConfig(DiffusersConfigBase):
"""Model config for CLIPVision."""
type: Literal[ModelType.CLIPVision] = ModelType.CLIPVision
- format: Literal[ModelFormat.Diffusers]
+ format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers
@staticmethod
def get_tag() -> Tag:
@@ -365,7 +365,7 @@ class T2IAdapterConfig(DiffusersConfigBase, ControlAdapterConfigBase):
"""Model config for T2I."""
type: Literal[ModelType.T2IAdapter] = ModelType.T2IAdapter
- format: Literal[ModelFormat.Diffusers]
+ format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers
@staticmethod
def get_tag() -> Tag:
diff --git a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py
index d90352f0e6..33ce4abc4d 100644
--- a/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py
+++ b/invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py
@@ -98,6 +98,9 @@ class StableDiffusionDiffusersModel(GenericDiffusersLoader):
ModelVariantType.Normal: StableDiffusionXLPipeline,
ModelVariantType.Inpaint: StableDiffusionXLInpaintPipeline,
},
+ BaseModelType.StableDiffusionXLRefiner: {
+ ModelVariantType.Normal: StableDiffusionXLPipeline,
+ },
}
assert isinstance(config, MainCheckpointConfig)
try:
diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py
index e1526392f6..c460a5e86e 100644
--- a/invokeai/backend/model_manager/starter_models.py
+++ b/invokeai/backend/model_manager/starter_models.py
@@ -187,164 +187,171 @@ STARTER_MODELS: list[StarterModel] = [
# endregion
# region ControlNet
StarterModel(
- name="QRCode Monster",
+ name="QRCode Monster v2 (SD1.5)",
base=BaseModelType.StableDiffusion1,
- source="monster-labs/control_v1p_sd15_qrcode_monster",
- description="Controlnet model that generates scannable creative QR codes",
+ source="monster-labs/control_v1p_sd15_qrcode_monster::v2",
+ description="ControlNet model that generates scannable creative QR codes",
+ type=ModelType.ControlNet,
+ ),
+ StarterModel(
+ name="QRCode Monster (SDXL)",
+ base=BaseModelType.StableDiffusionXL,
+ source="monster-labs/control_v1p_sdxl_qrcode_monster",
+ description="ControlNet model that generates scannable creative QR codes",
type=ModelType.ControlNet,
),
StarterModel(
name="canny",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_canny",
- description="Controlnet weights trained on sd-1.5 with canny conditioning.",
+ description="ControlNet weights trained on sd-1.5 with canny conditioning.",
type=ModelType.ControlNet,
),
StarterModel(
name="inpaint",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_inpaint",
- description="Controlnet weights trained on sd-1.5 with canny conditioning, inpaint version",
+ description="ControlNet weights trained on sd-1.5 with canny conditioning, inpaint version",
type=ModelType.ControlNet,
),
StarterModel(
name="mlsd",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_mlsd",
- description="Controlnet weights trained on sd-1.5 with canny conditioning, MLSD version",
+ description="ControlNet weights trained on sd-1.5 with canny conditioning, MLSD version",
type=ModelType.ControlNet,
),
StarterModel(
name="depth",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11f1p_sd15_depth",
- description="Controlnet weights trained on sd-1.5 with depth conditioning",
+ description="ControlNet weights trained on sd-1.5 with depth conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="normal_bae",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_normalbae",
- description="Controlnet weights trained on sd-1.5 with normalbae image conditioning",
+ description="ControlNet weights trained on sd-1.5 with normalbae image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="seg",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_seg",
- description="Controlnet weights trained on sd-1.5 with seg image conditioning",
+ description="ControlNet weights trained on sd-1.5 with seg image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="lineart",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_lineart",
- description="Controlnet weights trained on sd-1.5 with lineart image conditioning",
+ description="ControlNet weights trained on sd-1.5 with lineart image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="lineart_anime",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15s2_lineart_anime",
- description="Controlnet weights trained on sd-1.5 with anime image conditioning",
+ description="ControlNet weights trained on sd-1.5 with anime image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="openpose",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_openpose",
- description="Controlnet weights trained on sd-1.5 with openpose image conditioning",
+ description="ControlNet weights trained on sd-1.5 with openpose image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="scribble",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_scribble",
- description="Controlnet weights trained on sd-1.5 with scribble image conditioning",
+ description="ControlNet weights trained on sd-1.5 with scribble image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="softedge",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11p_sd15_softedge",
- description="Controlnet weights trained on sd-1.5 with soft edge conditioning",
+ description="ControlNet weights trained on sd-1.5 with soft edge conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="shuffle",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11e_sd15_shuffle",
- description="Controlnet weights trained on sd-1.5 with shuffle image conditioning",
+ description="ControlNet weights trained on sd-1.5 with shuffle image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="tile",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11f1e_sd15_tile",
- description="Controlnet weights trained on sd-1.5 with tiled image conditioning",
+ description="ControlNet weights trained on sd-1.5 with tiled image conditioning",
type=ModelType.ControlNet,
),
StarterModel(
name="ip2p",
base=BaseModelType.StableDiffusion1,
source="lllyasviel/control_v11e_sd15_ip2p",
- description="Controlnet weights trained on sd-1.5 with ip2p conditioning.",
+ description="ControlNet weights trained on sd-1.5 with ip2p conditioning.",
type=ModelType.ControlNet,
),
StarterModel(
name="canny-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="xinsir/controlnet-canny-sdxl-1.0",
- description="Controlnet weights trained on sdxl-1.0 with canny conditioning, by Xinsir.",
+ source="xinsir/controlNet-canny-sdxl-1.0",
+ description="ControlNet weights trained on sdxl-1.0 with canny conditioning, by Xinsir.",
type=ModelType.ControlNet,
),
StarterModel(
name="depth-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="diffusers/controlnet-depth-sdxl-1.0",
- description="Controlnet weights trained on sdxl-1.0 with depth conditioning.",
+ source="diffusers/controlNet-depth-sdxl-1.0",
+ description="ControlNet weights trained on sdxl-1.0 with depth conditioning.",
type=ModelType.ControlNet,
),
StarterModel(
name="softedge-dexined-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="SargeZT/controlnet-sd-xl-1.0-softedge-dexined",
- description="Controlnet weights trained on sdxl-1.0 with dexined soft edge preprocessing.",
+ source="SargeZT/controlNet-sd-xl-1.0-softedge-dexined",
+ description="ControlNet weights trained on sdxl-1.0 with dexined soft edge preprocessing.",
type=ModelType.ControlNet,
),
StarterModel(
name="depth-16bit-zoe-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="SargeZT/controlnet-sd-xl-1.0-depth-16bit-zoe",
- description="Controlnet weights trained on sdxl-1.0 with Zoe's preprocessor (16 bits).",
+ source="SargeZT/controlNet-sd-xl-1.0-depth-16bit-zoe",
+ description="ControlNet weights trained on sdxl-1.0 with Zoe's preprocessor (16 bits).",
type=ModelType.ControlNet,
),
StarterModel(
name="depth-zoe-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="diffusers/controlnet-zoe-depth-sdxl-1.0",
- description="Controlnet weights trained on sdxl-1.0 with Zoe's preprocessor (32 bits).",
+ source="diffusers/controlNet-zoe-depth-sdxl-1.0",
+ description="ControlNet weights trained on sdxl-1.0 with Zoe's preprocessor (32 bits).",
type=ModelType.ControlNet,
),
StarterModel(
name="openpose-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="xinsir/controlnet-openpose-sdxl-1.0",
- description="Controlnet weights trained on sdxl-1.0 compatible with the DWPose processor by Xinsir.",
+ source="xinsir/controlNet-openpose-sdxl-1.0",
+ description="ControlNet weights trained on sdxl-1.0 compatible with the DWPose processor by Xinsir.",
type=ModelType.ControlNet,
),
StarterModel(
name="scribble-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="xinsir/controlnet-scribble-sdxl-1.0",
- description="Controlnet weights trained on sdxl-1.0 compatible with various lineart processors and black/white sketches by Xinsir.",
+ source="xinsir/controlNet-scribble-sdxl-1.0",
+ description="ControlNet weights trained on sdxl-1.0 compatible with various lineart processors and black/white sketches by Xinsir.",
type=ModelType.ControlNet,
),
StarterModel(
name="tile-sdxl",
base=BaseModelType.StableDiffusionXL,
- source="xinsir/controlnet-tile-sdxl-1.0",
- description="Controlnet weights trained on sdxl-1.0 with tiled image conditioning",
+ source="xinsir/controlNet-tile-sdxl-1.0",
+ description="ControlNet weights trained on sdxl-1.0 with tiled image conditioning",
type=ModelType.ControlNet,
),
# endregion
diff --git a/invokeai/backend/stable_diffusion/__init__.py b/invokeai/backend/stable_diffusion/__init__.py
index 440cb4410b..6a6f2ebc49 100644
--- a/invokeai/backend/stable_diffusion/__init__.py
+++ b/invokeai/backend/stable_diffusion/__init__.py
@@ -7,11 +7,9 @@ from invokeai.backend.stable_diffusion.diffusers_pipeline import ( # noqa: F401
StableDiffusionGeneratorPipeline,
)
from invokeai.backend.stable_diffusion.diffusion import InvokeAIDiffuserComponent # noqa: F401
-from invokeai.backend.stable_diffusion.seamless import set_seamless # noqa: F401
__all__ = [
"PipelineIntermediateState",
"StableDiffusionGeneratorPipeline",
"InvokeAIDiffuserComponent",
- "set_seamless",
]
diff --git a/invokeai/backend/stable_diffusion/denoise_context.py b/invokeai/backend/stable_diffusion/denoise_context.py
index 2b43d3fb0f..9060d54977 100644
--- a/invokeai/backend/stable_diffusion/denoise_context.py
+++ b/invokeai/backend/stable_diffusion/denoise_context.py
@@ -83,47 +83,47 @@ class DenoiseContext:
unet: Optional[UNet2DConditionModel] = None
# Current state of latent-space image in denoising process.
- # None until `pre_denoise_loop` callback.
+ # None until `PRE_DENOISE_LOOP` callback.
# Shape: [batch, channels, latent_height, latent_width]
latents: Optional[torch.Tensor] = None
# Current denoising step index.
- # None until `pre_step` callback.
+ # None until `PRE_STEP` callback.
step_index: Optional[int] = None
# Current denoising step timestep.
- # None until `pre_step` callback.
+ # None until `PRE_STEP` callback.
timestep: Optional[torch.Tensor] = None
# Arguments which will be passed to UNet model.
- # Available in `pre_unet`/`post_unet` callbacks, otherwise will be None.
+ # Available in `PRE_UNET`/`POST_UNET` callbacks, otherwise will be None.
unet_kwargs: Optional[UNetKwargs] = None
# SchedulerOutput class returned from step function(normally, generated by scheduler).
- # Supposed to be used only in `post_step` callback, otherwise can be None.
+ # Supposed to be used only in `POST_STEP` callback, otherwise can be None.
step_output: Optional[SchedulerOutput] = None
# Scaled version of `latents`, which will be passed to unet_kwargs initialization.
- # Available in events inside step(between `pre_step` and `post_stop`).
+ # Available in events inside step(between `PRE_STEP` and `POST_STEP`).
# Shape: [batch, channels, latent_height, latent_width]
latent_model_input: Optional[torch.Tensor] = None
# [TMP] Defines on which conditionings current unet call will be runned.
- # Available in `pre_unet`/`post_unet` callbacks, otherwise will be None.
+ # Available in `PRE_UNET`/`POST_UNET` callbacks, otherwise will be None.
conditioning_mode: Optional[ConditioningMode] = None
# [TMP] Noise predictions from negative conditioning.
- # Available in `apply_cfg` and `post_apply_cfg` callbacks, otherwise will be None.
+ # Available in `POST_COMBINE_NOISE_PREDS` callback, otherwise will be None.
# Shape: [batch, channels, latent_height, latent_width]
negative_noise_pred: Optional[torch.Tensor] = None
# [TMP] Noise predictions from positive conditioning.
- # Available in `apply_cfg` and `post_apply_cfg` callbacks, otherwise will be None.
+ # Available in `POST_COMBINE_NOISE_PREDS` callback, otherwise will be None.
# Shape: [batch, channels, latent_height, latent_width]
positive_noise_pred: Optional[torch.Tensor] = None
# Combined noise prediction from passed conditionings.
- # Available in `apply_cfg` and `post_apply_cfg` callbacks, otherwise will be None.
+ # Available in `POST_COMBINE_NOISE_PREDS` callback, otherwise will be None.
# Shape: [batch, channels, latent_height, latent_width]
noise_pred: Optional[torch.Tensor] = None
diff --git a/invokeai/backend/stable_diffusion/diffusion_backend.py b/invokeai/backend/stable_diffusion/diffusion_backend.py
index 806deb5e03..4191db734f 100644
--- a/invokeai/backend/stable_diffusion/diffusion_backend.py
+++ b/invokeai/backend/stable_diffusion/diffusion_backend.py
@@ -76,12 +76,12 @@ class StableDiffusionBackend:
both_noise_pred = self.run_unet(ctx, ext_manager, ConditioningMode.Both)
ctx.negative_noise_pred, ctx.positive_noise_pred = both_noise_pred.chunk(2)
- # ext: override apply_cfg
- ctx.noise_pred = self.apply_cfg(ctx)
+ # ext: override combine_noise_preds
+ ctx.noise_pred = self.combine_noise_preds(ctx)
# ext: cfg_rescale [modify_noise_prediction]
# TODO: rename
- ext_manager.run_callback(ExtensionCallbackType.POST_APPLY_CFG, ctx)
+ ext_manager.run_callback(ExtensionCallbackType.POST_COMBINE_NOISE_PREDS, ctx)
# compute the previous noisy sample x_t -> x_t-1
step_output = ctx.scheduler.step(ctx.noise_pred, ctx.timestep, ctx.latents, **ctx.inputs.scheduler_step_kwargs)
@@ -95,13 +95,15 @@ class StableDiffusionBackend:
return step_output
@staticmethod
- def apply_cfg(ctx: DenoiseContext) -> torch.Tensor:
+ def combine_noise_preds(ctx: DenoiseContext) -> torch.Tensor:
guidance_scale = ctx.inputs.conditioning_data.guidance_scale
if isinstance(guidance_scale, list):
guidance_scale = guidance_scale[ctx.step_index]
- return torch.lerp(ctx.negative_noise_pred, ctx.positive_noise_pred, guidance_scale)
- # return ctx.negative_noise_pred + guidance_scale * (ctx.positive_noise_pred - ctx.negative_noise_pred)
+ # Note: Although this `torch.lerp(...)` line is logically equivalent to the current CFG line, it seems to result
+ # in slightly different outputs. It is suspected that this is caused by small precision differences.
+ # return torch.lerp(ctx.negative_noise_pred, ctx.positive_noise_pred, guidance_scale)
+ return ctx.negative_noise_pred + guidance_scale * (ctx.positive_noise_pred - ctx.negative_noise_pred)
def run_unet(self, ctx: DenoiseContext, ext_manager: ExtensionsManager, conditioning_mode: ConditioningMode):
sample = ctx.latent_model_input
diff --git a/invokeai/backend/stable_diffusion/extension_callback_type.py b/invokeai/backend/stable_diffusion/extension_callback_type.py
index aaefbd7ed0..e4c365007b 100644
--- a/invokeai/backend/stable_diffusion/extension_callback_type.py
+++ b/invokeai/backend/stable_diffusion/extension_callback_type.py
@@ -9,4 +9,4 @@ class ExtensionCallbackType(Enum):
POST_STEP = "post_step"
PRE_UNET = "pre_unet"
POST_UNET = "post_unet"
- POST_APPLY_CFG = "post_apply_cfg"
+ POST_COMBINE_NOISE_PREDS = "post_combine_noise_preds"
diff --git a/invokeai/backend/stable_diffusion/extensions/base.py b/invokeai/backend/stable_diffusion/extensions/base.py
index 802af86e6d..820d5d32a3 100644
--- a/invokeai/backend/stable_diffusion/extensions/base.py
+++ b/invokeai/backend/stable_diffusion/extensions/base.py
@@ -2,7 +2,7 @@ from __future__ import annotations
from contextlib import contextmanager
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Callable, Dict, List
+from typing import TYPE_CHECKING, Callable, Dict, List, Optional
import torch
from diffusers import UNet2DConditionModel
@@ -52,9 +52,9 @@ class ExtensionBase:
return self._callbacks
@contextmanager
- def patch_extension(self, context: DenoiseContext):
+ def patch_extension(self, ctx: DenoiseContext):
yield None
@contextmanager
- def patch_unet(self, state_dict: Dict[str, torch.Tensor], unet: UNet2DConditionModel):
+ def patch_unet(self, unet: UNet2DConditionModel, cached_weights: Optional[Dict[str, torch.Tensor]] = None):
yield None
diff --git a/invokeai/backend/stable_diffusion/extensions/controlnet.py b/invokeai/backend/stable_diffusion/extensions/controlnet.py
new file mode 100644
index 0000000000..a48a681af3
--- /dev/null
+++ b/invokeai/backend/stable_diffusion/extensions/controlnet.py
@@ -0,0 +1,158 @@
+from __future__ import annotations
+
+import math
+from contextlib import contextmanager
+from typing import TYPE_CHECKING, List, Optional, Union
+
+import torch
+from PIL.Image import Image
+
+from invokeai.app.invocations.constants import LATENT_SCALE_FACTOR
+from invokeai.app.util.controlnet_utils import CONTROLNET_MODE_VALUES, CONTROLNET_RESIZE_VALUES, prepare_control_image
+from invokeai.backend.stable_diffusion.denoise_context import UNetKwargs
+from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ConditioningMode
+from invokeai.backend.stable_diffusion.extension_callback_type import ExtensionCallbackType
+from invokeai.backend.stable_diffusion.extensions.base import ExtensionBase, callback
+
+if TYPE_CHECKING:
+ from invokeai.backend.stable_diffusion.denoise_context import DenoiseContext
+ from invokeai.backend.util.hotfixes import ControlNetModel
+
+
+class ControlNetExt(ExtensionBase):
+ def __init__(
+ self,
+ model: ControlNetModel,
+ image: Image,
+ weight: Union[float, List[float]],
+ begin_step_percent: float,
+ end_step_percent: float,
+ control_mode: CONTROLNET_MODE_VALUES,
+ resize_mode: CONTROLNET_RESIZE_VALUES,
+ ):
+ super().__init__()
+ self._model = model
+ self._image = image
+ self._weight = weight
+ self._begin_step_percent = begin_step_percent
+ self._end_step_percent = end_step_percent
+ self._control_mode = control_mode
+ self._resize_mode = resize_mode
+
+ self._image_tensor: Optional[torch.Tensor] = None
+
+ @contextmanager
+ def patch_extension(self, ctx: DenoiseContext):
+ original_processors = self._model.attn_processors
+ try:
+ self._model.set_attn_processor(ctx.inputs.attention_processor_cls())
+
+ yield None
+ finally:
+ self._model.set_attn_processor(original_processors)
+
+ @callback(ExtensionCallbackType.PRE_DENOISE_LOOP)
+ def resize_image(self, ctx: DenoiseContext):
+ _, _, latent_height, latent_width = ctx.latents.shape
+ image_height = latent_height * LATENT_SCALE_FACTOR
+ image_width = latent_width * LATENT_SCALE_FACTOR
+
+ self._image_tensor = prepare_control_image(
+ image=self._image,
+ do_classifier_free_guidance=False,
+ width=image_width,
+ height=image_height,
+ device=ctx.latents.device,
+ dtype=ctx.latents.dtype,
+ control_mode=self._control_mode,
+ resize_mode=self._resize_mode,
+ )
+
+ @callback(ExtensionCallbackType.PRE_UNET)
+ def pre_unet_step(self, ctx: DenoiseContext):
+ # skip if model not active in current step
+ total_steps = len(ctx.inputs.timesteps)
+ first_step = math.floor(self._begin_step_percent * total_steps)
+ last_step = math.ceil(self._end_step_percent * total_steps)
+ if ctx.step_index < first_step or ctx.step_index > last_step:
+ return
+
+ # convert mode to internal flags
+ soft_injection = self._control_mode in ["more_prompt", "more_control"]
+ cfg_injection = self._control_mode in ["more_control", "unbalanced"]
+
+ # no negative conditioning in cfg_injection mode
+ if cfg_injection:
+ if ctx.conditioning_mode == ConditioningMode.Negative:
+ return
+ down_samples, mid_sample = self._run(ctx, soft_injection, ConditioningMode.Positive)
+
+ if ctx.conditioning_mode == ConditioningMode.Both:
+ # add zeros as samples for negative conditioning
+ down_samples = [torch.cat([torch.zeros_like(d), d]) for d in down_samples]
+ mid_sample = torch.cat([torch.zeros_like(mid_sample), mid_sample])
+
+ else:
+ down_samples, mid_sample = self._run(ctx, soft_injection, ctx.conditioning_mode)
+
+ if (
+ ctx.unet_kwargs.down_block_additional_residuals is None
+ and ctx.unet_kwargs.mid_block_additional_residual is None
+ ):
+ ctx.unet_kwargs.down_block_additional_residuals = down_samples
+ ctx.unet_kwargs.mid_block_additional_residual = mid_sample
+ else:
+ # add controlnet outputs together if have multiple controlnets
+ ctx.unet_kwargs.down_block_additional_residuals = [
+ samples_prev + samples_curr
+ for samples_prev, samples_curr in zip(
+ ctx.unet_kwargs.down_block_additional_residuals, down_samples, strict=True
+ )
+ ]
+ ctx.unet_kwargs.mid_block_additional_residual += mid_sample
+
+ def _run(self, ctx: DenoiseContext, soft_injection: bool, conditioning_mode: ConditioningMode):
+ total_steps = len(ctx.inputs.timesteps)
+
+ model_input = ctx.latent_model_input
+ image_tensor = self._image_tensor
+ if conditioning_mode == ConditioningMode.Both:
+ model_input = torch.cat([model_input] * 2)
+ image_tensor = torch.cat([image_tensor] * 2)
+
+ cn_unet_kwargs = UNetKwargs(
+ sample=model_input,
+ timestep=ctx.timestep,
+ encoder_hidden_states=None, # set later by conditioning
+ cross_attention_kwargs=dict( # noqa: C408
+ percent_through=ctx.step_index / total_steps,
+ ),
+ )
+
+ ctx.inputs.conditioning_data.to_unet_kwargs(cn_unet_kwargs, conditioning_mode=conditioning_mode)
+
+ # get static weight, or weight corresponding to current step
+ weight = self._weight
+ if isinstance(weight, list):
+ weight = weight[ctx.step_index]
+
+ tmp_kwargs = vars(cn_unet_kwargs)
+
+ # Remove kwargs not related to ControlNet unet
+ # ControlNet guidance fields
+ del tmp_kwargs["down_block_additional_residuals"]
+ del tmp_kwargs["mid_block_additional_residual"]
+
+ # T2i Adapter guidance fields
+ del tmp_kwargs["down_intrablock_additional_residuals"]
+
+ # controlnet(s) inference
+ down_samples, mid_sample = self._model(
+ controlnet_cond=image_tensor,
+ conditioning_scale=weight, # controlnet specific, NOT the guidance scale
+ guess_mode=soft_injection, # this is still called guess_mode in diffusers ControlNetModel
+ return_dict=False,
+ **vars(cn_unet_kwargs),
+ )
+
+ return down_samples, mid_sample
diff --git a/invokeai/backend/stable_diffusion/extensions/freeu.py b/invokeai/backend/stable_diffusion/extensions/freeu.py
new file mode 100644
index 0000000000..6ec4fea3fa
--- /dev/null
+++ b/invokeai/backend/stable_diffusion/extensions/freeu.py
@@ -0,0 +1,35 @@
+from __future__ import annotations
+
+from contextlib import contextmanager
+from typing import TYPE_CHECKING, Dict, Optional
+
+import torch
+from diffusers import UNet2DConditionModel
+
+from invokeai.backend.stable_diffusion.extensions.base import ExtensionBase
+
+if TYPE_CHECKING:
+ from invokeai.app.shared.models import FreeUConfig
+
+
+class FreeUExt(ExtensionBase):
+ def __init__(
+ self,
+ freeu_config: FreeUConfig,
+ ):
+ super().__init__()
+ self._freeu_config = freeu_config
+
+ @contextmanager
+ def patch_unet(self, unet: UNet2DConditionModel, cached_weights: Optional[Dict[str, torch.Tensor]] = None):
+ unet.enable_freeu(
+ b1=self._freeu_config.b1,
+ b2=self._freeu_config.b2,
+ s1=self._freeu_config.s1,
+ s2=self._freeu_config.s2,
+ )
+
+ try:
+ yield
+ finally:
+ unet.disable_freeu()
diff --git a/invokeai/backend/stable_diffusion/extensions/rescale_cfg.py b/invokeai/backend/stable_diffusion/extensions/rescale_cfg.py
new file mode 100644
index 0000000000..7cccbb8a2b
--- /dev/null
+++ b/invokeai/backend/stable_diffusion/extensions/rescale_cfg.py
@@ -0,0 +1,36 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import torch
+
+from invokeai.backend.stable_diffusion.extension_callback_type import ExtensionCallbackType
+from invokeai.backend.stable_diffusion.extensions.base import ExtensionBase, callback
+
+if TYPE_CHECKING:
+ from invokeai.backend.stable_diffusion.denoise_context import DenoiseContext
+
+
+class RescaleCFGExt(ExtensionBase):
+ def __init__(self, rescale_multiplier: float):
+ super().__init__()
+ self._rescale_multiplier = rescale_multiplier
+
+ @staticmethod
+ def _rescale_cfg(total_noise_pred: torch.Tensor, pos_noise_pred: torch.Tensor, multiplier: float = 0.7):
+ """Implementation of Algorithm 2 from https://arxiv.org/pdf/2305.08891.pdf."""
+ ro_pos = torch.std(pos_noise_pred, dim=(1, 2, 3), keepdim=True)
+ ro_cfg = torch.std(total_noise_pred, dim=(1, 2, 3), keepdim=True)
+
+ x_rescaled = total_noise_pred * (ro_pos / ro_cfg)
+ x_final = multiplier * x_rescaled + (1.0 - multiplier) * total_noise_pred
+ return x_final
+
+ @callback(ExtensionCallbackType.POST_COMBINE_NOISE_PREDS)
+ def rescale_noise_pred(self, ctx: DenoiseContext):
+ if self._rescale_multiplier > 0:
+ ctx.noise_pred = self._rescale_cfg(
+ ctx.noise_pred,
+ ctx.positive_noise_pred,
+ self._rescale_multiplier,
+ )
diff --git a/invokeai/backend/stable_diffusion/extensions/seamless.py b/invokeai/backend/stable_diffusion/extensions/seamless.py
new file mode 100644
index 0000000000..a96ea6e4d2
--- /dev/null
+++ b/invokeai/backend/stable_diffusion/extensions/seamless.py
@@ -0,0 +1,71 @@
+from __future__ import annotations
+
+from contextlib import contextmanager
+from typing import Callable, Dict, List, Optional, Tuple
+
+import torch
+import torch.nn as nn
+from diffusers import UNet2DConditionModel
+from diffusers.models.lora import LoRACompatibleConv
+
+from invokeai.backend.stable_diffusion.extensions.base import ExtensionBase
+
+
+class SeamlessExt(ExtensionBase):
+ def __init__(
+ self,
+ seamless_axes: List[str],
+ ):
+ super().__init__()
+ self._seamless_axes = seamless_axes
+
+ @contextmanager
+ def patch_unet(self, unet: UNet2DConditionModel, cached_weights: Optional[Dict[str, torch.Tensor]] = None):
+ with self.static_patch_model(
+ model=unet,
+ seamless_axes=self._seamless_axes,
+ ):
+ yield
+
+ @staticmethod
+ @contextmanager
+ def static_patch_model(
+ model: torch.nn.Module,
+ seamless_axes: List[str],
+ ):
+ if not seamless_axes:
+ yield
+ return
+
+ x_mode = "circular" if "x" in seamless_axes else "constant"
+ y_mode = "circular" if "y" in seamless_axes else "constant"
+
+ # override conv_forward
+ # https://github.com/huggingface/diffusers/issues/556#issuecomment-1993287019
+ def _conv_forward_asymmetric(
+ self, input: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor] = None
+ ):
+ self.paddingX = (self._reversed_padding_repeated_twice[0], self._reversed_padding_repeated_twice[1], 0, 0)
+ self.paddingY = (0, 0, self._reversed_padding_repeated_twice[2], self._reversed_padding_repeated_twice[3])
+ working = torch.nn.functional.pad(input, self.paddingX, mode=x_mode)
+ working = torch.nn.functional.pad(working, self.paddingY, mode=y_mode)
+ return torch.nn.functional.conv2d(
+ working, weight, bias, self.stride, torch.nn.modules.utils._pair(0), self.dilation, self.groups
+ )
+
+ original_layers: List[Tuple[nn.Conv2d, Callable]] = []
+ try:
+ for layer in model.modules():
+ if not isinstance(layer, torch.nn.Conv2d):
+ continue
+
+ if isinstance(layer, LoRACompatibleConv) and layer.lora_layer is None:
+ layer.lora_layer = lambda *x: 0
+ original_layers.append((layer, layer._conv_forward))
+ layer._conv_forward = _conv_forward_asymmetric.__get__(layer, torch.nn.Conv2d)
+
+ yield
+
+ finally:
+ for layer, orig_conv_forward in original_layers:
+ layer._conv_forward = orig_conv_forward
diff --git a/invokeai/backend/stable_diffusion/extensions/t2i_adapter.py b/invokeai/backend/stable_diffusion/extensions/t2i_adapter.py
new file mode 100644
index 0000000000..5c290ea4e7
--- /dev/null
+++ b/invokeai/backend/stable_diffusion/extensions/t2i_adapter.py
@@ -0,0 +1,120 @@
+from __future__ import annotations
+
+import math
+from typing import TYPE_CHECKING, List, Optional, Union
+
+import torch
+from diffusers import T2IAdapter
+from PIL.Image import Image
+
+from invokeai.app.util.controlnet_utils import prepare_control_image
+from invokeai.backend.model_manager import BaseModelType
+from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ConditioningMode
+from invokeai.backend.stable_diffusion.extension_callback_type import ExtensionCallbackType
+from invokeai.backend.stable_diffusion.extensions.base import ExtensionBase, callback
+
+if TYPE_CHECKING:
+ from invokeai.app.invocations.model import ModelIdentifierField
+ from invokeai.app.services.shared.invocation_context import InvocationContext
+ from invokeai.app.util.controlnet_utils import CONTROLNET_RESIZE_VALUES
+ from invokeai.backend.stable_diffusion.denoise_context import DenoiseContext
+
+
+class T2IAdapterExt(ExtensionBase):
+ def __init__(
+ self,
+ node_context: InvocationContext,
+ model_id: ModelIdentifierField,
+ image: Image,
+ weight: Union[float, List[float]],
+ begin_step_percent: float,
+ end_step_percent: float,
+ resize_mode: CONTROLNET_RESIZE_VALUES,
+ ):
+ super().__init__()
+ self._node_context = node_context
+ self._model_id = model_id
+ self._image = image
+ self._weight = weight
+ self._resize_mode = resize_mode
+ self._begin_step_percent = begin_step_percent
+ self._end_step_percent = end_step_percent
+
+ self._adapter_state: Optional[List[torch.Tensor]] = None
+
+ # The max_unet_downscale is the maximum amount that the UNet model downscales the latent image internally.
+ model_config = self._node_context.models.get_config(self._model_id.key)
+ if model_config.base == BaseModelType.StableDiffusion1:
+ self._max_unet_downscale = 8
+ elif model_config.base == BaseModelType.StableDiffusionXL:
+ self._max_unet_downscale = 4
+ else:
+ raise ValueError(f"Unexpected T2I-Adapter base model type: '{model_config.base}'.")
+
+ @callback(ExtensionCallbackType.SETUP)
+ def setup(self, ctx: DenoiseContext):
+ t2i_model: T2IAdapter
+ with self._node_context.models.load(self._model_id) as t2i_model:
+ _, _, latents_height, latents_width = ctx.inputs.orig_latents.shape
+
+ self._adapter_state = self._run_model(
+ model=t2i_model,
+ image=self._image,
+ latents_height=latents_height,
+ latents_width=latents_width,
+ )
+
+ def _run_model(
+ self,
+ model: T2IAdapter,
+ image: Image,
+ latents_height: int,
+ latents_width: int,
+ ):
+ # Resize the T2I-Adapter input image.
+ # We select the resize dimensions so that after the T2I-Adapter's total_downscale_factor is applied, the
+ # result will match the latent image's dimensions after max_unet_downscale is applied.
+ input_height = latents_height // self._max_unet_downscale * model.total_downscale_factor
+ input_width = latents_width // self._max_unet_downscale * model.total_downscale_factor
+
+ # Note: We have hard-coded `do_classifier_free_guidance=False`. This is because we only want to prepare
+ # a single image. If CFG is enabled, we will duplicate the resultant tensor after applying the
+ # T2I-Adapter model.
+ #
+ # Note: We re-use the `prepare_control_image(...)` from ControlNet for T2I-Adapter, because it has many
+ # of the same requirements (e.g. preserving binary masks during resize).
+ t2i_image = prepare_control_image(
+ image=image,
+ do_classifier_free_guidance=False,
+ width=input_width,
+ height=input_height,
+ num_channels=model.config["in_channels"],
+ device=model.device,
+ dtype=model.dtype,
+ resize_mode=self._resize_mode,
+ )
+
+ return model(t2i_image)
+
+ @callback(ExtensionCallbackType.PRE_UNET)
+ def pre_unet_step(self, ctx: DenoiseContext):
+ # skip if model not active in current step
+ total_steps = len(ctx.inputs.timesteps)
+ first_step = math.floor(self._begin_step_percent * total_steps)
+ last_step = math.ceil(self._end_step_percent * total_steps)
+ if ctx.step_index < first_step or ctx.step_index > last_step:
+ return
+
+ weight = self._weight
+ if isinstance(weight, list):
+ weight = weight[ctx.step_index]
+
+ adapter_state = self._adapter_state
+ if ctx.conditioning_mode == ConditioningMode.Both:
+ adapter_state = [torch.cat([v] * 2) for v in adapter_state]
+
+ if ctx.unet_kwargs.down_intrablock_additional_residuals is None:
+ ctx.unet_kwargs.down_intrablock_additional_residuals = [v * weight for v in adapter_state]
+ else:
+ for i, value in enumerate(adapter_state):
+ ctx.unet_kwargs.down_intrablock_additional_residuals[i] += value * weight
diff --git a/invokeai/backend/stable_diffusion/extensions_manager.py b/invokeai/backend/stable_diffusion/extensions_manager.py
index 1cae2e4219..c8d585406a 100644
--- a/invokeai/backend/stable_diffusion/extensions_manager.py
+++ b/invokeai/backend/stable_diffusion/extensions_manager.py
@@ -52,20 +52,24 @@ class ExtensionsManager:
cb.function(ctx)
@contextmanager
- def patch_extensions(self, context: DenoiseContext):
+ def patch_extensions(self, ctx: DenoiseContext):
if self._is_canceled and self._is_canceled():
raise CanceledException
with ExitStack() as exit_stack:
for ext in self._extensions:
- exit_stack.enter_context(ext.patch_extension(context))
+ exit_stack.enter_context(ext.patch_extension(ctx))
yield None
@contextmanager
- def patch_unet(self, state_dict: Dict[str, torch.Tensor], unet: UNet2DConditionModel):
+ def patch_unet(self, unet: UNet2DConditionModel, cached_weights: Optional[Dict[str, torch.Tensor]] = None):
if self._is_canceled and self._is_canceled():
raise CanceledException
- # TODO: create logic in PR with extension which uses it
- yield None
+ # TODO: create weight patch logic in PR with extension which uses it
+ with ExitStack() as exit_stack:
+ for ext in self._extensions:
+ exit_stack.enter_context(ext.patch_unet(unet, cached_weights))
+
+ yield None
diff --git a/invokeai/backend/stable_diffusion/seamless.py b/invokeai/backend/stable_diffusion/seamless.py
deleted file mode 100644
index 23ed978c6d..0000000000
--- a/invokeai/backend/stable_diffusion/seamless.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from contextlib import contextmanager
-from typing import Callable, List, Optional, Tuple, Union
-
-import torch
-import torch.nn as nn
-from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL
-from diffusers.models.autoencoders.autoencoder_tiny import AutoencoderTiny
-from diffusers.models.lora import LoRACompatibleConv
-from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel
-
-
-@contextmanager
-def set_seamless(model: Union[UNet2DConditionModel, AutoencoderKL, AutoencoderTiny], seamless_axes: List[str]):
- if not seamless_axes:
- yield
- return
-
- # override conv_forward
- # https://github.com/huggingface/diffusers/issues/556#issuecomment-1993287019
- def _conv_forward_asymmetric(self, input: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor] = None):
- self.paddingX = (self._reversed_padding_repeated_twice[0], self._reversed_padding_repeated_twice[1], 0, 0)
- self.paddingY = (0, 0, self._reversed_padding_repeated_twice[2], self._reversed_padding_repeated_twice[3])
- working = torch.nn.functional.pad(input, self.paddingX, mode=x_mode)
- working = torch.nn.functional.pad(working, self.paddingY, mode=y_mode)
- return torch.nn.functional.conv2d(
- working, weight, bias, self.stride, torch.nn.modules.utils._pair(0), self.dilation, self.groups
- )
-
- original_layers: List[Tuple[nn.Conv2d, Callable]] = []
-
- try:
- x_mode = "circular" if "x" in seamless_axes else "constant"
- y_mode = "circular" if "y" in seamless_axes else "constant"
-
- conv_layers: List[torch.nn.Conv2d] = []
-
- for module in model.modules():
- if isinstance(module, torch.nn.Conv2d):
- conv_layers.append(module)
-
- for layer in conv_layers:
- if isinstance(layer, LoRACompatibleConv) and layer.lora_layer is None:
- layer.lora_layer = lambda *x: 0
- original_layers.append((layer, layer._conv_forward))
- layer._conv_forward = _conv_forward_asymmetric.__get__(layer, torch.nn.Conv2d)
-
- yield
-
- finally:
- for layer, orig_conv_forward in original_layers:
- layer._conv_forward = orig_conv_forward
diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json
index f2210e4c68..879985accd 100644
--- a/invokeai/frontend/web/package.json
+++ b/invokeai/frontend/web/package.json
@@ -155,5 +155,8 @@
"vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0"
+ },
+ "engines": {
+ "pnpm": "8"
}
}
diff --git a/invokeai/frontend/web/public/locales/ar.json b/invokeai/frontend/web/public/locales/ar.json
index ee370d1e42..bb04edfa8b 100644
--- a/invokeai/frontend/web/public/locales/ar.json
+++ b/invokeai/frontend/web/public/locales/ar.json
@@ -77,10 +77,6 @@
"title": "استعادة الوجوه",
"desc": "استعادة الصورة الحالية"
},
- "upscale": {
- "title": "تحسين الحجم",
- "desc": "تحسين حجم الصورة الحالية"
- },
"showInfo": {
"title": "عرض المعلومات",
"desc": "عرض معلومات البيانات الخاصة بالصورة الحالية"
@@ -255,8 +251,6 @@
"type": "نوع",
"strength": "قوة",
"upscaling": "تصغير",
- "upscale": "تصغير",
- "upscaleImage": "تصغير الصورة",
"scale": "مقياس",
"imageFit": "ملائمة الصورة الأولية لحجم الخرج",
"scaleBeforeProcessing": "تحجيم قبل المعالجة",
diff --git a/invokeai/frontend/web/public/locales/de.json b/invokeai/frontend/web/public/locales/de.json
index 352b6ffcbf..b8a204df1d 100644
--- a/invokeai/frontend/web/public/locales/de.json
+++ b/invokeai/frontend/web/public/locales/de.json
@@ -187,10 +187,6 @@
"title": "Gesicht restaurieren",
"desc": "Das aktuelle Bild restaurieren"
},
- "upscale": {
- "title": "Hochskalieren",
- "desc": "Das aktuelle Bild hochskalieren"
- },
"showInfo": {
"title": "Info anzeigen",
"desc": "Metadaten des aktuellen Bildes anzeigen"
@@ -433,8 +429,6 @@
"type": "Art",
"strength": "Stärke",
"upscaling": "Hochskalierung",
- "upscale": "Hochskalieren (Shift + U)",
- "upscaleImage": "Bild hochskalieren",
"scale": "Maßstab",
"imageFit": "Ausgangsbild an Ausgabegröße anpassen",
"scaleBeforeProcessing": "Skalieren vor der Verarbeitung",
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 91a36bcee3..659df78d9b 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -32,12 +32,14 @@
"deleteBoardAndImages": "Delete Board and Images",
"deleteBoardOnly": "Delete Board Only",
"deletedBoardsCannotbeRestored": "Deleted boards cannot be restored",
+ "hideBoards": "Hide Boards",
"loading": "Loading...",
"menuItemAutoAdd": "Auto-add to this Board",
"move": "Move",
"movingImagesToBoard_one": "Moving {{count}} image to board:",
"movingImagesToBoard_other": "Moving {{count}} images to board:",
"myBoard": "My Board",
+ "noBoards": "No {{boardType}} Boards",
"noMatching": "No matching Boards",
"private": "Private Boards",
"searchBoard": "Search Boards...",
@@ -46,6 +48,7 @@
"topMessage": "This board contains images used in the following features:",
"unarchiveBoard": "Unarchive Board",
"uncategorized": "Uncategorized",
+ "viewBoards": "View Boards",
"downloadBoard": "Download Board",
"imagesWithCount_one": "{{count}} image",
"imagesWithCount_other": "{{count}} images",
@@ -102,6 +105,7 @@
"negativePrompt": "Negative Prompt",
"discordLabel": "Discord",
"dontAskMeAgain": "Don't ask me again",
+ "dontShowMeThese": "Don't show me these",
"editor": "Editor",
"error": "Error",
"file": "File",
@@ -373,10 +377,14 @@
"displayBoardSearch": "Display Board Search",
"displaySearch": "Display Search",
"download": "Download",
+ "exitBoardSearch": "Exit Board Search",
+ "exitSearch": "Exit Search",
"featuresWillReset": "If you delete this image, those features will immediately be reset.",
"galleryImageSize": "Image Size",
"gallerySettings": "Gallery Settings",
+ "go": "Go",
"image": "image",
+ "jump": "Jump",
"loading": "Loading",
"loadMore": "Load More",
"newestFirst": "Newest First",
@@ -636,9 +644,9 @@
"title": "Undo Stroke"
},
"unifiedCanvasHotkeys": "Unified Canvas",
- "upscale": {
- "desc": "Upscale the current image",
- "title": "Upscale"
+ "postProcess": {
+ "desc": "Process the current image using the selected post-processing model",
+ "title": "Process Image"
},
"toggleViewer": {
"desc": "Switches between the Image Viewer and workspace for the current tab.",
@@ -1027,6 +1035,7 @@
"imageActions": "Image Actions",
"sendToImg2Img": "Send to Image to Image",
"sendToUnifiedCanvas": "Send To Unified Canvas",
+ "sendToUpscale": "Send To Upscale",
"showOptionsPanel": "Show Side Panel (O or T)",
"shuffle": "Shuffle Seed",
"steps": "Steps",
@@ -1034,8 +1043,8 @@
"symmetry": "Symmetry",
"tileSize": "Tile Size",
"type": "Type",
- "upscale": "Upscale (Shift + U)",
- "upscaleImage": "Upscale Image",
+ "postProcessing": "Post-Processing (Shift + U)",
+ "processImage": "Process Image",
"upscaling": "Upscaling",
"useAll": "Use All",
"useSize": "Use Size",
@@ -1091,6 +1100,8 @@
"displayInProgress": "Display Progress Images",
"enableImageDebugging": "Enable Image Debugging",
"enableInformationalPopovers": "Enable Informational Popovers",
+ "informationalPopoversDisabled": "Informational Popovers Disabled",
+ "informationalPopoversDisabledDesc": "Informational popovers have been disabled. Enable them in Settings.",
"enableInvisibleWatermark": "Enable Invisible Watermark",
"enableNSFWChecker": "Enable NSFW Checker",
"general": "General",
@@ -1498,6 +1509,30 @@
"seamlessTilingYAxis": {
"heading": "Seamless Tiling Y Axis",
"paragraphs": ["Seamlessly tile an image along the vertical axis."]
+ },
+ "upscaleModel": {
+ "heading": "Upscale Model",
+ "paragraphs": [
+ "The upscale model scales the image to the output size before details are added. Any supported upscale model may be used, but some are specialized for different kinds of images, like photos or line drawings."
+ ]
+ },
+ "scale": {
+ "heading": "Scale",
+ "paragraphs": [
+ "Scale controls the output image size, and is based on a multiple of the input image resolution. For example a 2x upscale on a 1024x1024 image would produce a 2048 x 2048 output."
+ ]
+ },
+ "creativity": {
+ "heading": "Creativity",
+ "paragraphs": [
+ "Creativity controls the amount of freedom granted to the model when adding details. Low creativity stays close to the original image, while high creativity allows for more change. When using a prompt, high creativity increases the influence of the prompt."
+ ]
+ },
+ "structure": {
+ "heading": "Structure",
+ "paragraphs": [
+ "Structure controls how closely the output image will keep to the layout of the original. Low structure allows major changes, while high structure strictly maintains the original composition and layout."
+ ]
}
},
"unifiedCanvas": {
@@ -1640,6 +1675,27 @@
"layers_one": "Layer",
"layers_other": "Layers"
},
+ "upscaling": {
+ "creativity": "Creativity",
+ "structure": "Structure",
+ "upscaleModel": "Upscale Model",
+ "postProcessingModel": "Post-Processing Model",
+ "scale": "Scale",
+ "postProcessingMissingModelWarning": "Visit the Model Manager to install a post-processing (image to image) model.",
+ "missingModelsWarning": "Visit the Model Manager to install the required models:",
+ "mainModelDesc": "Main model (SD1.5 or SDXL architecture)",
+ "tileControlNetModelDesc": "Tile ControlNet model for the chosen main model architecture",
+ "upscaleModelDesc": "Upscale (image to image) model",
+ "missingUpscaleInitialImage": "Missing initial image for upscaling",
+ "missingUpscaleModel": "Missing upscale model",
+ "missingTileControlNetModel": "No valid tile ControlNet models installed"
+ },
+ "upsell": {
+ "inviteTeammates": "Invite Teammates",
+ "professional": "Professional",
+ "professionalUpsell": "Available in Invoke’s Professional Edition. Click here or visit invoke.com/pricing for more details.",
+ "shareAccess": "Share Access"
+ },
"ui": {
"tabs": {
"generation": "Generation",
@@ -1651,7 +1707,9 @@
"models": "Models",
"modelsTab": "$t(ui.tabs.models) $t(common.tab)",
"queue": "Queue",
- "queueTab": "$t(ui.tabs.queue) $t(common.tab)"
+ "queueTab": "$t(ui.tabs.queue) $t(common.tab)",
+ "upscaling": "Upscaling",
+ "upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)"
}
}
}
diff --git a/invokeai/frontend/web/public/locales/es.json b/invokeai/frontend/web/public/locales/es.json
index 52ee3b5fe3..fa87dd0c76 100644
--- a/invokeai/frontend/web/public/locales/es.json
+++ b/invokeai/frontend/web/public/locales/es.json
@@ -151,10 +151,6 @@
"title": "Restaurar rostros",
"desc": "Restaurar rostros en la imagen actual"
},
- "upscale": {
- "title": "Aumentar resolución",
- "desc": "Aumentar la resolución de la imagen actual"
- },
"showInfo": {
"title": "Mostrar información",
"desc": "Mostar metadatos de la imagen actual"
@@ -360,8 +356,6 @@
"type": "Tipo",
"strength": "Fuerza",
"upscaling": "Aumento de resolución",
- "upscale": "Aumentar resolución",
- "upscaleImage": "Aumentar la resolución de la imagen",
"scale": "Escala",
"imageFit": "Ajuste tamaño de imagen inicial al tamaño objetivo",
"scaleBeforeProcessing": "Redimensionar antes de procesar",
@@ -408,7 +402,12 @@
"showProgressInViewer": "Mostrar las imágenes del progreso en el visor",
"ui": "Interfaz del usuario",
"generation": "Generación",
- "beta": "Beta"
+ "beta": "Beta",
+ "reloadingIn": "Recargando en",
+ "intermediatesClearedFailed": "Error limpiando los intermediarios",
+ "intermediatesCleared_one": "Borrado {{count}} intermediario",
+ "intermediatesCleared_many": "Borrados {{count}} intermediarios",
+ "intermediatesCleared_other": "Borrados {{count}} intermediarios"
},
"toast": {
"uploadFailed": "Error al subir archivo",
@@ -426,7 +425,12 @@
"parameterSet": "Conjunto de parámetros",
"parameterNotSet": "Parámetro no configurado",
"problemCopyingImage": "No se puede copiar la imagen",
- "errorCopied": "Error al copiar"
+ "errorCopied": "Error al copiar",
+ "baseModelChanged": "Modelo base cambiado",
+ "addedToBoard": "Añadido al tablero",
+ "baseModelChangedCleared_one": "Borrado o desactivado {{count}} submodelo incompatible",
+ "baseModelChangedCleared_many": "Borrados o desactivados {{count}} submodelos incompatibles",
+ "baseModelChangedCleared_other": "Borrados o desactivados {{count}} submodelos incompatibles"
},
"tooltip": {
"feature": {
@@ -540,7 +544,13 @@
"downloadBoard": "Descargar panel",
"deleteBoardOnly": "Borrar solo el panel",
"myBoard": "Mi panel",
- "noMatching": "No hay paneles que coincidan"
+ "noMatching": "No hay paneles que coincidan",
+ "imagesWithCount_one": "{{count}} imagen",
+ "imagesWithCount_many": "{{count}} imágenes",
+ "imagesWithCount_other": "{{count}} imágenes",
+ "assetsWithCount_one": "{{count}} activo",
+ "assetsWithCount_many": "{{count}} activos",
+ "assetsWithCount_other": "{{count}} activos"
},
"accordions": {
"compositing": {
@@ -590,6 +600,27 @@
"balanced": "Equilibrado",
"beginEndStepPercent": "Inicio / Final Porcentaje de pasos",
"detectResolution": "Detectar resolución",
- "beginEndStepPercentShort": "Inicio / Final %"
+ "beginEndStepPercentShort": "Inicio / Final %",
+ "t2i_adapter": "$t(controlnet.controlAdapter_one) #{{number}} ($t(common.t2iAdapter))",
+ "controlnet": "$t(controlnet.controlAdapter_one) #{{number}} ($t(common.controlNet))",
+ "ip_adapter": "$t(controlnet.controlAdapter_one) #{{number}} ($t(common.ipAdapter))",
+ "addControlNet": "Añadir $t(common.controlNet)",
+ "addIPAdapter": "Añadir $t(common.ipAdapter)",
+ "controlAdapter_one": "Adaptador de control",
+ "controlAdapter_many": "Adaptadores de control",
+ "controlAdapter_other": "Adaptadores de control",
+ "addT2IAdapter": "Añadir $t(common.t2iAdapter)"
+ },
+ "queue": {
+ "back": "Atrás",
+ "front": "Delante",
+ "batchQueuedDesc_one": "Se agregó {{count}} sesión a {{direction}} la cola",
+ "batchQueuedDesc_many": "Se agregaron {{count}} sesiones a {{direction}} la cola",
+ "batchQueuedDesc_other": "Se agregaron {{count}} sesiones a {{direction}} la cola"
+ },
+ "upsell": {
+ "inviteTeammates": "Invitar compañeros de equipo",
+ "shareAccess": "Compartir acceso",
+ "professionalUpsell": "Disponible en la edición profesional de Invoke. Haz clic aquí o visita invoke.com/pricing para obtener más detalles."
}
}
diff --git a/invokeai/frontend/web/public/locales/fr.json b/invokeai/frontend/web/public/locales/fr.json
index 558545b2f4..efc181c59a 100644
--- a/invokeai/frontend/web/public/locales/fr.json
+++ b/invokeai/frontend/web/public/locales/fr.json
@@ -130,10 +130,6 @@
"title": "Restaurer les visages",
"desc": "Restaurer l'image actuelle"
},
- "upscale": {
- "title": "Agrandir",
- "desc": "Agrandir l'image actuelle"
- },
"showInfo": {
"title": "Afficher les informations",
"desc": "Afficher les informations de métadonnées de l'image actuelle"
@@ -308,8 +304,6 @@
"type": "Type",
"strength": "Force",
"upscaling": "Agrandissement",
- "upscale": "Agrandir",
- "upscaleImage": "Image en Agrandissement",
"scale": "Echelle",
"imageFit": "Ajuster Image Initiale à la Taille de Sortie",
"scaleBeforeProcessing": "Echelle Avant Traitement",
diff --git a/invokeai/frontend/web/public/locales/he.json b/invokeai/frontend/web/public/locales/he.json
index dbbb3cbec4..def170fdbc 100644
--- a/invokeai/frontend/web/public/locales/he.json
+++ b/invokeai/frontend/web/public/locales/he.json
@@ -90,10 +90,6 @@
"desc": "שחזור התמונה הנוכחית",
"title": "שחזור פרצופים"
},
- "upscale": {
- "title": "הגדלת קנה מידה",
- "desc": "הגדל את התמונה הנוכחית"
- },
"showInfo": {
"title": "הצג מידע",
"desc": "הצגת פרטי מטא-נתונים של התמונה הנוכחית"
@@ -263,8 +259,6 @@
"seed": "זרע",
"type": "סוג",
"strength": "חוזק",
- "upscale": "הגדלת קנה מידה",
- "upscaleImage": "הגדלת קנה מידת התמונה",
"denoisingStrength": "חוזק מנטרל הרעש",
"scaleBeforeProcessing": "שנה קנה מידה לפני עיבוד",
"scaledWidth": "קנה מידה לאחר שינוי W",
diff --git a/invokeai/frontend/web/public/locales/it.json b/invokeai/frontend/web/public/locales/it.json
index eced64a1e3..7637a41310 100644
--- a/invokeai/frontend/web/public/locales/it.json
+++ b/invokeai/frontend/web/public/locales/it.json
@@ -150,7 +150,11 @@
"showArchivedBoards": "Mostra le bacheche archiviate",
"searchImages": "Ricerca per metadati",
"displayBoardSearch": "Mostra la ricerca nelle Bacheche",
- "displaySearch": "Mostra la ricerca"
+ "displaySearch": "Mostra la ricerca",
+ "selectAllOnPage": "Seleziona tutto nella pagina",
+ "selectAllOnBoard": "Seleziona tutto nella bacheca",
+ "exitBoardSearch": "Esci da Ricerca bacheca",
+ "exitSearch": "Esci dalla ricerca"
},
"hotkeys": {
"keyboardShortcuts": "Tasti di scelta rapida",
@@ -210,10 +214,6 @@
"title": "Restaura volti",
"desc": "Restaura l'immagine corrente"
},
- "upscale": {
- "title": "Amplia",
- "desc": "Amplia l'immagine corrente"
- },
"showInfo": {
"title": "Mostra informazioni",
"desc": "Mostra le informazioni sui metadati dell'immagine corrente"
@@ -377,6 +377,10 @@
"toggleViewer": {
"title": "Attiva/disattiva il visualizzatore di immagini",
"desc": "Passa dal visualizzatore immagini all'area di lavoro per la scheda corrente."
+ },
+ "postProcess": {
+ "desc": "Elabora l'immagine corrente utilizzando il modello di post-elaborazione selezionato",
+ "title": "Elabora immagine"
}
},
"modelManager": {
@@ -505,8 +509,6 @@
"type": "Tipo",
"strength": "Forza",
"upscaling": "Ampliamento",
- "upscale": "Amplia (Shift + U)",
- "upscaleImage": "Amplia Immagine",
"scale": "Scala",
"imageFit": "Adatta l'immagine iniziale alle dimensioni di output",
"scaleBeforeProcessing": "Scala prima dell'elaborazione",
@@ -591,7 +593,10 @@
"infillColorValue": "Colore di riempimento",
"globalSettings": "Impostazioni globali",
"globalPositivePromptPlaceholder": "Prompt positivo globale",
- "globalNegativePromptPlaceholder": "Prompt negativo globale"
+ "globalNegativePromptPlaceholder": "Prompt negativo globale",
+ "processImage": "Elabora Immagine",
+ "sendToUpscale": "Invia a Ampliare",
+ "postProcessing": "Post-elaborazione (Shift + U)"
},
"settings": {
"models": "Modelli",
@@ -964,7 +969,10 @@
"boards": "Bacheche",
"private": "Bacheche private",
"shared": "Bacheche condivise",
- "addPrivateBoard": "Aggiungi una Bacheca Privata"
+ "addPrivateBoard": "Aggiungi una Bacheca Privata",
+ "noBoards": "Nessuna bacheca {{boardType}}",
+ "hideBoards": "Nascondi bacheche",
+ "viewBoards": "Visualizza bacheche"
},
"controlnet": {
"contentShuffleDescription": "Rimescola il contenuto di un'immagine",
@@ -1684,7 +1692,30 @@
"models": "Modelli",
"modelsTab": "$t(ui.tabs.models) $t(common.tab)",
"queue": "Coda",
- "queueTab": "$t(ui.tabs.queue) $t(common.tab)"
+ "queueTab": "$t(ui.tabs.queue) $t(common.tab)",
+ "upscaling": "Ampliamento",
+ "upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)"
}
+ },
+ "upscaling": {
+ "creativity": "Creatività",
+ "structure": "Struttura",
+ "upscaleModel": "Modello di Ampliamento",
+ "scale": "Scala",
+ "missingModelsWarning": "Visita Gestione modelli per installare i modelli richiesti:",
+ "mainModelDesc": "Modello principale (architettura SD1.5 o SDXL)",
+ "tileControlNetModelDesc": "Modello Tile ControlNet per l'architettura del modello principale scelto",
+ "upscaleModelDesc": "Modello per l'ampliamento (da immagine a immagine)",
+ "missingUpscaleInitialImage": "Immagine iniziale mancante per l'ampliamento",
+ "missingUpscaleModel": "Modello per l’ampliamento mancante",
+ "missingTileControlNetModel": "Nessun modello ControlNet Tile valido installato",
+ "postProcessingModel": "Modello di post-elaborazione",
+ "postProcessingMissingModelWarning": "Visita Gestione modelli per installare un modello di post-elaborazione (da immagine a immagine)."
+ },
+ "upsell": {
+ "inviteTeammates": "Invita collaboratori",
+ "shareAccess": "Condividi l'accesso",
+ "professional": "Professionale",
+ "professionalUpsell": "Disponibile nell'edizione Professional di Invoke. Fai clic qui o visita invoke.com/pricing per ulteriori dettagli."
}
}
diff --git a/invokeai/frontend/web/public/locales/ja.json b/invokeai/frontend/web/public/locales/ja.json
index 6d5775ecd6..e7bc11d34e 100644
--- a/invokeai/frontend/web/public/locales/ja.json
+++ b/invokeai/frontend/web/public/locales/ja.json
@@ -199,10 +199,6 @@
"title": "顔の修復",
"desc": "現在の画像を修復"
},
- "upscale": {
- "title": "アップスケール",
- "desc": "現在の画像をアップスケール"
- },
"showInfo": {
"title": "情報を見る",
"desc": "現在の画像のメタデータ情報を表示"
@@ -427,8 +423,6 @@
"shuffle": "シャッフル",
"strength": "強度",
"upscaling": "アップスケーリング",
- "upscale": "アップスケール",
- "upscaleImage": "画像をアップスケール",
"scale": "Scale",
"scaleBeforeProcessing": "処理前のスケール",
"scaledWidth": "幅のスケール",
diff --git a/invokeai/frontend/web/public/locales/ko.json b/invokeai/frontend/web/public/locales/ko.json
index db9cd0ca67..ed745da8aa 100644
--- a/invokeai/frontend/web/public/locales/ko.json
+++ b/invokeai/frontend/web/public/locales/ko.json
@@ -258,10 +258,6 @@
"desc": "캔버스 브러시를 선택",
"title": "브러시 선택"
},
- "upscale": {
- "desc": "현재 이미지를 업스케일",
- "title": "업스케일"
- },
"previousImage": {
"title": "이전 이미지",
"desc": "갤러리에 이전 이미지 표시"
diff --git a/invokeai/frontend/web/public/locales/nl.json b/invokeai/frontend/web/public/locales/nl.json
index afcce62163..2c38353240 100644
--- a/invokeai/frontend/web/public/locales/nl.json
+++ b/invokeai/frontend/web/public/locales/nl.json
@@ -168,10 +168,6 @@
"title": "Herstel gezichten",
"desc": "Herstelt de huidige afbeelding"
},
- "upscale": {
- "title": "Schaal op",
- "desc": "Schaalt de huidige afbeelding op"
- },
"showInfo": {
"title": "Toon info",
"desc": "Toont de metagegevens van de huidige afbeelding"
@@ -412,8 +408,6 @@
"type": "Soort",
"strength": "Sterkte",
"upscaling": "Opschalen",
- "upscale": "Vergroot (Shift + U)",
- "upscaleImage": "Schaal afbeelding op",
"scale": "Schaal",
"imageFit": "Pas initiële afbeelding in uitvoergrootte",
"scaleBeforeProcessing": "Schalen voor verwerking",
diff --git a/invokeai/frontend/web/public/locales/pl.json b/invokeai/frontend/web/public/locales/pl.json
index b7592c3fae..0fccbded4a 100644
--- a/invokeai/frontend/web/public/locales/pl.json
+++ b/invokeai/frontend/web/public/locales/pl.json
@@ -78,10 +78,6 @@
"title": "Popraw twarze",
"desc": "Uruchamia proces poprawiania twarzy dla aktywnego obrazu"
},
- "upscale": {
- "title": "Powiększ",
- "desc": "Uruchamia proces powiększania aktywnego obrazu"
- },
"showInfo": {
"title": "Pokaż informacje",
"desc": "Pokazuje metadane zapisane w aktywnym obrazie"
@@ -232,8 +228,6 @@
"type": "Metoda",
"strength": "Siła",
"upscaling": "Powiększanie",
- "upscale": "Powiększ",
- "upscaleImage": "Powiększ obraz",
"scale": "Skala",
"imageFit": "Przeskaluj oryginalny obraz",
"scaleBeforeProcessing": "Tryb skalowania",
diff --git a/invokeai/frontend/web/public/locales/pt.json b/invokeai/frontend/web/public/locales/pt.json
index 3003a1732b..a8a675f22f 100644
--- a/invokeai/frontend/web/public/locales/pt.json
+++ b/invokeai/frontend/web/public/locales/pt.json
@@ -160,10 +160,6 @@
"title": "Restaurar Rostos",
"desc": "Restaurar a imagem atual"
},
- "upscale": {
- "title": "Redimensionar",
- "desc": "Redimensionar a imagem atual"
- },
"showInfo": {
"title": "Mostrar Informações",
"desc": "Mostrar metadados de informações da imagem atual"
@@ -275,8 +271,6 @@
"showOptionsPanel": "Mostrar Painel de Opções",
"strength": "Força",
"upscaling": "Redimensionando",
- "upscale": "Redimensionar",
- "upscaleImage": "Redimensionar Imagem",
"scaleBeforeProcessing": "Escala Antes do Processamento",
"images": "Imagems",
"steps": "Passos",
diff --git a/invokeai/frontend/web/public/locales/pt_BR.json b/invokeai/frontend/web/public/locales/pt_BR.json
index c966c6db50..1572d4fe0b 100644
--- a/invokeai/frontend/web/public/locales/pt_BR.json
+++ b/invokeai/frontend/web/public/locales/pt_BR.json
@@ -80,10 +80,6 @@
"title": "Restaurar Rostos",
"desc": "Restaurar a imagem atual"
},
- "upscale": {
- "title": "Redimensionar",
- "desc": "Redimensionar a imagem atual"
- },
"showInfo": {
"title": "Mostrar Informações",
"desc": "Mostrar metadados de informações da imagem atual"
@@ -268,8 +264,6 @@
"type": "Tipo",
"strength": "Força",
"upscaling": "Redimensionando",
- "upscale": "Redimensionar",
- "upscaleImage": "Redimensionar Imagem",
"scale": "Escala",
"imageFit": "Caber Imagem Inicial No Tamanho de Saída",
"scaleBeforeProcessing": "Escala Antes do Processamento",
diff --git a/invokeai/frontend/web/public/locales/ru.json b/invokeai/frontend/web/public/locales/ru.json
index ecceaf42aa..644c30fd05 100644
--- a/invokeai/frontend/web/public/locales/ru.json
+++ b/invokeai/frontend/web/public/locales/ru.json
@@ -214,10 +214,6 @@
"title": "Восстановить лица",
"desc": "Восстановить лица на текущем изображении"
},
- "upscale": {
- "title": "Увеличение",
- "desc": "Увеличить текущеее изображение"
- },
"showInfo": {
"title": "Показать метаданные",
"desc": "Показать метаданные из текущего изображения"
@@ -512,8 +508,6 @@
"type": "Тип",
"strength": "Сила",
"upscaling": "Увеличение",
- "upscale": "Увеличить",
- "upscaleImage": "Увеличить изображение",
"scale": "Масштаб",
"imageFit": "Уместить изображение",
"scaleBeforeProcessing": "Масштабировать",
diff --git a/invokeai/frontend/web/public/locales/sv.json b/invokeai/frontend/web/public/locales/sv.json
index 2c51027244..a9ebd9f491 100644
--- a/invokeai/frontend/web/public/locales/sv.json
+++ b/invokeai/frontend/web/public/locales/sv.json
@@ -90,10 +90,6 @@
"title": "Återskapa ansikten",
"desc": "Återskapa nuvarande bild"
},
- "upscale": {
- "title": "Skala upp",
- "desc": "Skala upp nuvarande bild"
- },
"showInfo": {
"title": "Visa info",
"desc": "Visa metadata för nuvarande bild"
diff --git a/invokeai/frontend/web/public/locales/tr.json b/invokeai/frontend/web/public/locales/tr.json
index 415bd2d744..75efaf8294 100644
--- a/invokeai/frontend/web/public/locales/tr.json
+++ b/invokeai/frontend/web/public/locales/tr.json
@@ -416,10 +416,6 @@
"desc": "Maske/Taban katmanları arasında geçiş yapar",
"title": "Katmanı Gizle-Göster"
},
- "upscale": {
- "title": "Büyüt",
- "desc": "Seçili görseli büyüt"
- },
"setSeed": {
"title": "Tohumu Kullan",
"desc": "Seçili görselin tohumunu kullan"
@@ -641,7 +637,6 @@
"copyImage": "Görseli Kopyala",
"height": "Boy",
"width": "En",
- "upscale": "Büyüt (Shift + U)",
"useSize": "Boyutu Kullan",
"symmetry": "Bakışım",
"tileSize": "Döşeme Boyutu",
@@ -657,7 +652,6 @@
"showOptionsPanel": "Yan Paneli Göster (O ya da T)",
"shuffle": "Kar",
"usePrompt": "İstemi Kullan",
- "upscaleImage": "Görseli Büyüt",
"setToOptimalSizeTooSmall": "$t(parameters.setToOptimalSize) (çok küçük olabilir)",
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (çok büyük olabilir)",
"cfgRescaleMultiplier": "CFG Rescale Çarpanı",
diff --git a/invokeai/frontend/web/public/locales/uk.json b/invokeai/frontend/web/public/locales/uk.json
index 9bb38c21b3..0ade5c7a1f 100644
--- a/invokeai/frontend/web/public/locales/uk.json
+++ b/invokeai/frontend/web/public/locales/uk.json
@@ -85,10 +85,6 @@
"title": "Відновити обличчя",
"desc": "Відновити обличчя на поточному зображенні"
},
- "upscale": {
- "title": "Збільшення",
- "desc": "Збільшити поточне зображення"
- },
"showInfo": {
"title": "Показати метадані",
"desc": "Показати метадані з поточного зображення"
@@ -276,8 +272,6 @@
"type": "Тип",
"strength": "Сила",
"upscaling": "Збільшення",
- "upscale": "Збільшити",
- "upscaleImage": "Збільшити зображення",
"scale": "Масштаб",
"imageFit": "Вмістити зображення",
"scaleBeforeProcessing": "Масштабувати",
diff --git a/invokeai/frontend/web/public/locales/zh_CN.json b/invokeai/frontend/web/public/locales/zh_CN.json
index c4bb52e7cc..aa1c723814 100644
--- a/invokeai/frontend/web/public/locales/zh_CN.json
+++ b/invokeai/frontend/web/public/locales/zh_CN.json
@@ -193,10 +193,6 @@
"title": "面部修复",
"desc": "对当前图像进行面部修复"
},
- "upscale": {
- "title": "放大",
- "desc": "对当前图像进行放大"
- },
"showInfo": {
"title": "显示信息",
"desc": "显示当前图像的元数据"
@@ -422,8 +418,6 @@
"type": "种类",
"strength": "强度",
"upscaling": "放大",
- "upscale": "放大 (Shift + U)",
- "upscaleImage": "放大图像",
"scale": "等级",
"imageFit": "使生成图像长宽适配初始图像",
"scaleBeforeProcessing": "处理前缩放",
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 9698f85219..a1ce52b407 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
@@ -1,5 +1,6 @@
import type { TypedStartListening } from '@reduxjs/toolkit';
import { createListenerMiddleware } from '@reduxjs/toolkit';
+import { addAdHocPostProcessingRequestedListener } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
import { addCommitStagingAreaImageListener } from 'app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener';
import { addAnyEnqueuedListener } from 'app/store/middleware/listenerMiddleware/listeners/anyEnqueued';
import { addAppConfigReceivedListener } from 'app/store/middleware/listenerMiddleware/listeners/appConfigReceived';
@@ -47,11 +48,11 @@ import { addModelLoadEventListener } from 'app/store/middleware/listenerMiddlewa
import { addSocketQueueItemStatusChangedEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged';
import { addStagingAreaImageSavedListener } from 'app/store/middleware/listenerMiddleware/listeners/stagingAreaImageSaved';
import { addUpdateAllNodesRequestedListener } from 'app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested';
-import { addUpscaleRequestedListener } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested';
import { addWorkflowLoadRequestedListener } from 'app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested';
import type { AppDispatch, RootState } from 'app/store/store';
import { addArchivedOrDeletedBoardListener } from './listeners/addArchivedOrDeletedBoardListener';
+import { addEnqueueRequestedUpscale } from './listeners/enqueueRequestedUpscale';
export const listenerMiddleware = createListenerMiddleware();
@@ -85,6 +86,7 @@ addGalleryOffsetChangedListener(startAppListening);
addEnqueueRequestedCanvasListener(startAppListening);
addEnqueueRequestedNodes(startAppListening);
addEnqueueRequestedLinear(startAppListening);
+addEnqueueRequestedUpscale(startAppListening);
addAnyEnqueuedListener(startAppListening);
addBatchEnqueuedListener(startAppListening);
@@ -140,7 +142,7 @@ addModelsLoadedListener(startAppListening);
addAppConfigReceivedListener(startAppListening);
// Ad-hoc upscale workflwo
-addUpscaleRequestedListener(startAppListening);
+addAdHocPostProcessingRequestedListener(startAppListening);
// Prompts
addDynamicPromptsListener(startAppListening);
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/upscaleRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener.ts
similarity index 58%
rename from invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/upscaleRequested.ts
rename to invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener.ts
index ce480a3573..65f3198b91 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/upscaleRequested.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener.ts
@@ -2,46 +2,28 @@ import { createAction } from '@reduxjs/toolkit';
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { parseify } from 'common/util/serialize';
-import { buildAdHocUpscaleGraph } from 'features/nodes/util/graph/buildAdHocUpscaleGraph';
-import { createIsAllowedToUpscaleSelector } from 'features/parameters/hooks/useIsAllowedToUpscale';
+import { buildAdHocPostProcessingGraph } from 'features/nodes/util/graph/buildAdHocPostProcessingGraph';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { queueApi } from 'services/api/endpoints/queue';
import type { BatchConfig, ImageDTO } from 'services/api/types';
-export const upscaleRequested = createAction<{ imageDTO: ImageDTO }>(`upscale/upscaleRequested`);
+export const adHocPostProcessingRequested = createAction<{ imageDTO: ImageDTO }>(`upscaling/postProcessingRequested`);
-export const addUpscaleRequestedListener = (startAppListening: AppStartListening) => {
+export const addAdHocPostProcessingRequestedListener = (startAppListening: AppStartListening) => {
startAppListening({
- actionCreator: upscaleRequested,
+ actionCreator: adHocPostProcessingRequested,
effect: async (action, { dispatch, getState }) => {
const log = logger('session');
const { imageDTO } = action.payload;
- const { image_name } = imageDTO;
const state = getState();
- const { isAllowedToUpscale, detailTKey } = createIsAllowedToUpscaleSelector(imageDTO)(state);
-
- // if we can't upscale, show a toast and return
- if (!isAllowedToUpscale) {
- log.error(
- { imageDTO },
- t(detailTKey ?? 'parameters.isAllowedToUpscale.tooLarge') // should never coalesce
- );
- toast({
- id: 'NOT_ALLOWED_TO_UPSCALE',
- title: t(detailTKey ?? 'parameters.isAllowedToUpscale.tooLarge'), // should never coalesce
- status: 'error',
- });
- return;
- }
-
const enqueueBatchArg: BatchConfig = {
prepend: true,
batch: {
- graph: buildAdHocUpscaleGraph({
- image_name,
+ graph: await buildAdHocPostProcessingGraph({
+ image: imageDTO,
state,
}),
runs: 1,
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addArchivedOrDeletedBoardListener.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addArchivedOrDeletedBoardListener.ts
index 1581da9b37..23d3cbc9af 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addArchivedOrDeletedBoardListener.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addArchivedOrDeletedBoardListener.ts
@@ -10,32 +10,32 @@ import {
import { boardsApi } from 'services/api/endpoints/boards';
import { imagesApi } from 'services/api/endpoints/images';
+// Type inference doesn't work for this if you inline it in the listener for some reason
+const matchAnyBoardDeleted = isAnyOf(
+ imagesApi.endpoints.deleteBoard.matchFulfilled,
+ imagesApi.endpoints.deleteBoardAndImages.matchFulfilled
+);
+
export const addArchivedOrDeletedBoardListener = (startAppListening: AppStartListening) => {
/**
* The auto-add board shouldn't be set to an archived board or deleted board. When we archive a board, delete
* a board, or change a the archived board visibility flag, we may need to reset the auto-add board.
*/
startAppListening({
- matcher: isAnyOf(
- // If a board is deleted, we'll need to reset the auto-add board
- imagesApi.endpoints.deleteBoard.matchFulfilled,
- imagesApi.endpoints.deleteBoardAndImages.matchFulfilled
- ),
+ matcher: matchAnyBoardDeleted,
effect: async (action, { dispatch, getState }) => {
const state = getState();
- const queryArgs = selectListBoardsQueryArgs(state);
- const queryResult = boardsApi.endpoints.listAllBoards.select(queryArgs)(state);
+ const deletedBoardId = action.meta.arg.originalArgs;
const { autoAddBoardId, selectedBoardId } = state.gallery;
- if (!queryResult.data) {
- return;
- }
-
- if (!queryResult.data.find((board) => board.board_id === selectedBoardId)) {
+ // If the deleted board was currently selected, we should reset the selected board to uncategorized
+ if (deletedBoardId === selectedBoardId) {
dispatch(boardIdSelected({ boardId: 'none' }));
dispatch(galleryViewChanged('images'));
}
- if (!queryResult.data.find((board) => board.board_id === autoAddBoardId)) {
+
+ // If the deleted board was selected for auto-add, we should reset the auto-add board to uncategorized
+ if (deletedBoardId === autoAddBoardId) {
dispatch(autoAddBoardIdChanged('none'));
}
},
@@ -46,14 +46,8 @@ export const addArchivedOrDeletedBoardListener = (startAppListening: AppStartLis
matcher: boardsApi.endpoints.updateBoard.matchFulfilled,
effect: async (action, { dispatch, getState }) => {
const state = getState();
- const queryArgs = selectListBoardsQueryArgs(state);
- const queryResult = boardsApi.endpoints.listAllBoards.select(queryArgs)(state);
const { shouldShowArchivedBoards } = state.gallery;
- if (!queryResult.data) {
- return;
- }
-
const wasArchived = action.meta.arg.originalArgs.changes.archived === true;
if (wasArchived && !shouldShowArchivedBoards) {
@@ -71,7 +65,7 @@ export const addArchivedOrDeletedBoardListener = (startAppListening: AppStartLis
const shouldShowArchivedBoards = action.payload;
// We only need to take action if we have just hidden archived boards.
- if (!shouldShowArchivedBoards) {
+ if (shouldShowArchivedBoards) {
return;
}
@@ -86,14 +80,16 @@ export const addArchivedOrDeletedBoardListener = (startAppListening: AppStartLis
// Handle the case where selected board is archived
const selectedBoard = queryResult.data.find((b) => b.board_id === selectedBoardId);
- if (selectedBoard && selectedBoard.archived) {
+ if (!selectedBoard || selectedBoard.archived) {
+ // If we can't find the selected board or it's archived, we should reset the selected board to uncategorized
dispatch(boardIdSelected({ boardId: 'none' }));
dispatch(galleryViewChanged('images'));
}
// Handle the case where auto-add board is archived
const autoAddBoard = queryResult.data.find((b) => b.board_id === autoAddBoardId);
- if (autoAddBoard && autoAddBoard.archived) {
+ if (!autoAddBoard || autoAddBoard.archived) {
+ // If we can't find the auto-add board or it's archived, we should reset the selected board to uncategorized
dispatch(autoAddBoardIdChanged('none'));
}
},
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedUpscale.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedUpscale.ts
new file mode 100644
index 0000000000..dc870a9f8b
--- /dev/null
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedUpscale.ts
@@ -0,0 +1,36 @@
+import { enqueueRequested } from 'app/store/actions';
+import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
+import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice';
+import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
+import { buildMultidiffusionUpscaleGraph } from 'features/nodes/util/graph/buildMultidiffusionUpscaleGraph';
+import { queueApi } from 'services/api/endpoints/queue';
+
+export const addEnqueueRequestedUpscale = (startAppListening: AppStartListening) => {
+ startAppListening({
+ predicate: (action): action is ReturnType =>
+ enqueueRequested.match(action) && action.payload.tabName === 'upscaling',
+ effect: async (action, { getState, dispatch }) => {
+ const state = getState();
+ const { shouldShowProgressInViewer } = state.ui;
+ const { prepend } = action.payload;
+
+ const graph = await buildMultidiffusionUpscaleGraph(state);
+
+ const batchConfig = prepareLinearUIBatch(state, graph, prepend);
+
+ const req = dispatch(
+ queueApi.endpoints.enqueueBatch.initiate(batchConfig, {
+ fixedCacheKey: 'enqueueBatch',
+ })
+ );
+ try {
+ await req.unwrap();
+ if (shouldShowProgressInViewer) {
+ dispatch(isImageViewerOpenChanged(true));
+ }
+ } finally {
+ req.reset();
+ }
+ },
+ });
+};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts
index 0cd77dc2e7..a65c31b7cd 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts
@@ -23,6 +23,7 @@ import {
} from 'features/gallery/store/gallerySlice';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
+import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
import { imagesApi } from 'services/api/endpoints/images';
export const dndDropped = createAction<{
@@ -243,6 +244,20 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
return;
}
+ /**
+ * Image dropped on upscale initial image
+ */
+ if (
+ overData.actionType === 'SET_UPSCALE_INITIAL_IMAGE' &&
+ activeData.payloadType === 'IMAGE_DTO' &&
+ activeData.payload.imageDTO
+ ) {
+ const { imageDTO } = activeData.payload;
+
+ dispatch(upscaleInitialImageChanged(imageDTO));
+ return;
+ }
+
/**
* Multiple images dropped on user board
*/
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
index bd1ee47825..1aa47345e1 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
@@ -14,6 +14,7 @@ import {
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
+import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { omit } from 'lodash-es';
@@ -89,6 +90,15 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
return;
}
+ if (postUploadAction?.type === 'SET_UPSCALE_INITIAL_IMAGE') {
+ dispatch(upscaleInitialImageChanged(imageDTO));
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: 'set as upscale initial image',
+ });
+ return;
+ }
+
if (postUploadAction?.type === 'SET_CONTROL_ADAPTER_IMAGE') {
const { id } = postUploadAction;
dispatch(
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts
index eb86f54c84..9d02fcbfa5 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts
@@ -10,6 +10,7 @@ import { heightChanged, widthChanged } from 'features/controlLayers/store/contro
import { loraRemoved } from 'features/lora/store/loraSlice';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
+import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
@@ -17,7 +18,12 @@ import { forEach } from 'lodash-es';
import type { Logger } from 'roarr';
import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models';
import type { AnyModelConfig } from 'services/api/types';
-import { isNonRefinerMainModelConfig, isRefinerMainModelModelConfig, isVAEModelConfig } from 'services/api/types';
+import {
+ isNonRefinerMainModelConfig,
+ isRefinerMainModelModelConfig,
+ isSpandrelImageToImageModelConfig,
+ isVAEModelConfig,
+} from 'services/api/types';
export const addModelsLoadedListener = (startAppListening: AppStartListening) => {
startAppListening({
@@ -36,6 +42,7 @@ export const addModelsLoadedListener = (startAppListening: AppStartListening) =>
handleVAEModels(models, state, dispatch, log);
handleLoRAModels(models, state, dispatch, log);
handleControlAdapterModels(models, state, dispatch, log);
+ handleSpandrelImageToImageModels(models, state, dispatch, log);
},
});
};
@@ -177,3 +184,25 @@ const handleControlAdapterModels: ModelHandler = (models, state, dispatch, _log)
dispatch(controlAdapterModelCleared({ id: ca.id }));
});
};
+
+const handleSpandrelImageToImageModels: ModelHandler = (models, state, dispatch, _log) => {
+ const { upscaleModel: currentUpscaleModel, postProcessingModel: currentPostProcessingModel } = state.upscale;
+ const upscaleModels = models.filter(isSpandrelImageToImageModelConfig);
+ const firstModel = upscaleModels[0] || null;
+
+ const isCurrentUpscaleModelAvailable = currentUpscaleModel
+ ? upscaleModels.some((m) => m.key === currentUpscaleModel.key)
+ : false;
+
+ if (!isCurrentUpscaleModelAvailable) {
+ dispatch(upscaleModelChanged(firstModel));
+ }
+
+ const isCurrentPostProcessingModelAvailable = currentPostProcessingModel
+ ? upscaleModels.some((m) => m.key === currentPostProcessingModel.key)
+ : false;
+
+ if (!isCurrentPostProcessingModelAvailable) {
+ dispatch(postProcessingModelChanged(firstModel));
+ }
+};
diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts
index 062cdc1cbf..6ae2011355 100644
--- a/invokeai/frontend/web/src/app/store/store.ts
+++ b/invokeai/frontend/web/src/app/store/store.ts
@@ -25,7 +25,7 @@ import { nodesPersistConfig, nodesSlice, nodesUndoableConfig } from 'features/no
import { workflowSettingsPersistConfig, workflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice';
import { workflowPersistConfig, workflowSlice } from 'features/nodes/store/workflowSlice';
import { generationPersistConfig, generationSlice } from 'features/parameters/store/generationSlice';
-import { postprocessingPersistConfig, postprocessingSlice } from 'features/parameters/store/postprocessingSlice';
+import { upscalePersistConfig, upscaleSlice } from 'features/parameters/store/upscaleSlice';
import { queueSlice } from 'features/queue/store/queueSlice';
import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice';
import { configSlice } from 'features/system/store/configSlice';
@@ -52,7 +52,6 @@ const allReducers = {
[gallerySlice.name]: gallerySlice.reducer,
[generationSlice.name]: generationSlice.reducer,
[nodesSlice.name]: undoable(nodesSlice.reducer, nodesUndoableConfig),
- [postprocessingSlice.name]: postprocessingSlice.reducer,
[systemSlice.name]: systemSlice.reducer,
[configSlice.name]: configSlice.reducer,
[uiSlice.name]: uiSlice.reducer,
@@ -69,6 +68,7 @@ const allReducers = {
[controlLayersSlice.name]: undoable(controlLayersSlice.reducer, controlLayersUndoableConfig),
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
[api.reducerPath]: api.reducer,
+ [upscaleSlice.name]: upscaleSlice.reducer,
};
const rootReducer = combineReducers(allReducers);
@@ -102,7 +102,6 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
[galleryPersistConfig.name]: galleryPersistConfig,
[generationPersistConfig.name]: generationPersistConfig,
[nodesPersistConfig.name]: nodesPersistConfig,
- [postprocessingPersistConfig.name]: postprocessingPersistConfig,
[systemPersistConfig.name]: systemPersistConfig,
[workflowPersistConfig.name]: workflowPersistConfig,
[uiPersistConfig.name]: uiPersistConfig,
@@ -114,6 +113,7 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
[hrfPersistConfig.name]: hrfPersistConfig,
[controlLayersPersistConfig.name]: controlLayersPersistConfig,
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
+ [upscalePersistConfig.name]: upscalePersistConfig,
};
const unserialize: UnserializeFunction = (data, key) => {
diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts
index 6d7416d95d..bb27cf58a8 100644
--- a/invokeai/frontend/web/src/app/types/invokeai.ts
+++ b/invokeai/frontend/web/src/app/types/invokeai.ts
@@ -72,7 +72,6 @@ export type AppConfig = {
canRestoreDeletedImagesFromBin: boolean;
nodesAllowlist: string[] | undefined;
nodesDenylist: string[] | undefined;
- maxUpscalePixels?: number;
metadataFetchDebounce?: number;
workflowFetchDebounce?: number;
isLocal?: boolean;
diff --git a/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx b/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
index 935212ee58..cb295bb0f4 100644
--- a/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
+++ b/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
@@ -10,9 +10,12 @@ import {
PopoverContent,
PopoverTrigger,
Portal,
+ Spacer,
Text,
} from '@invoke-ai/ui-library';
-import { useAppSelector } from 'app/store/storeHooks';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { setShouldEnableInformationalPopovers } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { merge, omit } from 'lodash-es';
import type { ReactElement } from 'react';
import { memo, useCallback, useMemo } from 'react';
@@ -71,7 +74,7 @@ type ContentProps = {
const Content = ({ data, feature }: ContentProps) => {
const { t } = useTranslation();
-
+ const dispatch = useAppDispatch();
const heading = useMemo(() => t(`popovers.${feature}.heading`), [feature, t]);
const paragraphs = useMemo(
@@ -82,16 +85,25 @@ const Content = ({ data, feature }: ContentProps) => {
[feature, t]
);
- const handleClick = useCallback(() => {
+ const onClickLearnMore = useCallback(() => {
if (!data?.href) {
return;
}
window.open(data.href);
}, [data?.href]);
+ const onClickDontShowMeThese = useCallback(() => {
+ dispatch(setShouldEnableInformationalPopovers(false));
+ toast({
+ title: t('settings.informationalPopoversDisabled'),
+ description: t('settings.informationalPopoversDisabledDesc'),
+ status: 'info',
+ });
+ }, [dispatch, t]);
+
return (
-
-
+
+
{heading && (
@@ -116,20 +128,19 @@ const Content = ({ data, feature }: ContentProps) => {
{paragraphs.map((p) => (
{p}
))}
- {data?.href && (
- <>
-
- }
- alignSelf="flex-end"
- variant="link"
- >
+
+
+
+
+
+ {data?.href && (
+ } variant="link" size="sm">
{t('common.learnMore') ?? heading}
- >
- )}
+ )}
+
diff --git a/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts b/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
index f973d98f1a..de28f72223 100644
--- a/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
+++ b/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
@@ -53,7 +53,11 @@ export type Feature =
| 'refinerCfgScale'
| 'scaleBeforeProcessing'
| 'seamlessTilingXAxis'
- | 'seamlessTilingYAxis';
+ | 'seamlessTilingYAxis'
+ | 'upscaleModel'
+ | 'scale'
+ | 'creativity'
+ | 'structure';
export type PopoverData = PopoverProps & {
image?: string;
diff --git a/invokeai/frontend/web/src/common/hooks/useAssertSingleton.ts b/invokeai/frontend/web/src/common/hooks/useAssertSingleton.ts
new file mode 100644
index 0000000000..0f7cc9db6f
--- /dev/null
+++ b/invokeai/frontend/web/src/common/hooks/useAssertSingleton.ts
@@ -0,0 +1,18 @@
+import { useEffect } from 'react';
+import { assert } from 'tsafe';
+
+const IDS = new Set();
+
+/**
+ * Asserts that there is only one instance of a singleton entity. It can be a hook or a component.
+ * @param id The ID of the singleton entity.
+ */
+export function useAssertSingleton(id: string) {
+ useEffect(() => {
+ assert(!IDS.has(id), `There should be only one instance of ${id}`);
+ IDS.add(id);
+ return () => {
+ IDS.delete(id);
+ };
+ }, [id]);
+}
diff --git a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts
index 5b1bf1f5b3..d8e7d70a8c 100644
--- a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts
+++ b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts
@@ -21,6 +21,10 @@ const selectPostUploadAction = createMemoizedSelector(activeTabNameSelector, (ac
postUploadAction = { type: 'SET_CANVAS_INITIAL_IMAGE' };
}
+ if (activeTabName === 'upscaling') {
+ postUploadAction = { type: 'SET_UPSCALE_INITIAL_IMAGE' };
+ }
+
return postUploadAction;
});
diff --git a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts
index dbf3c41480..ba2117f207 100644
--- a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts
+++ b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts
@@ -15,6 +15,7 @@ import type { Templates } from 'features/nodes/store/types';
import { selectWorkflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
+import { selectUpscalelice } from 'features/parameters/store/upscaleSlice';
import { selectSystemSlice } from 'features/system/store/systemSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import i18n from 'i18next';
@@ -40,8 +41,19 @@ const createSelector = (templates: Templates) =>
selectDynamicPromptsSlice,
selectControlLayersSlice,
activeTabNameSelector,
+ selectUpscalelice,
],
- (controlAdapters, generation, system, nodes, workflowSettings, dynamicPrompts, controlLayers, activeTabName) => {
+ (
+ controlAdapters,
+ generation,
+ system,
+ nodes,
+ workflowSettings,
+ dynamicPrompts,
+ controlLayers,
+ activeTabName,
+ upscale
+ ) => {
const { model } = generation;
const { size } = controlLayers.present;
const { positivePrompt } = controlLayers.present;
@@ -194,6 +206,16 @@ const createSelector = (templates: Templates) =>
reasons.push({ prefix, content });
}
});
+ } else if (activeTabName === 'upscaling') {
+ if (!upscale.upscaleInitialImage) {
+ reasons.push({ content: i18n.t('upscaling.missingUpscaleInitialImage') });
+ }
+ if (!upscale.upscaleModel) {
+ reasons.push({ content: i18n.t('upscaling.missingUpscaleModel') });
+ }
+ if (!upscale.tileControlnetModel) {
+ reasons.push({ content: i18n.t('upscaling.missingTileControlNetModel') });
+ }
} else {
// Handling for all other tabs
selectControlAdapterAll(controlAdapters)
diff --git a/invokeai/frontend/web/src/features/dnd/types/index.ts b/invokeai/frontend/web/src/features/dnd/types/index.ts
index 6fcf18421e..93bde117a1 100644
--- a/invokeai/frontend/web/src/features/dnd/types/index.ts
+++ b/invokeai/frontend/web/src/features/dnd/types/index.ts
@@ -62,6 +62,10 @@ export type CanvasInitialImageDropData = BaseDropData & {
actionType: 'SET_CANVAS_INITIAL_IMAGE';
};
+type UpscaleInitialImageDropData = BaseDropData & {
+ actionType: 'SET_UPSCALE_INITIAL_IMAGE';
+};
+
type NodesImageDropData = BaseDropData & {
actionType: 'SET_NODES_IMAGE';
context: {
@@ -98,7 +102,8 @@ export type TypesafeDroppableData =
| IPALayerImageDropData
| RGLayerIPAdapterImageDropData
| IILayerImageDropData
- | SelectForCompareDropData;
+ | SelectForCompareDropData
+ | UpscaleInitialImageDropData;
type BaseDragData = {
id: string;
diff --git a/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts b/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts
index 6dec862345..3f8fe5ab73 100644
--- a/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts
+++ b/invokeai/frontend/web/src/features/dnd/util/isValidDrop.ts
@@ -27,6 +27,8 @@ export const isValidDrop = (overData?: TypesafeDroppableData | null, activeData?
return payloadType === 'IMAGE_DTO';
case 'SET_CANVAS_INITIAL_IMAGE':
return payloadType === 'IMAGE_DTO';
+ case 'SET_UPSCALE_INITIAL_IMAGE':
+ return payloadType === 'IMAGE_DTO';
case 'SET_NODES_IMAGE':
return payloadType === 'IMAGE_DTO';
case 'SELECT_FOR_COMPARE':
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx
new file mode 100644
index 0000000000..63ba2991cf
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx
@@ -0,0 +1,47 @@
+import { Flex, Image, Text } from '@invoke-ai/ui-library';
+import { skipToken } from '@reduxjs/toolkit/query';
+import { useTranslation } from 'react-i18next';
+import { useGetBoardAssetsTotalQuery, useGetBoardImagesTotalQuery } from 'services/api/endpoints/boards';
+import { useGetImageDTOQuery } from 'services/api/endpoints/images';
+import type { BoardDTO } from 'services/api/types';
+
+type Props = {
+ board: BoardDTO | null;
+};
+
+export const BoardTooltip = ({ board }: Props) => {
+ const { t } = useTranslation();
+ const { imagesTotal } = useGetBoardImagesTotalQuery(board?.board_id || 'none', {
+ selectFromResult: ({ data }) => {
+ return { imagesTotal: data?.total ?? 0 };
+ },
+ });
+ const { assetsTotal } = useGetBoardAssetsTotalQuery(board?.board_id || 'none', {
+ selectFromResult: ({ data }) => {
+ return { assetsTotal: data?.total ?? 0 };
+ },
+ });
+ const { currentData: coverImage } = useGetImageDTOQuery(board?.cover_image_name ?? skipToken);
+
+ return (
+
+ {coverImage && (
+
+ )}
+
+
+ {t('boards.imagesWithCount', { count: imagesTotal })}, {t('boards.assetsWithCount', { count: assetsTotal })}
+
+ {board?.archived && ({t('boards.archived')})}
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTotalsTooltip.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTotalsTooltip.tsx
deleted file mode 100644
index b4c89a002d..0000000000
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTotalsTooltip.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { useTranslation } from 'react-i18next';
-import { useGetBoardAssetsTotalQuery, useGetBoardImagesTotalQuery } from 'services/api/endpoints/boards';
-
-type Props = {
- board_id: string;
- isArchived: boolean;
-};
-
-export const BoardTotalsTooltip = ({ board_id, isArchived }: Props) => {
- const { t } = useTranslation();
- const { imagesTotal } = useGetBoardImagesTotalQuery(board_id, {
- selectFromResult: ({ data }) => {
- return { imagesTotal: data?.total ?? 0 };
- },
- });
- const { assetsTotal } = useGetBoardAssetsTotalQuery(board_id, {
- selectFromResult: ({ data }) => {
- return { assetsTotal: data?.total ?? 0 };
- },
- });
- return `${t('boards.imagesWithCount', { count: imagesTotal })}, ${t('boards.assetsWithCount', { count: assetsTotal })}${isArchived ? ` (${t('boards.archived')})` : ''}`;
-};
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx
index 4325281e0f..d5a96e5966 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx
@@ -1,13 +1,10 @@
-import { Box, Flex, Text } from '@invoke-ai/ui-library';
+import { Button, Collapse, Flex, Icon, Text, useDisclosure } from '@invoke-ai/ui-library';
import { EMPTY_ARRAY } from 'app/store/constants';
import { useAppSelector } from 'app/store/storeHooks';
-import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
-import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors';
-import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
-import type { CSSProperties } from 'react';
-import { memo, useMemo, useState } from 'react';
+import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
+import { PiCaretDownBold } from 'react-icons/pi';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import type { BoardDTO } from 'services/api/types';
@@ -15,101 +12,111 @@ import AddBoardButton from './AddBoardButton';
import GalleryBoard from './GalleryBoard';
import NoBoardBoard from './NoBoardBoard';
-const overlayScrollbarsStyles: CSSProperties = {
- height: '100%',
- width: '100%',
+type Props = {
+ isPrivate: boolean;
+ setBoardToDelete: (board?: BoardDTO) => void;
};
-const BoardsList = () => {
+export const BoardsList = ({ isPrivate, setBoardToDelete }: Props) => {
+ const { t } = useTranslation();
const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId);
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
- const allowPrivateBoards = useAppSelector((s) => s.config.allowPrivateBoards);
const queryArgs = useAppSelector(selectListBoardsQueryArgs);
const { data: boards } = useListAllBoardsQuery(queryArgs);
- const [boardToDelete, setBoardToDelete] = useState();
- const { t } = useTranslation();
+ const allowPrivateBoards = useAppSelector((s) => s.config.allowPrivateBoards);
+ const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
- const { filteredPrivateBoards, filteredSharedBoards } = useMemo(() => {
- const filteredBoards = boardSearchText
- ? boards?.filter((board) => board.board_name.toLowerCase().includes(boardSearchText.toLowerCase()))
- : boards;
- const filteredPrivateBoards = filteredBoards?.filter((board) => board.is_private) ?? EMPTY_ARRAY;
- const filteredSharedBoards = filteredBoards?.filter((board) => !board.is_private) ?? EMPTY_ARRAY;
- return { filteredPrivateBoards, filteredSharedBoards };
- }, [boardSearchText, boards]);
+ const filteredBoards = useMemo(() => {
+ if (!boards) {
+ return EMPTY_ARRAY;
+ }
+
+ return boards.filter((board) => {
+ if (boardSearchText.length) {
+ return board.is_private === isPrivate && board.board_name.toLowerCase().includes(boardSearchText.toLowerCase());
+ } else {
+ return board.is_private === isPrivate;
+ }
+ });
+ }, [boardSearchText, boards, isPrivate]);
+
+ const boardElements = useMemo(() => {
+ const elements = [];
+ if (allowPrivateBoards && isPrivate && !boardSearchText.length) {
+ elements.push();
+ }
+
+ if (!allowPrivateBoards && !boardSearchText.length) {
+ elements.push();
+ }
+
+ filteredBoards.forEach((board) => {
+ elements.push(
+
+ );
+ });
+
+ return elements;
+ }, [allowPrivateBoards, isPrivate, boardSearchText.length, filteredBoards, selectedBoardId, setBoardToDelete]);
+
+ const boardListTitle = useMemo(() => {
+ if (allowPrivateBoards) {
+ return isPrivate ? t('boards.private') : t('boards.shared');
+ } else {
+ return t('boards.boards');
+ }
+ }, [isPrivate, allowPrivateBoards, t]);
return (
- <>
-
-
-
- {allowPrivateBoards && (
-
-
-
- {t('boards.private')}
-
-
-
-
-
- {filteredPrivateBoards.map((board) => (
-
- ))}
-
-
- )}
-
-
-
- {allowPrivateBoards ? t('boards.shared') : t('boards.boards')}
-
-
-
-
- {!allowPrivateBoards && }
- {filteredSharedBoards.map((board) => (
-
- ))}
-
+
+
+ {allowPrivateBoards ? (
+
-
-
-
- >
+
+ ) : (
+
+ {boardListTitle}
+
+ )}
+
+
+
+
+ {boardElements.length ? (
+ boardElements
+ ) : (
+
+ {t('boards.noBoards', { boardType: boardSearchText.length ? 'Matching' : '' })}
+
+ )}
+
+
+
);
};
-export default memo(BoardsList);
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsListWrapper.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsListWrapper.tsx
new file mode 100644
index 0000000000..637dbd304a
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsListWrapper.tsx
@@ -0,0 +1,35 @@
+import { Box } from '@invoke-ai/ui-library';
+import { useAppSelector } from 'app/store/storeHooks';
+import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
+import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
+import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
+import type { CSSProperties } from 'react';
+import { memo, useState } from 'react';
+import type { BoardDTO } from 'services/api/types';
+
+import { BoardsList } from './BoardsList';
+
+const overlayScrollbarsStyles: CSSProperties = {
+ height: '100%',
+ width: '100%',
+};
+
+const BoardsListWrapper = () => {
+ const allowPrivateBoards = useAppSelector((s) => s.config.allowPrivateBoards);
+ const [boardToDelete, setBoardToDelete] = useState();
+
+ return (
+ <>
+
+
+
+ {allowPrivateBoards && }
+
+
+
+
+
+ >
+ );
+};
+export default memo(BoardsListWrapper);
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx
index fb53a13795..931c1e6cbb 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx
@@ -40,7 +40,7 @@ const BoardsSearch = () => {
);
return (
-
+
{(ref) => (
- }
- openDelay={1000}
- placement="left"
- closeOnScroll
- >
+ } openDelay={1000} placement="left" closeOnScroll p={2}>
@@ -168,7 +165,7 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
{board.archived && !editingDisclosure.isOpen && }
{!editingDisclosure.isOpen && {board.image_count}}
- {t('unifiedCanvas.move')}} />
+ {t('unifiedCanvas.move')}} />
)}
@@ -197,8 +194,8 @@ const CoverImage = ({ board }: { board: BoardDTO }) => {
src={coverImage.thumbnail_url}
draggable={false}
objectFit="cover"
- w={8}
- h={8}
+ w={10}
+ h={10}
borderRadius="base"
borderBottomRadius="lg"
/>
@@ -206,8 +203,8 @@ const CoverImage = ({ board }: { board: BoardDTO }) => {
}
return (
-
-
+
+
);
};
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx
index 14bf3d5742..c8b3ceb431 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx
@@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIDroppable from 'common/components/IAIDroppable';
import type { RemoveFromBoardDropData } from 'features/dnd/types';
import { AutoAddBadge } from 'features/gallery/components/Boards/AutoAddBadge';
-import { BoardTotalsTooltip } from 'features/gallery/components/Boards/BoardsList/BoardTotalsTooltip';
+import { BoardTooltip } from 'features/gallery/components/Boards/BoardsList/BoardTooltip';
import NoBoardBoardContextMenu from 'features/gallery/components/Boards/NoBoardBoardContextMenu';
import { autoAddBoardIdChanged, boardIdSelected } from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo } from 'react';
@@ -46,25 +46,16 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
[]
);
- const filteredOut = useMemo(() => {
- return boardSearchText ? !boardName.toLowerCase().includes(boardSearchText.toLowerCase()) : false;
- }, [boardName, boardSearchText]);
-
const { t } = useTranslation();
- if (filteredOut) {
+ if (boardSearchText.length) {
return null;
}
return (
{(ref) => (
- }
- openDelay={1000}
- placement="left"
- closeOnScroll
- >
+ } openDelay={1000} placement="left" closeOnScroll>
{
alignItems="center"
borderRadius="base"
cursor="pointer"
- px={2}
py={1}
- gap={2}
+ ps={1}
+ pe={4}
+ gap={4}
bg={isSelected ? 'base.850' : undefined}
_hover={_hover}
+ h={12}
>
-
+
{/* iconified from public/assets/images/invoke-symbol-wht-lrg.svg */}
-
+
{
-
+
{boardName}
{autoAddBoardId === 'none' && }
{imagesTotal}
- {t('unifiedCanvas.move')}} />
+ {t('unifiedCanvas.move')}} />
)}
diff --git a/invokeai/frontend/web/src/features/gallery/components/Gallery.tsx b/invokeai/frontend/web/src/features/gallery/components/Gallery.tsx
new file mode 100644
index 0000000000..8776fa888b
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/Gallery.tsx
@@ -0,0 +1,105 @@
+import type { ChakraProps } from '@invoke-ai/ui-library';
+import {
+ Box,
+ Collapse,
+ Flex,
+ IconButton,
+ Spacer,
+ Tab,
+ TabList,
+ Tabs,
+ Text,
+ useDisclosure,
+} from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { useGallerySearchTerm } from 'features/gallery/components/ImageGrid/useGallerySearchTerm';
+import { galleryViewChanged } from 'features/gallery/store/gallerySlice';
+import type { CSSProperties } from 'react';
+import { useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiMagnifyingGlassBold } from 'react-icons/pi';
+import { useBoardName } from 'services/api/hooks/useBoardName';
+
+import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
+import { GalleryPagination } from './ImageGrid/GalleryPagination';
+import { GallerySearch } from './ImageGrid/GallerySearch';
+
+const BASE_STYLES: ChakraProps['sx'] = {
+ fontWeight: 'semibold',
+ fontSize: 'sm',
+ color: 'base.300',
+};
+
+const SELECTED_STYLES: ChakraProps['sx'] = {
+ borderColor: 'base.800',
+ borderBottomColor: 'base.900',
+ color: 'invokeBlue.300',
+};
+
+const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0 };
+
+export const Gallery = () => {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const galleryView = useAppSelector((s) => s.gallery.galleryView);
+ const initialSearchTerm = useAppSelector((s) => s.gallery.searchTerm);
+ const searchDisclosure = useDisclosure({ defaultIsOpen: initialSearchTerm.length > 0 });
+ const [searchTerm, onChangeSearchTerm, onResetSearchTerm] = useGallerySearchTerm();
+
+ const handleClickImages = useCallback(() => {
+ dispatch(galleryViewChanged('images'));
+ }, [dispatch]);
+
+ const handleClickAssets = useCallback(() => {
+ dispatch(galleryViewChanged('assets'));
+ }, [dispatch]);
+
+ const handleClickSearch = useCallback(() => {
+ searchDisclosure.onToggle();
+ onResetSearchTerm();
+ }, [onResetSearchTerm, searchDisclosure]);
+
+ const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId);
+ const boardName = useBoardName(selectedBoardId);
+
+ return (
+
+
+
+
+ {boardName}
+
+
+
+ {t('parameters.images')}
+
+
+ {t('gallery.assets')}
+
+ }
+ colorScheme={searchDisclosure.isOpen ? 'invokeBlue' : 'base'}
+ variant="link"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx
deleted file mode 100644
index 8ede311f9e..0000000000
--- a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Flex, Text } from '@invoke-ai/ui-library';
-import { useAppSelector } from 'app/store/storeHooks';
-import { memo } from 'react';
-import { useBoardName } from 'services/api/hooks/useBoardName';
-
-type Props = {
- onClick: () => void;
-};
-
-const GalleryBoardName = (props: Props) => {
- const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId);
- const boardName = useBoardName(selectedBoardId);
-
- return (
-
-
- {boardName}
-
-
- );
-};
-
-export default memo(GalleryBoardName);
diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryHeader.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryHeader.tsx
index 69c2c8fe35..ce9436811c 100644
--- a/invokeai/frontend/web/src/features/gallery/components/GalleryHeader.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/GalleryHeader.tsx
@@ -3,32 +3,21 @@ import { useStore } from '@nanostores/react';
import { $projectName, $projectUrl } from 'app/store/nanostores/projectId';
import { memo } from 'react';
-import GalleryBoardName from './GalleryBoardName';
-
-type Props = {
- onClickBoardName: () => void;
-};
-
-export const GalleryHeader = memo((props: Props) => {
+export const GalleryHeader = memo(() => {
const projectName = useStore($projectName);
const projectUrl = useStore($projectUrl);
if (projectName && projectUrl) {
return (
-
+
{projectName}
-
);
}
- return (
-
-
-
- );
+ return null;
});
GalleryHeader.displayName = 'GalleryHeader';
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
index 31df113115..ab12684c11 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
@@ -13,6 +13,7 @@ import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/ac
import { imageToCompareChanged } from 'features/gallery/store/gallerySlice';
import { $templates } from 'features/nodes/store/nodesSlice';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
+import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { toast } from 'features/toast/toast';
import { setActiveTab } from 'features/ui/store/uiSlice';
@@ -124,6 +125,11 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
dispatch(imageToCompareChanged(imageDTO));
}, [dispatch, imageDTO]);
+ const handleSendToUpscale = useCallback(() => {
+ dispatch(upscaleInitialImageChanged(imageDTO));
+ dispatch(setActiveTab('upscaling'));
+ }, [dispatch, imageDTO]);
+
return (
<>
}>
@@ -185,6 +191,9 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
{t('parameters.sendToUnifiedCanvas')}
)}
+ } onClickCapture={handleSendToUpscale} id="send-to-upscale">
+ {t('parameters.sendToUpscale')}
+
} onClickCapture={handleChangeBoard}>
{t('boards.changeBoard')}
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx
index 5a096f5cef..a42410b885 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx
@@ -1,57 +1,28 @@
-import type { ChakraProps } from '@invoke-ai/ui-library';
-import {
- Box,
- Collapse,
- Divider,
- Flex,
- IconButton,
- Spacer,
- Tab,
- TabList,
- Tabs,
- useDisclosure,
-} from '@invoke-ai/ui-library';
+import { Box, Button, Collapse, Divider, Flex, IconButton, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { GalleryHeader } from 'features/gallery/components/GalleryHeader';
-import { galleryViewChanged } from 'features/gallery/store/gallerySlice';
+import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
import { usePanel, type UsePanelOptions } from 'features/ui/hooks/usePanel';
import type { CSSProperties } from 'react';
import { memo, useCallback, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiMagnifyingGlassBold } from 'react-icons/pi';
+import { PiCaretDownBold, PiCaretUpBold, PiMagnifyingGlassBold } from 'react-icons/pi';
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
import { Panel, PanelGroup } from 'react-resizable-panels';
-import BoardsList from './Boards/BoardsList/BoardsList';
+import BoardsListWrapper from './Boards/BoardsList/BoardsListWrapper';
import BoardsSearch from './Boards/BoardsList/BoardsSearch';
+import { Gallery } from './Gallery';
import GallerySettingsPopover from './GallerySettingsPopover/GallerySettingsPopover';
-import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
-import { GalleryPagination } from './ImageGrid/GalleryPagination';
-import { GallerySearch } from './ImageGrid/GallerySearch';
const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0 };
-const BASE_STYLES: ChakraProps['sx'] = {
- fontWeight: 'semibold',
- fontSize: 'sm',
- color: 'base.300',
-};
-
-const SELECTED_STYLES: ChakraProps['sx'] = {
- borderColor: 'base.800',
- borderBottomColor: 'base.900',
- color: 'invokeBlue.300',
-};
-
const ImageGalleryContent = () => {
const { t } = useTranslation();
- const galleryView = useAppSelector((s) => s.gallery.galleryView);
- const searchTerm = useAppSelector((s) => s.gallery.searchTerm);
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
const dispatch = useAppDispatch();
- const searchDisclosure = useDisclosure({ defaultIsOpen: false });
- const boardSearchDisclosure = useDisclosure({ defaultIsOpen: false });
+ const boardSearchDisclosure = useDisclosure({ defaultIsOpen: !!boardSearchText.length });
const panelGroupRef = useRef(null);
const boardsListPanelOptions = useMemo(
@@ -67,42 +38,58 @@ const ImageGalleryContent = () => {
);
const boardsListPanel = usePanel(boardsListPanelOptions);
- const handleClickImages = useCallback(() => {
- dispatch(galleryViewChanged('images'));
- }, [dispatch]);
+ const handleClickBoardSearch = useCallback(() => {
+ if (boardSearchText.length) {
+ dispatch(boardSearchTextChanged(''));
+ }
+ boardSearchDisclosure.onToggle();
+ boardsListPanel.expand();
+ }, [boardSearchText.length, boardSearchDisclosure, boardsListPanel, dispatch]);
- const handleClickAssets = useCallback(() => {
- dispatch(galleryViewChanged('assets'));
- }, [dispatch]);
+ const handleToggleBoardPanel = useCallback(() => {
+ if (boardSearchText.length) {
+ dispatch(boardSearchTextChanged(''));
+ }
+ boardSearchDisclosure.onClose();
+ boardsListPanel.toggle();
+ }, [boardSearchText.length, boardSearchDisclosure, boardsListPanel, dispatch]);
return (
-
-
-
-
- }
- variant="link"
- />
- {boardSearchText && (
-
- )}
-
+
+
+
+ : }
+ >
+ {boardsListPanel.isCollapsed ? t('boards.viewBoards') : t('boards.hideBoards')}
+
+
+
+
+ }
+ colorScheme={boardSearchDisclosure.isOpen ? 'invokeBlue' : 'base'}
+ variant="link"
+ />
+
+
+
+
{
>
-
+
+
+
-
+
{
onDoubleClick={boardsListPanel.onDoubleClickHandle}
/>
-
-
-
-
- {t('parameters.images')}
-
-
- {t('gallery.assets')}
-
-
-
- }
- variant="link"
- />
- {searchTerm && (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryPagination.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryPagination.tsx
index ddc23ebe4b..27776ab77d 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryPagination.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryPagination.tsx
@@ -3,6 +3,8 @@ import { ELLIPSIS, useGalleryPagination } from 'features/gallery/hooks/useGaller
import { useCallback } from 'react';
import { PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi';
+import { JumpTo } from './JumpTo';
+
export const GalleryPagination = () => {
const { goPrev, goNext, isPrevEnabled, isNextEnabled, pageButtons, goToPage, currentPage, total } =
useGalleryPagination();
@@ -20,7 +22,7 @@ export const GalleryPagination = () => {
}
return (
-
+
{
variant="ghost"
/>
- {pageButtons.map((page, i) => {
- if (page === ELLIPSIS) {
- return (
-
- );
- }
- return (
-
- );
- })}
+ {pageButtons.map((page, i) => (
+
+ ))}
{
isDisabled={!isNextEnabled}
variant="ghost"
/>
+
);
};
+
+type PageButtonProps = {
+ page: number | typeof ELLIPSIS;
+ currentPage: number;
+ goToPage: (page: number) => void;
+};
+
+const PageButton = ({ page, currentPage, goToPage }: PageButtonProps) => {
+ if (page === ELLIPSIS) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySearch.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySearch.tsx
index 9e3a1bf487..bb2fe8ff29 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySearch.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySearch.tsx
@@ -1,59 +1,60 @@
import { IconButton, Input, InputGroup, InputRightElement, Spinner } from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { useAppSelector } from 'app/store/storeHooks';
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
-import { searchTermChanged } from 'features/gallery/store/gallerySlice';
-import { debounce } from 'lodash-es';
-import type { ChangeEvent } from 'react';
-import { useCallback, useMemo, useState } from 'react';
+import type { ChangeEvent, KeyboardEvent } from 'react';
+import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
import { useListImagesQuery } from 'services/api/endpoints/images';
-export const GallerySearch = () => {
- const dispatch = useAppDispatch();
- const searchTerm = useAppSelector((s) => s.gallery.searchTerm);
+type Props = {
+ searchTerm: string;
+ onChangeSearchTerm: (value: string) => void;
+ onResetSearchTerm: () => void;
+};
+
+export const GallerySearch = ({ searchTerm, onChangeSearchTerm, onResetSearchTerm }: Props) => {
const { t } = useTranslation();
- const [searchTermInput, setSearchTermInput] = useState(searchTerm);
const queryArgs = useAppSelector(selectListImagesQueryArgs);
const { isPending } = useListImagesQuery(queryArgs, {
selectFromResult: ({ isLoading, isFetching }) => ({ isPending: isLoading || isFetching }),
});
- const debouncedSetSearchTerm = useMemo(() => {
- return debounce((value: string) => {
- dispatch(searchTermChanged(value));
- }, 1000);
- }, [dispatch]);
const handleChangeInput = useCallback(
(e: ChangeEvent) => {
- setSearchTermInput(e.target.value);
- debouncedSetSearchTerm(e.target.value);
+ onChangeSearchTerm(e.target.value);
},
- [debouncedSetSearchTerm]
+ [onChangeSearchTerm]
);
- const handleClearInput = useCallback(() => {
- setSearchTermInput('');
- dispatch(searchTermChanged(''));
- }, [dispatch]);
+ const handleKeydown = useCallback(
+ (e: KeyboardEvent) => {
+ // exit search mode on escape
+ if (e.key === 'Escape') {
+ onResetSearchTerm();
+ }
+ },
+ [onResetSearchTerm]
+ );
return (
{isPending && (
)}
- {!isPending && searchTermInput.length && (
+ {!isPending && searchTerm.length && (
{
+ const { t } = useTranslation();
+ const { goToPage, currentPage, pages } = useGalleryPagination();
+ const [newPage, setNewPage] = useState(currentPage);
+ const { isOpen, onToggle, onClose } = useDisclosure();
+ const ref = useRef(null);
+
+ const onOpen = useCallback(() => {
+ setNewPage(currentPage);
+ setTimeout(() => {
+ const input = ref.current?.querySelector('input');
+ input?.focus();
+ input?.select();
+ }, 0);
+ }, [currentPage]);
+
+ const onChangeJumpTo = useCallback((v: number) => {
+ setNewPage(v - 1);
+ }, []);
+
+ const onClickGo = useCallback(() => {
+ goToPage(newPage);
+ onClose();
+ }, [newPage, goToPage, onClose]);
+
+ useHotkeys(
+ 'enter',
+ () => {
+ onClickGo();
+ },
+ { enabled: isOpen, enableOnFormTags: ['input'] },
+ [isOpen, onClickGo]
+ );
+
+ useHotkeys(
+ 'esc',
+ () => {
+ setNewPage(currentPage);
+ onClose();
+ },
+ { enabled: isOpen, enableOnFormTags: ['input'] },
+ [isOpen, onClose]
+ );
+
+ useEffect(() => {
+ setNewPage(currentPage);
+ }, [currentPage]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/useGallerySearchTerm.ts b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/useGallerySearchTerm.ts
new file mode 100644
index 0000000000..52e6f8ec08
--- /dev/null
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/useGallerySearchTerm.ts
@@ -0,0 +1,37 @@
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
+import { searchTermChanged } from 'features/gallery/store/gallerySlice';
+import { debounce } from 'lodash-es';
+import { useCallback, useMemo, useState } from 'react';
+
+export const useGallerySearchTerm = () => {
+ // Highlander!
+ useAssertSingleton('gallery-search-state');
+
+ const dispatch = useAppDispatch();
+ const searchTerm = useAppSelector((s) => s.gallery.searchTerm);
+
+ const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm);
+
+ const debouncedSetSearchTerm = useMemo(() => {
+ return debounce((val: string) => {
+ dispatch(searchTermChanged(val));
+ }, 1000);
+ }, [dispatch]);
+
+ const onChange = useCallback(
+ (val: string) => {
+ setLocalSearchTerm(val);
+ debouncedSetSearchTerm(val);
+ },
+ [debouncedSetSearchTerm]
+ );
+
+ const onReset = useCallback(() => {
+ debouncedSetSearchTerm.cancel();
+ setLocalSearchTerm('');
+ dispatch(searchTermChanged(''));
+ }, [debouncedSetSearchTerm, dispatch]);
+
+ return [localSearchTerm, onChange, onReset] as const;
+};
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx
index d500d692fe..d1f874271d 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx
@@ -2,7 +2,7 @@ import { ButtonGroup, IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/
import { useStore } from '@nanostores/react';
import { createSelector } from '@reduxjs/toolkit';
import { skipToken } from '@reduxjs/toolkit/query';
-import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested';
+import { adHocPostProcessingRequested } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
@@ -14,7 +14,7 @@ import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { parseAndRecallImageDimensions } from 'features/metadata/util/handlers';
import { $templates } from 'features/nodes/store/nodesSlice';
-import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings';
+import { PostProcessingPopover } from 'features/parameters/components/PostProcessing/PostProcessingPopover';
import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { selectSystemSlice } from 'features/system/store/systemSlice';
@@ -97,7 +97,7 @@ const CurrentImageButtons = () => {
if (!imageDTO) {
return;
}
- dispatch(upscaleRequested({ imageDTO }));
+ dispatch(adHocPostProcessingRequested({ imageDTO }));
}, [dispatch, imageDTO]);
const handleDelete = useCallback(() => {
@@ -193,7 +193,7 @@ const CurrentImageButtons = () => {
{isUpscalingEnabled && (
- {isUpscalingEnabled && }
+ {isUpscalingEnabled && }
)}
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx
index e610ca0077..dfc131c87c 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToolbar.tsx
@@ -9,7 +9,13 @@ import CurrentImageButtons from './CurrentImageButtons';
import { ViewerToggleMenu } from './ViewerToggleMenu';
export const ViewerToolbar = memo(() => {
- const tab = useAppSelector(activeTabNameSelector);
+ const showToggle = useAppSelector((s) => {
+ const tab = activeTabNameSelector(s);
+ if (tab === 'upscaling' || tab === 'workflows') {
+ return false;
+ }
+ return true;
+ });
return (
@@ -23,7 +29,7 @@ export const ViewerToolbar = memo(() => {
- {tab !== 'workflows' && }
+ {showToggle && }
diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useGalleryPagination.ts b/invokeai/frontend/web/src/features/gallery/hooks/useGalleryPagination.ts
index 75bea2dcb1..2350a6ccf5 100644
--- a/invokeai/frontend/web/src/features/gallery/hooks/useGalleryPagination.ts
+++ b/invokeai/frontend/web/src/features/gallery/hooks/useGalleryPagination.ts
@@ -1,6 +1,7 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
import { offsetChanged } from 'features/gallery/store/gallerySlice';
+import { throttle } from 'lodash-es';
import { useCallback, useEffect, useMemo } from 'react';
import { useListImagesQuery } from 'services/api/endpoints/images';
@@ -80,32 +81,41 @@ export const useGalleryPagination = () => {
return offset > 0;
}, [count, offset]);
+ const onOffsetChanged = useCallback(
+ (arg: Parameters[0]) => {
+ dispatch(offsetChanged(arg));
+ },
+ [dispatch]
+ );
+
+ const throttledOnOffsetChanged = useMemo(() => throttle(onOffsetChanged, 500), [onOffsetChanged]);
+
const goNext = useCallback(
(withHotkey?: 'arrow' | 'alt+arrow') => {
- dispatch(offsetChanged({ offset: offset + (limit || 0), withHotkey }));
+ throttledOnOffsetChanged({ offset: offset + (limit || 0), withHotkey });
},
- [dispatch, offset, limit]
+ [throttledOnOffsetChanged, offset, limit]
);
const goPrev = useCallback(
(withHotkey?: 'arrow' | 'alt+arrow') => {
- dispatch(offsetChanged({ offset: Math.max(offset - (limit || 0), 0), withHotkey }));
+ throttledOnOffsetChanged({ offset: Math.max(offset - (limit || 0), 0), withHotkey });
},
- [dispatch, offset, limit]
+ [throttledOnOffsetChanged, offset, limit]
);
const goToPage = useCallback(
(page: number) => {
- dispatch(offsetChanged({ offset: page * (limit || 0) }));
+ throttledOnOffsetChanged({ offset: page * (limit || 0) });
},
- [dispatch, limit]
+ [throttledOnOffsetChanged, limit]
);
const goToFirst = useCallback(() => {
- dispatch(offsetChanged({ offset: 0 }));
- }, [dispatch]);
+ throttledOnOffsetChanged({ offset: 0 });
+ }, [throttledOnOffsetChanged]);
const goToLast = useCallback(() => {
- dispatch(offsetChanged({ offset: (pages - 1) * (limit || 0) }));
- }, [dispatch, pages, limit]);
+ throttledOnOffsetChanged({ offset: (pages - 1) * (limit || 0) });
+ }, [throttledOnOffsetChanged, pages, limit]);
// handle when total/pages decrease and user is on high page number (ie bulk removing or deleting)
useEffect(() => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useControlNetOrT2IAdapterDefaultSettings.ts b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useControlNetOrT2IAdapterDefaultSettings.ts
index 826bec17b1..693cf3cf52 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useControlNetOrT2IAdapterDefaultSettings.ts
+++ b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useControlNetOrT2IAdapterDefaultSettings.ts
@@ -1,15 +1,10 @@
-import { skipToken } from '@reduxjs/toolkit/query';
import { isNil } from 'lodash-es';
import { useMemo } from 'react';
-import { useGetModelConfigWithTypeGuard } from 'services/api/hooks/useGetModelConfigWithTypeGuard';
-import { isControlNetOrT2IAdapterModelConfig } from 'services/api/types';
-
-export const useControlNetOrT2IAdapterDefaultSettings = (modelKey?: string | null) => {
- const { modelConfig, isLoading } = useGetModelConfigWithTypeGuard(
- modelKey ?? skipToken,
- isControlNetOrT2IAdapterModelConfig
- );
+import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
+export const useControlNetOrT2IAdapterDefaultSettings = (
+ modelConfig: ControlNetModelConfig | T2IAdapterModelConfig
+) => {
const defaultSettingsDefaults = useMemo(() => {
return {
preprocessor: {
@@ -19,5 +14,5 @@ export const useControlNetOrT2IAdapterDefaultSettings = (modelKey?: string | nul
};
}, [modelConfig?.default_settings]);
- return { defaultSettingsDefaults, isLoading };
+ return defaultSettingsDefaults;
};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useInstallModel.ts b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useInstallModel.ts
index 7636b9f314..12141af2a5 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useInstallModel.ts
+++ b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useInstallModel.ts
@@ -1,11 +1,9 @@
import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
-import { useInstallModelMutation } from 'services/api/endpoints/models';
+import { type InstallModelArg, useInstallModelMutation } from 'services/api/endpoints/models';
-type InstallModelArg = {
- source: string;
- inplace?: boolean;
+type InstallModelArgWithCallbacks = InstallModelArg & {
onSuccess?: () => void;
onError?: (error: unknown) => void;
};
@@ -15,8 +13,9 @@ export const useInstallModel = () => {
const [_installModel, request] = useInstallModelMutation();
const installModel = useCallback(
- ({ source, inplace, onSuccess, onError }: InstallModelArg) => {
- _installModel({ source, inplace })
+ ({ source, inplace, config, onSuccess, onError }: InstallModelArgWithCallbacks) => {
+ config ||= {};
+ _installModel({ source, inplace, config })
.unwrap()
.then((_) => {
if (onSuccess) {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts
index 6de99673e4..55ee40ada5 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts
+++ b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts
@@ -1,12 +1,9 @@
-import { skipToken } from '@reduxjs/toolkit/query';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
-import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { isNil } from 'lodash-es';
import { useMemo } from 'react';
-import { useGetModelConfigWithTypeGuard } from 'services/api/hooks/useGetModelConfigWithTypeGuard';
-import { isNonRefinerMainModelConfig } from 'services/api/types';
+import type { MainModelConfig } from 'services/api/types';
const initialStatesSelector = createMemoizedSelector(selectConfigSlice, (config) => {
const { steps, guidance, scheduler, cfgRescaleMultiplier, vaePrecision, width, height } = config.sd;
@@ -22,9 +19,7 @@ const initialStatesSelector = createMemoizedSelector(selectConfigSlice, (config)
};
});
-export const useMainModelDefaultSettings = (modelKey?: string | null) => {
- const { modelConfig, isLoading } = useGetModelConfigWithTypeGuard(modelKey ?? skipToken, isNonRefinerMainModelConfig);
-
+export const useMainModelDefaultSettings = (modelConfig: MainModelConfig) => {
const {
initialSteps,
initialCfg,
@@ -81,5 +76,5 @@ export const useMainModelDefaultSettings = (modelKey?: string | null) => {
initialHeight,
]);
- return { defaultSettingsDefaults, isLoading, optimalDimension: getOptimalDimension(modelConfig) };
+ return defaultSettingsDefaults;
};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx
index 6da320aa0b..101394f85a 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx
@@ -1,5 +1,6 @@
import { Button, Text, useToast } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
+import { $installModelsTab } from 'features/modelManagerV2/subpanels/InstallModels';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { useCallback, useEffect, useState } from 'react';
@@ -44,6 +45,7 @@ const ToastDescription = () => {
const onClick = useCallback(() => {
dispatch(setActiveTab('models'));
+ $installModelsTab.set(3);
toast.close(TOAST_ID);
}, [dispatch, toast]);
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/store/modelManagerV2Slice.ts b/invokeai/frontend/web/src/features/modelManagerV2/store/modelManagerV2Slice.ts
index c637d30fd8..8a7e3a7aa8 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/store/modelManagerV2Slice.ts
+++ b/invokeai/frontend/web/src/features/modelManagerV2/store/modelManagerV2Slice.ts
@@ -1,6 +1,6 @@
import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
-import type { PersistConfig } from 'app/store/store';
+import type { PersistConfig, RootState } from 'app/store/store';
import type { ModelType } from 'services/api/types';
export type FilterableModelType = Exclude | 'refiner';
@@ -50,6 +50,8 @@ export const modelManagerV2Slice = createSlice({
export const { setSelectedModelKey, setSearchTerm, setFilteredModelType, setSelectedModelMode, setScanPath } =
modelManagerV2Slice.actions;
+export const selectModelManagerV2Slice = (state: RootState) => state.modelmanagerV2;
+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrateModelManagerState = (state: any): any => {
if (!('_version' in state)) {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx
index ee5960f7d2..7257c30007 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx
@@ -1,13 +1,13 @@
import { Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import type { ChangeEventHandler } from 'react';
-import { useCallback, useState } from 'react';
+import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyGetHuggingFaceModelsQuery } from 'services/api/endpoints/models';
import { HuggingFaceResults } from './HuggingFaceResults';
-export const HuggingFaceForm = () => {
+export const HuggingFaceForm = memo(() => {
const [huggingFaceRepo, setHuggingFaceRepo] = useState('');
const [displayResults, setDisplayResults] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
@@ -66,4 +66,6 @@ export const HuggingFaceForm = () => {
{data && data.urls && displayResults && }
);
-};
+});
+
+HuggingFaceForm.displayName = 'HuggingFaceForm';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx
index 32970a3666..5edb67eefa 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx
@@ -1,13 +1,13 @@
import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
type Props = {
result: string;
};
-export const HuggingFaceResultItem = ({ result }: Props) => {
+export const HuggingFaceResultItem = memo(({ result }: Props) => {
const { t } = useTranslation();
const [installModel] = useInstallModel();
@@ -27,4 +27,6 @@ export const HuggingFaceResultItem = ({ result }: Props) => {
} onClick={onClick} size="sm" />
);
-};
+});
+
+HuggingFaceResultItem.displayName = 'HuggingFaceResultItem';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx
index 826fd177ea..25546c6822 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx
@@ -11,7 +11,7 @@ import {
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import type { ChangeEventHandler } from 'react';
-import { useCallback, useMemo, useState } from 'react';
+import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
@@ -21,7 +21,7 @@ type HuggingFaceResultsProps = {
results: string[];
};
-export const HuggingFaceResults = ({ results }: HuggingFaceResultsProps) => {
+export const HuggingFaceResults = memo(({ results }: HuggingFaceResultsProps) => {
const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState('');
@@ -93,4 +93,6 @@ export const HuggingFaceResults = ({ results }: HuggingFaceResultsProps) => {
>
);
-};
+});
+
+HuggingFaceResults.displayName = 'HuggingFaceResults';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx
index cc052878bf..64eb725e5e 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx
@@ -1,7 +1,7 @@
import { Button, Checkbox, Flex, FormControl, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import { t } from 'i18next';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
@@ -10,7 +10,7 @@ type SimpleImportModelConfig = {
inplace: boolean;
};
-export const InstallModelForm = () => {
+export const InstallModelForm = memo(() => {
const [installModel, { isLoading }] = useInstallModel();
const { register, handleSubmit, formState, reset } = useForm({
@@ -74,4 +74,6 @@ export const InstallModelForm = () => {
);
-};
+});
+
+InstallModelForm.displayName = 'InstallModelForm';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
index b3544af5b3..c2443dde6b 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
@@ -2,12 +2,12 @@ import { Box, Button, Flex, Heading } from '@invoke-ai/ui-library';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import { useListModelInstallsQuery, usePruneCompletedModelInstallsMutation } from 'services/api/endpoints/models';
import { ModelInstallQueueItem } from './ModelInstallQueueItem';
-export const ModelInstallQueue = () => {
+export const ModelInstallQueue = memo(() => {
const { data } = useListModelInstallsQuery();
const [_pruneCompletedModelInstalls] = usePruneCompletedModelInstallsMutation();
@@ -61,4 +61,6 @@ export const ModelInstallQueue = () => {
);
-};
+});
+
+ModelInstallQueue.displayName = 'ModelInstallQueue';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx
index 82a28b2d75..b14b1fbc4c 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx
@@ -2,7 +2,7 @@ import { Flex, IconButton, Progress, Text, Tooltip } from '@invoke-ai/ui-library
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { isNil } from 'lodash-es';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import { PiXBold } from 'react-icons/pi';
import { useCancelModelInstallMutation } from 'services/api/endpoints/models';
import type { ModelInstallJob } from 'services/api/types';
@@ -25,7 +25,7 @@ const formatBytes = (bytes: number) => {
return `${bytes.toFixed(2)} ${units[i]}`;
};
-export const ModelInstallQueueItem = (props: ModelListItemProps) => {
+export const ModelInstallQueueItem = memo((props: ModelListItemProps) => {
const { installJob } = props;
const [deleteImportModel] = useCancelModelInstallMutation();
@@ -124,7 +124,9 @@ export const ModelInstallQueueItem = (props: ModelListItemProps) => {
/>
);
-};
+});
+
+ModelInstallQueueItem.displayName = 'ModelInstallQueueItem';
type TooltipLabelProps = {
installJob: ModelInstallJob;
@@ -132,7 +134,7 @@ type TooltipLabelProps = {
source: string;
};
-const TooltipLabel = ({ name, source, installJob }: TooltipLabelProps) => {
+const TooltipLabel = memo(({ name, source, installJob }: TooltipLabelProps) => {
const progressString = useMemo(() => {
if (installJob.status !== 'downloading' || installJob.bytes === undefined || installJob.total_bytes === undefined) {
return '';
@@ -156,4 +158,6 @@ const TooltipLabel = ({ name, source, installJob }: TooltipLabelProps) => {
)}
>
);
-};
+});
+
+TooltipLabel.displayName = 'TooltipLabel';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx
index 2a8aec3285..1cd036bf13 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx
@@ -2,13 +2,13 @@ import { Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormLabel,
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setScanPath } from 'features/modelManagerV2/store/modelManagerV2Slice';
import type { ChangeEventHandler } from 'react';
-import { useCallback, useState } from 'react';
+import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyScanFolderQuery } from 'services/api/endpoints/models';
import { ScanModelsResults } from './ScanFolderResults';
-export const ScanModelsForm = () => {
+export const ScanModelsForm = memo(() => {
const scanPath = useAppSelector((state) => state.modelmanagerV2.scanPath);
const dispatch = useAppDispatch();
const [errorMessage, setErrorMessage] = useState('');
@@ -56,4 +56,6 @@ export const ScanModelsForm = () => {
{data && }
);
-};
+});
+
+ScanModelsForm.displayName = 'ScanModelsForm';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx
index 4f2f77470d..9f8c1bdc84 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx
@@ -1,5 +1,5 @@
import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
import type { ScanFolderResponse } from 'services/api/endpoints/models';
@@ -8,7 +8,7 @@ type Props = {
result: ScanFolderResponse[number];
installModel: (source: string) => void;
};
-export const ScanModelResultItem = ({ result, installModel }: Props) => {
+export const ScanModelResultItem = memo(({ result, installModel }: Props) => {
const { t } = useTranslation();
const handleInstall = useCallback(() => {
@@ -30,4 +30,6 @@ export const ScanModelResultItem = ({ result, installModel }: Props) => {
);
-};
+});
+
+ScanModelResultItem.displayName = 'ScanModelResultItem';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx
index 749ef4c8e0..ee8b834a29 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx
@@ -14,7 +14,7 @@ import {
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import type { ChangeEvent, ChangeEventHandler } from 'react';
-import { useCallback, useMemo, useState } from 'react';
+import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
import type { ScanFolderResponse } from 'services/api/endpoints/models';
@@ -25,7 +25,7 @@ type ScanModelResultsProps = {
results: ScanFolderResponse;
};
-export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
+export const ScanModelsResults = memo(({ results }: ScanModelResultsProps) => {
const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState('');
const [inplace, setInplace] = useState(true);
@@ -116,4 +116,6 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
>
);
-};
+});
+
+ScanModelsResults.displayName = 'ScanModelsResults';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx
index 98e1e39640..a3c9c82d0e 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx
@@ -1,7 +1,7 @@
import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
import ModelBaseBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
import type { GetStarterModelsResponse } from 'services/api/endpoints/models';
@@ -9,20 +9,22 @@ import type { GetStarterModelsResponse } from 'services/api/endpoints/models';
type Props = {
result: GetStarterModelsResponse[number];
};
-export const StarterModelsResultItem = ({ result }: Props) => {
+export const StarterModelsResultItem = memo(({ result }: Props) => {
const { t } = useTranslation();
const allSources = useMemo(() => {
- const _allSources = [result.source];
+ const _allSources = [{ source: result.source, config: { name: result.name, description: result.description } }];
if (result.dependencies) {
- _allSources.push(...result.dependencies.map((d) => d.source));
+ for (const d of result.dependencies) {
+ _allSources.push({ source: d.source, config: { name: d.name, description: d.description } });
+ }
}
return _allSources;
}, [result]);
const [installModel] = useInstallModel();
const onClick = useCallback(() => {
- for (const source of allSources) {
- installModel({ source });
+ for (const { config, source } of allSources) {
+ installModel({ config, source });
}
}, [allSources, installModel]);
@@ -30,7 +32,7 @@ export const StarterModelsResultItem = ({ result }: Props) => {
- {result.type.replace('_', ' ')}
+ {result.type.replaceAll('_', ' ')}
{result.name}
@@ -45,4 +47,6 @@ export const StarterModelsResultItem = ({ result }: Props) => {
);
-};
+});
+
+StarterModelsResultItem.displayName = 'StarterModelsResultItem';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx
index 3198f1df78..837ef5c63b 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx
@@ -1,10 +1,11 @@
import { Flex } from '@invoke-ai/ui-library';
import { FetchingModelsLoader } from 'features/modelManagerV2/subpanels/ModelManagerPanel/FetchingModelsLoader';
+import { memo } from 'react';
import { useGetStarterModelsQuery } from 'services/api/endpoints/models';
import { StarterModelsResults } from './StarterModelsResults';
-export const StarterModelsForm = () => {
+export const StarterModelsForm = memo(() => {
const { isLoading, data } = useGetStarterModelsQuery();
return (
@@ -13,4 +14,6 @@ export const StarterModelsForm = () => {
{data && }
);
-};
+});
+
+StarterModelsForm.displayName = 'StarterModelsForm';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx
index 7aa05af300..e593ee5fc3 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx
@@ -1,7 +1,7 @@
import { Flex, IconButton, Input, InputGroup, InputRightElement } from '@invoke-ai/ui-library';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import type { ChangeEventHandler } from 'react';
-import { useCallback, useMemo, useState } from 'react';
+import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
import type { GetStarterModelsResponse } from 'services/api/endpoints/models';
@@ -12,20 +12,30 @@ type StarterModelsResultsProps = {
results: NonNullable;
};
-export const StarterModelsResults = ({ results }: StarterModelsResultsProps) => {
+export const StarterModelsResults = memo(({ results }: StarterModelsResultsProps) => {
const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState('');
const filteredResults = useMemo(() => {
return results.filter((result) => {
- const name = result.name.toLowerCase();
- const type = result.type.toLowerCase();
- return name.includes(searchTerm.toLowerCase()) || type.includes(searchTerm.toLowerCase());
+ const trimmedSearchTerm = searchTerm.trim().toLowerCase();
+ const matchStrings = [
+ result.name.toLowerCase(),
+ result.type.toLowerCase().replaceAll('_', ' '),
+ result.description.toLowerCase(),
+ ];
+ if (result.type === 'spandrel_image_to_image') {
+ matchStrings.push('upscale');
+ matchStrings.push('post-processing');
+ matchStrings.push('postprocessing');
+ matchStrings.push('post processing');
+ }
+ return matchStrings.some((matchString) => matchString.includes(trimmedSearchTerm));
});
}, [results, searchTerm]);
const handleSearch: ChangeEventHandler = useCallback((e) => {
- setSearchTerm(e.target.value.trim());
+ setSearchTerm(e.target.value);
}, []);
const clearSearch = useCallback(() => {
@@ -69,4 +79,6 @@ export const StarterModelsResults = ({ results }: StarterModelsResultsProps) =>
);
-};
+});
+
+StarterModelsResults.displayName = 'StarterModelsResults';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx
index d09ab67fa4..90f3a578da 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx
@@ -1,28 +1,28 @@
import { Box, Flex, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
+import { useStore } from '@nanostores/react';
import { StarterModelsForm } from 'features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm';
-import { useMemo } from 'react';
+import { atom } from 'nanostores';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
-import { useMainModels } from 'services/api/hooks/modelsByType';
import { HuggingFaceForm } from './AddModelPanel/HuggingFaceFolder/HuggingFaceForm';
import { InstallModelForm } from './AddModelPanel/InstallModelForm';
import { ModelInstallQueue } from './AddModelPanel/ModelInstallQueue/ModelInstallQueue';
import { ScanModelsForm } from './AddModelPanel/ScanFolder/ScanFolderForm';
-export const InstallModels = () => {
+export const $installModelsTab = atom(0);
+
+export const InstallModels = memo(() => {
const { t } = useTranslation();
- const [mainModels, { data }] = useMainModels();
- const defaultIndex = useMemo(() => {
- if (data && mainModels.length) {
- return 0;
- }
- return 3;
- }, [data, mainModels.length]);
+ const index = useStore($installModelsTab);
+ const onChange = useCallback((index: number) => {
+ $installModelsTab.set(index);
+ }, []);
return (
{t('modelManager.addModel')}
-
+
{t('modelManager.urlOrLocalPath')}
{t('modelManager.huggingFace')}
@@ -49,4 +49,6 @@ export const InstallModels = () => {
);
-};
+});
+
+InstallModels.displayName = 'InstallModels';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx
index dbe02392db..a07cb8c10b 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx
@@ -1,14 +1,14 @@
import { Button, Flex, Heading } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
import ModelList from './ModelManagerPanel/ModelList';
import { ModelListNavigation } from './ModelManagerPanel/ModelListNavigation';
-export const ModelManager = () => {
+export const ModelManager = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const handleClickAddModel = useCallback(() => {
@@ -29,4 +29,6 @@ export const ModelManager = () => {
);
-};
+});
+
+ModelManager.displayName = 'ModelManager';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx
index b82917221e..755a6e21fb 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx
@@ -21,7 +21,8 @@ import { FetchingModelsLoader } from './FetchingModelsLoader';
import { ModelListWrapper } from './ModelListWrapper';
const ModelList = () => {
- const { searchTerm, filteredModelType } = useAppSelector((s) => s.modelmanagerV2);
+ const filteredModelType = useAppSelector((s) => s.modelmanagerV2.filteredModelType);
+ const searchTerm = useAppSelector((s) => s.modelmanagerV2.searchTerm);
const { t } = useTranslation();
const [mainModels, { isLoading: isLoadingMainModels }] = useMainModels();
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
index a4a6d5c833..8bfcbd7351 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
@@ -1,7 +1,8 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { ConfirmationAlertDialog, Flex, IconButton, Spacer, Text, useDisclosure } from '@invoke-ai/ui-library';
+import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
+import { selectModelManagerV2Slice, setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
import ModelBaseBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
import ModelFormatBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge';
import { toast } from 'features/toast/toast';
@@ -23,15 +24,21 @@ const sx: SystemStyleObject = {
"&[aria-selected='true']": { bg: 'base.700' },
};
-const ModelListItem = (props: ModelListItemProps) => {
+const ModelListItem = ({ model }: ModelListItemProps) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
- const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
+ const selectIsSelected = useMemo(
+ () =>
+ createSelector(
+ selectModelManagerV2Slice,
+ (modelManagerV2Slice) => modelManagerV2Slice.selectedModelKey === model.key
+ ),
+ [model.key]
+ );
+ const isSelected = useAppSelector(selectIsSelected);
const [deleteModel] = useDeleteModelsMutation();
const { isOpen, onOpen, onClose } = useDisclosure();
- const { model } = props;
-
const handleSelectModel = useCallback(() => {
dispatch(setSelectedModelKey(model.key));
}, [model.key, dispatch]);
@@ -43,11 +50,6 @@ const ModelListItem = (props: ModelListItemProps) => {
},
[onOpen]
);
-
- const isSelected = useMemo(() => {
- return selectedModelKey === model.key;
- }, [selectedModelKey, model.key]);
-
const handleModelDelete = useCallback(() => {
deleteModel({ key: model.key })
.unwrap()
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx
index b256e8a70f..766b4f27b8 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx
@@ -3,12 +3,12 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setSearchTerm } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { t } from 'i18next';
import type { ChangeEventHandler } from 'react';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import { PiXBold } from 'react-icons/pi';
import { ModelTypeFilter } from './ModelTypeFilter';
-export const ModelListNavigation = () => {
+export const ModelListNavigation = memo(() => {
const dispatch = useAppDispatch();
const searchTerm = useAppSelector((s) => s.modelmanagerV2.searchTerm);
@@ -49,4 +49,6 @@ export const ModelListNavigation = () => {
);
-};
+});
+
+ModelListNavigation.displayName = 'ModelListNavigation';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx
index e52bd12f25..bde79e4554 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx
@@ -1,4 +1,5 @@
import { StickyScrollable } from 'features/system/components/StickyScrollable';
+import { memo } from 'react';
import type { AnyModelConfig } from 'services/api/types';
import ModelListItem from './ModelListItem';
@@ -8,7 +9,7 @@ type ModelListWrapperProps = {
modelList: AnyModelConfig[];
};
-export const ModelListWrapper = (props: ModelListWrapperProps) => {
+export const ModelListWrapper = memo((props: ModelListWrapperProps) => {
const { title, modelList } = props;
return (
@@ -17,4 +18,6 @@ export const ModelListWrapper = (props: ModelListWrapperProps) => {
))}
);
-};
+});
+
+ModelListWrapper.displayName = 'ModelListWrapper';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelTypeFilter.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelTypeFilter.tsx
index 1a2444870b..9db3334e89 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelTypeFilter.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelTypeFilter.tsx
@@ -2,12 +2,12 @@ import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-libr
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import type { FilterableModelType } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { setFilteredModelType } from 'features/modelManagerV2/store/modelManagerV2Slice';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiFunnelBold } from 'react-icons/pi';
import { objectKeys } from 'tsafe';
-export const ModelTypeFilter = () => {
+export const ModelTypeFilter = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const MODEL_TYPE_LABELS: Record = useMemo(
@@ -57,4 +57,6 @@ export const ModelTypeFilter = () => {
);
-};
+});
+
+ModelTypeFilter.displayName = 'ModelTypeFilter';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx
index 5f1a70e0fe..eb85434d36 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx
@@ -1,14 +1,17 @@
import { Box } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
+import { memo } from 'react';
import { InstallModels } from './InstallModels';
import { Model } from './ModelPanel/Model';
-export const ModelPane = () => {
+export const ModelPane = memo(() => {
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
return (
{selectedModelKey ? : }
);
-};
+});
+
+ModelPane.displayName = 'ModelPane';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx
index 9a84fbc726..25005e76c9 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx
@@ -1,26 +1,28 @@
-import { Button, Flex, Heading, SimpleGrid, Text } from '@invoke-ai/ui-library';
-import { useAppSelector } from 'app/store/storeHooks';
+import { Button, Flex, Heading, SimpleGrid } from '@invoke-ai/ui-library';
import { useControlNetOrT2IAdapterDefaultSettings } from 'features/modelManagerV2/hooks/useControlNetOrT2IAdapterDefaultSettings';
import { DefaultPreprocessor } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor';
import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings';
import { toast } from 'features/toast/toast';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { PiCheckBold } from 'react-icons/pi';
import { useUpdateModelMutation } from 'services/api/endpoints/models';
+import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
export type ControlNetOrT2IAdapterDefaultSettingsFormData = {
preprocessor: FormField;
};
-export const ControlNetOrT2IAdapterDefaultSettings = () => {
- const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
+type Props = {
+ modelConfig: ControlNetModelConfig | T2IAdapterModelConfig;
+};
+
+export const ControlNetOrT2IAdapterDefaultSettings = memo(({ modelConfig }: Props) => {
const { t } = useTranslation();
- const { defaultSettingsDefaults, isLoading: isLoadingDefaultSettings } =
- useControlNetOrT2IAdapterDefaultSettings(selectedModelKey);
+ const defaultSettingsDefaults = useControlNetOrT2IAdapterDefaultSettings(modelConfig);
const [updateModel, { isLoading: isLoadingUpdateModel }] = useUpdateModelMutation();
@@ -30,16 +32,12 @@ export const ControlNetOrT2IAdapterDefaultSettings = () => {
const onSubmit = useCallback>(
(data) => {
- if (!selectedModelKey) {
- return;
- }
-
const body = {
preprocessor: data.preprocessor.isEnabled ? data.preprocessor.value : null,
};
updateModel({
- key: selectedModelKey,
+ key: modelConfig.key,
body: { default_settings: body },
})
.unwrap()
@@ -61,13 +59,9 @@ export const ControlNetOrT2IAdapterDefaultSettings = () => {
}
});
},
- [selectedModelKey, reset, updateModel, t]
+ [updateModel, modelConfig.key, t, reset]
);
- if (isLoadingDefaultSettings) {
- return {t('common.loading')};
- }
-
return (
<>
@@ -89,4 +83,6 @@ export const ControlNetOrT2IAdapterDefaultSettings = () => {
>
);
-};
+});
+
+ControlNetOrT2IAdapterDefaultSettings.displayName = 'ControlNetOrT2IAdapterDefaultSettings';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor.tsx
index b2284336bf..e446992779 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor.tsx
@@ -4,7 +4,7 @@ import { InformationalPopover } from 'common/components/InformationalPopover/Inf
import type { ControlNetOrT2IAdapterDefaultSettingsFormData } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings';
import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -28,7 +28,7 @@ const OPTIONS = [
type DefaultSchedulerType = ControlNetOrT2IAdapterDefaultSettingsFormData['preprocessor'];
-export function DefaultPreprocessor(props: UseControllerProps) {
+export const DefaultPreprocessor = memo((props: UseControllerProps) => {
const { t } = useTranslation();
const { field } = useController(props);
@@ -63,4 +63,6 @@ export function DefaultPreprocessor(props: UseControllerProps
);
-}
+});
+
+DefaultPreprocessor.displayName = 'DefaultPreprocessor';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx
index d16ce1460c..cde1b8d7f3 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx
@@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -11,7 +11,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultCfgRescaleMultiplierType = MainModelDefaultSettingsFormData['cfgRescaleMultiplier'];
-export function DefaultCfgRescaleMultiplier(props: UseControllerProps) {
+export const DefaultCfgRescaleMultiplier = memo((props: UseControllerProps) => {
const { field } = useController(props);
const sliderMin = useAppSelector((s) => s.config.sd.cfgRescaleMultiplier.sliderMin);
@@ -74,4 +74,6 @@ export function DefaultCfgRescaleMultiplier(props: UseControllerProps
);
-}
+});
+
+DefaultCfgRescaleMultiplier.displayName = 'DefaultCfgRescaleMultiplier';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx
index 293261bc35..85d11daba8 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx
@@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -11,7 +11,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultCfgType = MainModelDefaultSettingsFormData['cfgScale'];
-export function DefaultCfgScale(props: UseControllerProps) {
+export const DefaultCfgScale = memo((props: UseControllerProps) => {
const { field } = useController(props);
const sliderMin = useAppSelector((s) => s.config.sd.guidance.sliderMin);
@@ -74,4 +74,6 @@ export function DefaultCfgScale(props: UseControllerProps
);
-}
+});
+
+DefaultCfgScale.displayName = 'DefaultCfgScale';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx
index a19ae26ebe..8dc00e6239 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx
@@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -16,7 +16,7 @@ type Props = {
optimalDimension: number;
};
-export function DefaultHeight({ control, optimalDimension }: Props) {
+export const DefaultHeight = memo(({ control, optimalDimension }: Props) => {
const { field } = useController({ control, name: 'height' });
const sliderMin = useAppSelector((s) => s.config.sd.height.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.height.sliderMax);
@@ -78,4 +78,6 @@ export function DefaultHeight({ control, optimalDimension }: Props) {
);
-}
+});
+
+DefaultHeight.displayName = 'DefaultHeight';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx
index 4397e35a51..1ad8177cf6 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx
@@ -4,7 +4,7 @@ import { InformationalPopover } from 'common/components/InformationalPopover/Inf
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { SCHEDULER_OPTIONS } from 'features/parameters/types/constants';
import { isParameterScheduler } from 'features/parameters/types/parameterSchemas';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -13,7 +13,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultSchedulerType = MainModelDefaultSettingsFormData['scheduler'];
-export function DefaultScheduler(props: UseControllerProps) {
+export const DefaultScheduler = memo((props: UseControllerProps) => {
const { t } = useTranslation();
const { field } = useController(props);
@@ -51,4 +51,6 @@ export function DefaultScheduler(props: UseControllerProps
);
-}
+});
+
+DefaultScheduler.displayName = 'DefaultScheduler';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx
index 9c1912a0f7..5bfb268d08 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx
@@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -11,7 +11,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultSteps = MainModelDefaultSettingsFormData['steps'];
-export function DefaultSteps(props: UseControllerProps) {
+export const DefaultSteps = memo((props: UseControllerProps) => {
const { field } = useController(props);
const sliderMin = useAppSelector((s) => s.config.sd.steps.sliderMin);
@@ -74,4 +74,6 @@ export function DefaultSteps(props: UseControllerProps
);
-}
+});
+
+DefaultSteps.displayName = 'DefaultSteps';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx
index 5517d7b347..cf1578d9f4 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx
@@ -4,7 +4,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -15,7 +15,7 @@ import type { MainModelDefaultSettingsFormData } from './MainModelDefaultSetting
type DefaultVaeType = MainModelDefaultSettingsFormData['vae'];
-export function DefaultVae(props: UseControllerProps) {
+export const DefaultVae = memo((props: UseControllerProps) => {
const { t } = useTranslation();
const { field } = useController(props);
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
@@ -64,4 +64,6 @@ export function DefaultVae(props: UseControllerProps
);
-}
+});
+
+DefaultVae.displayName = 'DefaultVae';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx
index d33cf4e08d..b725d60968 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx
@@ -3,7 +3,7 @@ import { Combobox, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
import { isParameterPrecision } from 'features/parameters/types/parameterSchemas';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -17,7 +17,7 @@ const options = [
type DefaultVaePrecisionType = MainModelDefaultSettingsFormData['vaePrecision'];
-export function DefaultVaePrecision(props: UseControllerProps) {
+export const DefaultVaePrecision = memo((props: UseControllerProps) => {
const { t } = useTranslation();
const { field } = useController(props);
@@ -52,4 +52,6 @@ export function DefaultVaePrecision(props: UseControllerProps
);
-}
+});
+
+DefaultVaePrecision.displayName = 'DefaultVaePrecision';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx
index 851dba1ede..8986b9820b 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx
@@ -2,7 +2,7 @@ import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } f
import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { SettingToggle } from 'features/modelManagerV2/subpanels/ModelPanel/SettingToggle';
-import { useCallback, useMemo } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
@@ -16,7 +16,7 @@ type Props = {
optimalDimension: number;
};
-export function DefaultWidth({ control, optimalDimension }: Props) {
+export const DefaultWidth = memo(({ control, optimalDimension }: Props) => {
const { field } = useController({ control, name: 'width' });
const sliderMin = useAppSelector((s) => s.config.sd.width.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.width.sliderMax);
@@ -78,4 +78,6 @@ export function DefaultWidth({ control, optimalDimension }: Props) {
);
-}
+});
+
+DefaultWidth.displayName = 'DefaultWidth';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx
index 233fc7bc6b..095b452dfc 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx
@@ -1,16 +1,18 @@
-import { Button, Flex, Heading, SimpleGrid, Text } from '@invoke-ai/ui-library';
+import { Button, Flex, Heading, SimpleGrid } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useMainModelDefaultSettings } from 'features/modelManagerV2/hooks/useMainModelDefaultSettings';
import { DefaultHeight } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight';
import { DefaultWidth } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth';
import type { ParameterScheduler } from 'features/parameters/types/parameterSchemas';
+import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { toast } from 'features/toast/toast';
-import { useCallback } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { PiCheckBold } from 'react-icons/pi';
import { useUpdateModelMutation } from 'services/api/endpoints/models';
+import type { MainModelConfig } from 'services/api/types';
import { DefaultCfgRescaleMultiplier } from './DefaultCfgRescaleMultiplier';
import { DefaultCfgScale } from './DefaultCfgScale';
@@ -35,16 +37,16 @@ export type MainModelDefaultSettingsFormData = {
height: FormField;
};
-export const MainModelDefaultSettings = () => {
+type Props = {
+ modelConfig: MainModelConfig;
+};
+
+export const MainModelDefaultSettings = memo(({ modelConfig }: Props) => {
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
const { t } = useTranslation();
- const {
- defaultSettingsDefaults,
- isLoading: isLoadingDefaultSettings,
- optimalDimension,
- } = useMainModelDefaultSettings(selectedModelKey);
-
+ const defaultSettingsDefaults = useMainModelDefaultSettings(modelConfig);
+ const optimalDimension = useMemo(() => getOptimalDimension(modelConfig), [modelConfig]);
const [updateModel, { isLoading: isLoadingUpdateModel }] = useUpdateModelMutation();
const { handleSubmit, control, formState, reset } = useForm({
@@ -94,10 +96,6 @@ export const MainModelDefaultSettings = () => {
[selectedModelKey, reset, updateModel, t]
);
- if (isLoadingDefaultSettings) {
- return {t('common.loading')};
- }
-
return (
<>
@@ -126,4 +124,6 @@ export const MainModelDefaultSettings = () => {
>
);
-};
+});
+
+MainModelDefaultSettings.displayName = 'MainModelDefaultSettings';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx
index fa7ca4c394..2934ac916a 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx
@@ -1,120 +1,47 @@
-import { Button, Flex, Heading, Spacer, Text } from '@invoke-ai/ui-library';
-import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice';
-import { ModelConvertButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton';
-import { ModelEditButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelEditButton';
-import { toast } from 'features/toast/toast';
-import { useCallback } from 'react';
-import type { SubmitHandler } from 'react-hook-form';
-import { useForm } from 'react-hook-form';
+import { useAppSelector } from 'app/store/storeHooks';
+import { IAINoContentFallback, IAINoContentFallbackWithSpinner } from 'common/components/IAIImageFallback';
+import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiCheckBold, PiXBold } from 'react-icons/pi';
-import type { UpdateModelArg } from 'services/api/endpoints/models';
-import { useGetModelConfigQuery, useUpdateModelMutation } from 'services/api/endpoints/models';
+import { PiExclamationMarkBold } from 'react-icons/pi';
+import { modelConfigsAdapterSelectors, useGetModelConfigsQuery } from 'services/api/endpoints/models';
-import ModelImageUpload from './Fields/ModelImageUpload';
import { ModelEdit } from './ModelEdit';
import { ModelView } from './ModelView';
-export const Model = () => {
+export const Model = memo(() => {
const { t } = useTranslation();
const selectedModelMode = useAppSelector((s) => s.modelmanagerV2.selectedModelMode);
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
- const { data, isLoading } = useGetModelConfigQuery(selectedModelKey ?? skipToken);
- const [updateModel, { isLoading: isSubmitting }] = useUpdateModelMutation();
- const dispatch = useAppDispatch();
+ const { data: modelConfigs, isLoading } = useGetModelConfigsQuery();
+ const modelConfig = useMemo(() => {
+ if (!modelConfigs) {
+ return null;
+ }
+ if (selectedModelKey === null) {
+ return null;
+ }
+ const modelConfig = modelConfigsAdapterSelectors.selectById(modelConfigs, selectedModelKey);
- const form = useForm({
- defaultValues: data,
- mode: 'onChange',
- });
+ if (!modelConfig) {
+ return null;
+ }
- const onSubmit = useCallback>(
- (values) => {
- if (!data?.key) {
- return;
- }
-
- const responseBody: UpdateModelArg = {
- key: data.key,
- body: values,
- };
-
- updateModel(responseBody)
- .unwrap()
- .then((payload) => {
- form.reset(payload, { keepDefaultValues: true });
- dispatch(setSelectedModelMode('view'));
- toast({
- id: 'MODEL_UPDATED',
- title: t('modelManager.modelUpdated'),
- status: 'success',
- });
- })
- .catch((_) => {
- form.reset();
- toast({
- id: 'MODEL_UPDATE_FAILED',
- title: t('modelManager.modelUpdateFailed'),
- status: 'error',
- });
- });
- },
- [dispatch, data?.key, form, t, updateModel]
- );
-
- const handleClickCancel = useCallback(() => {
- dispatch(setSelectedModelMode('view'));
- }, [dispatch]);
+ return modelConfig;
+ }, [modelConfigs, selectedModelKey]);
if (isLoading) {
- return {t('common.loading')};
+ return ;
}
- if (!data) {
- return {t('common.somethingWentWrong')};
+ if (!modelConfig) {
+ return ;
}
- return (
-
-
-
-
-
-
- {data.name}
-
-
- {selectedModelMode === 'view' && }
- {selectedModelMode === 'view' && }
- {selectedModelMode === 'edit' && (
- }>
- {t('common.cancel')}
-
- )}
- {selectedModelMode === 'edit' && (
- }
- onClick={form.handleSubmit(onSubmit)}
- isLoading={isSubmitting}
- isDisabled={Boolean(Object.keys(form.formState.errors).length)}
- >
- {t('common.save')}
-
- )}
-
- {data.source && (
-
- {t('modelManager.source')}: {data?.source}
-
- )}
- {data.description}
-
-
- {selectedModelMode === 'view' ? : }
-
- );
-};
+ if (selectedModelMode === 'view') {
+ return ;
+ }
+
+ return ;
+});
+
+Model.displayName = 'Model';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx
index ebdedffebf..106e0a1df0 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx
@@ -1,11 +1,12 @@
import { FormControl, FormLabel, Text } from '@invoke-ai/ui-library';
+import { memo } from 'react';
interface Props {
label: string;
value: string | null | undefined;
}
-export const ModelAttrView = ({ label, value }: Props) => {
+export const ModelAttrView = memo(({ label, value }: Props) => {
return (
{label}
@@ -14,4 +15,6 @@ export const ModelAttrView = ({ label, value }: Props) => {
);
-};
+});
+
+ModelAttrView.displayName = 'ModelAttrView';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx
index 40ffca76b4..70775842f8 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx
@@ -8,52 +8,46 @@ import {
UnorderedList,
useDisclosure,
} from '@invoke-ai/ui-library';
-import { skipToken } from '@reduxjs/toolkit/query';
import { toast } from 'features/toast/toast';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
-import { useConvertModelMutation, useGetModelConfigQuery } from 'services/api/endpoints/models';
+import { useConvertModelMutation } from 'services/api/endpoints/models';
+import type { CheckpointModelConfig } from 'services/api/types';
interface ModelConvertProps {
- modelKey: string | null;
+ modelConfig: CheckpointModelConfig;
}
-export const ModelConvertButton = (props: ModelConvertProps) => {
- const { modelKey } = props;
+export const ModelConvertButton = memo(({ modelConfig }: ModelConvertProps) => {
const { t } = useTranslation();
- const { data } = useGetModelConfigQuery(modelKey ?? skipToken);
const [convertModel, { isLoading }] = useConvertModelMutation();
const { isOpen, onOpen, onClose } = useDisclosure();
const modelConvertHandler = useCallback(() => {
- if (!data || isLoading) {
+ if (!modelConfig || isLoading) {
return;
}
- const toastId = `CONVERTING_MODEL_${data.key}`;
+ const toastId = `CONVERTING_MODEL_${modelConfig.key}`;
toast({
id: toastId,
- title: `${t('modelManager.convertingModelBegin')}: ${data?.name}`,
+ title: `${t('modelManager.convertingModelBegin')}: ${modelConfig.name}`,
status: 'info',
});
- convertModel(data?.key)
+ convertModel(modelConfig.key)
.unwrap()
.then(() => {
- toast({ id: toastId, title: `${t('modelManager.modelConverted')}: ${data?.name}`, status: 'success' });
+ toast({ id: toastId, title: `${t('modelManager.modelConverted')}: ${modelConfig.name}`, status: 'success' });
})
.catch(() => {
toast({
id: toastId,
- title: `${t('modelManager.modelConversionFailed')}: ${data?.name}`,
+ title: `${t('modelManager.modelConversionFailed')}: ${modelConfig.name}`,
status: 'error',
});
});
- }, [data, isLoading, t, convertModel]);
-
- if (data?.format !== 'checkpoint') {
- return;
- }
+ }, [modelConfig, isLoading, t, convertModel]);
return (
<>
@@ -68,7 +62,7 @@ export const ModelConvertButton = (props: ModelConvertProps) => {
🧨 {t('modelManager.convert')}
{
>
);
-};
+});
+
+ModelConvertButton.displayName = 'ModelConvertButton';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx
index 8bc775c872..da57c30238 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx
@@ -1,4 +1,5 @@
import {
+ Button,
Checkbox,
Flex,
FormControl,
@@ -7,96 +8,154 @@ import {
Heading,
Input,
SimpleGrid,
- Text,
Textarea,
} from '@invoke-ai/ui-library';
-import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppSelector } from 'app/store/storeHooks';
-import type { SubmitHandler, UseFormReturn } from 'react-hook-form';
+import { useAppDispatch } from 'app/store/storeHooks';
+import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice';
+import { ModelHeader } from 'features/modelManagerV2/subpanels/ModelPanel/ModelHeader';
+import { toast } from 'features/toast/toast';
+import { memo, useCallback } from 'react';
+import { type SubmitHandler, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
-import type { UpdateModelArg } from 'services/api/endpoints/models';
-import { useGetModelConfigQuery } from 'services/api/endpoints/models';
+import { PiCheckBold, PiXBold } from 'react-icons/pi';
+import { type UpdateModelArg, useUpdateModelMutation } from 'services/api/endpoints/models';
+import type { AnyModelConfig } from 'services/api/types';
import BaseModelSelect from './Fields/BaseModelSelect';
import ModelVariantSelect from './Fields/ModelVariantSelect';
import PredictionTypeSelect from './Fields/PredictionTypeSelect';
type Props = {
- form: UseFormReturn;
- onSubmit: SubmitHandler;
+ modelConfig: AnyModelConfig;
};
const stringFieldOptions = {
validate: (value?: string | null) => (value && value.trim().length > 3) || 'Must be at least 3 characters',
};
-export const ModelEdit = ({ form }: Props) => {
- const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
- const { data, isLoading } = useGetModelConfigQuery(selectedModelKey ?? skipToken);
+export const ModelEdit = memo(({ modelConfig }: Props) => {
const { t } = useTranslation();
+ const [updateModel, { isLoading: isSubmitting }] = useUpdateModelMutation();
+ const dispatch = useAppDispatch();
- if (isLoading) {
- return {t('common.loading')};
- }
+ const form = useForm({
+ defaultValues: modelConfig,
+ mode: 'onChange',
+ });
- if (!data) {
- return {t('common.somethingWentWrong')};
- }
+ const onSubmit = useCallback>(
+ (values) => {
+ const responseBody: UpdateModelArg = {
+ key: modelConfig.key,
+ body: values,
+ };
+
+ updateModel(responseBody)
+ .unwrap()
+ .then((payload) => {
+ form.reset(payload, { keepDefaultValues: true });
+ dispatch(setSelectedModelMode('view'));
+ toast({
+ id: 'MODEL_UPDATED',
+ title: t('modelManager.modelUpdated'),
+ status: 'success',
+ });
+ })
+ .catch((_) => {
+ form.reset();
+ toast({
+ id: 'MODEL_UPDATE_FAILED',
+ title: t('modelManager.modelUpdateFailed'),
+ status: 'error',
+ });
+ });
+ },
+ [dispatch, modelConfig.key, form, t, updateModel]
+ );
+
+ const handleClickCancel = useCallback(() => {
+ dispatch(setSelectedModelMode('view'));
+ }, [dispatch]);
return (
-
-
+
);
-};
+});
+
+ModelEdit.displayName = 'ModelEdit';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx
index 1d0f506282..d81c443148 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx
@@ -1,11 +1,11 @@
import { Button } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice';
-import { useCallback } from 'react';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { IoPencil } from 'react-icons/io5';
-export const ModelEditButton = () => {
+export const ModelEditButton = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
@@ -18,4 +18,6 @@ export const ModelEditButton = () => {
{t('modelManager.edit')}
);
-};
+});
+
+ModelEditButton.displayName = 'ModelEditButton';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx
new file mode 100644
index 0000000000..a30f96b7fc
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx
@@ -0,0 +1,36 @@
+import { Flex, Heading, Spacer, Text } from '@invoke-ai/ui-library';
+import ModelImageUpload from 'features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload';
+import type { PropsWithChildren } from 'react';
+import { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+import type { AnyModelConfig } from 'services/api/types';
+
+type Props = PropsWithChildren<{
+ modelConfig: AnyModelConfig;
+}>;
+
+export const ModelHeader = memo(({ modelConfig, children }: Props) => {
+ const { t } = useTranslation();
+ return (
+
+
+
+
+
+ {modelConfig.name}
+
+
+ {children}
+
+ {modelConfig.source && (
+
+ {t('modelManager.source')}: {modelConfig.source}
+
+ )}
+ {modelConfig.description}
+
+
+ );
+});
+
+ModelHeader.displayName = 'ModelHeader';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
index 83ae94c9bb..f02a84efb3 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
@@ -1,55 +1,67 @@
-import { Box, Flex, SimpleGrid, Text } from '@invoke-ai/ui-library';
-import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppSelector } from 'app/store/storeHooks';
+import { Box, Flex, SimpleGrid } from '@invoke-ai/ui-library';
import { ControlNetOrT2IAdapterDefaultSettings } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings';
+import { ModelConvertButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton';
+import { ModelEditButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelEditButton';
+import { ModelHeader } from 'features/modelManagerV2/subpanels/ModelPanel/ModelHeader';
import { TriggerPhrases } from 'features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases';
+import { memo } from 'react';
import { useTranslation } from 'react-i18next';
-import { useGetModelConfigQuery } from 'services/api/endpoints/models';
+import type { AnyModelConfig } from 'services/api/types';
import { MainModelDefaultSettings } from './MainModelDefaultSettings/MainModelDefaultSettings';
import { ModelAttrView } from './ModelAttrView';
-export const ModelView = () => {
+type Props = {
+ modelConfig: AnyModelConfig;
+};
+
+export const ModelView = memo(({ modelConfig }: Props) => {
const { t } = useTranslation();
- const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
- const { data, isLoading } = useGetModelConfigQuery(selectedModelKey ?? skipToken);
-
- if (isLoading) {
- return {t('common.loading')};
- }
-
- if (!data) {
- return {t('common.somethingWentWrong')};
- }
return (
-
-
-
-
-
-
-
- {data.type === 'main' && }
- {data.type === 'main' && data.format === 'diffusers' && data.repo_variant && (
-
+
+
+ {modelConfig.format === 'checkpoint' && modelConfig.type === 'main' && (
+
+ )}
+
+
+
+
+
+
+
+
+
+ {modelConfig.type === 'main' && (
+
+ )}
+ {modelConfig.type === 'main' && modelConfig.format === 'diffusers' && modelConfig.repo_variant && (
+
+ )}
+ {modelConfig.type === 'main' && modelConfig.format === 'checkpoint' && (
+ <>
+
+
+
+ >
+ )}
+ {modelConfig.type === 'ip_adapter' && modelConfig.format === 'invokeai' && (
+
+ )}
+
+
+
+ {modelConfig.type === 'main' && modelConfig.base !== 'sdxl-refiner' && (
+
)}
- {data.type === 'main' && data.format === 'checkpoint' && (
- <>
-
-
-
- >
+ {(modelConfig.type === 'controlnet' || modelConfig.type === 't2i_adapter') && (
+
)}
- {data.type === 'ip_adapter' && data.format === 'invokeai' && (
-
- )}
-
-
-
- {data.type === 'main' && data.base !== 'sdxl-refiner' && }
- {(data.type === 'controlnet' || data.type === 't2i_adapter') && }
- {(data.type === 'main' || data.type === 'lora') && }
-
+ {(modelConfig.type === 'main' || modelConfig.type === 'lora') && }
+
+
);
-};
+});
+
+ModelView.displayName = 'ModelView';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx
index 15e4693c4f..45aeab2298 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx
@@ -1,4 +1,4 @@
-import { Switch } from '@invoke-ai/ui-library';
+import { Switch, typedMemo } from '@invoke-ai/ui-library';
import type { ChangeEvent } from 'react';
import { useCallback, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
@@ -6,7 +6,7 @@ import { useController } from 'react-hook-form';
import type { FormField } from './MainModelDefaultSettings/MainModelDefaultSettings';
-export function SettingToggle>>(props: UseControllerProps) {
+export const SettingToggle = typedMemo(>>(props: UseControllerProps) => {
const { field } = useController(props);
const value = useMemo(() => {
@@ -25,4 +25,6 @@ export function SettingToggle>>(props:
);
return ;
-}
+});
+
+SettingToggle.displayName = 'SettingToggle';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx
index 9352d7996c..3f5133dedf 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx
@@ -9,19 +9,19 @@ import {
TagCloseButton,
TagLabel,
} from '@invoke-ai/ui-library';
-import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppSelector } from 'app/store/storeHooks';
import type { ChangeEvent } from 'react';
-import { useCallback, useMemo, useState } from 'react';
+import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
-import { useGetModelConfigQuery, useUpdateModelMutation } from 'services/api/endpoints/models';
-import { isLoRAModelConfig, isNonRefinerMainModelConfig } from 'services/api/types';
+import { useUpdateModelMutation } from 'services/api/endpoints/models';
+import type { LoRAModelConfig, MainModelConfig } from 'services/api/types';
-export const TriggerPhrases = () => {
+type Props = {
+ modelConfig: MainModelConfig | LoRAModelConfig;
+};
+
+export const TriggerPhrases = memo(({ modelConfig }: Props) => {
const { t } = useTranslation();
- const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
- const { currentData: modelConfig } = useGetModelConfigQuery(selectedModelKey ?? skipToken);
const [phrase, setPhrase] = useState('');
const [updateModel, { isLoading }] = useUpdateModelMutation();
@@ -31,9 +31,6 @@ export const TriggerPhrases = () => {
}, []);
const triggerPhrases = useMemo(() => {
- if (!modelConfig || (!isNonRefinerMainModelConfig(modelConfig) && !isLoRAModelConfig(modelConfig))) {
- return [];
- }
return modelConfig?.trigger_phrases || [];
}, [modelConfig]);
@@ -48,10 +45,6 @@ export const TriggerPhrases = () => {
}, [phrase, triggerPhrases]);
const addTriggerPhrase = useCallback(async () => {
- if (!selectedModelKey) {
- return;
- }
-
if (!phrase.length || triggerPhrases.includes(phrase)) {
return;
}
@@ -59,22 +52,18 @@ export const TriggerPhrases = () => {
setPhrase('');
await updateModel({
- key: selectedModelKey,
+ key: modelConfig.key,
body: { trigger_phrases: [...triggerPhrases, phrase] },
}).unwrap();
- }, [updateModel, selectedModelKey, phrase, triggerPhrases]);
+ }, [phrase, triggerPhrases, updateModel, modelConfig.key]);
const removeTriggerPhrase = useCallback(
async (phraseToRemove: string) => {
- if (!selectedModelKey) {
- return;
- }
-
const filteredPhrases = triggerPhrases.filter((p) => p !== phraseToRemove);
- await updateModel({ key: selectedModelKey, body: { trigger_phrases: filteredPhrases } }).unwrap();
+ await updateModel({ key: modelConfig.key, body: { trigger_phrases: filteredPhrases } }).unwrap();
},
- [updateModel, selectedModelKey, triggerPhrases]
+ [triggerPhrases, updateModel, modelConfig]
);
const onTriggerPhraseAddFormSubmit = useCallback(
@@ -103,7 +92,9 @@ export const TriggerPhrases = () => {
{t('common.add')}
- {!!errors.length && errors.map((error) => {error})}
+ {errors.map((error) => (
+ {error}
+ ))}
@@ -118,4 +109,6 @@ export const TriggerPhrases = () => {
);
-};
+});
+
+TriggerPhrases.displayName = 'TriggerPhrases';
diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts b/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts
index 32db806cde..2a2bfde904 100644
--- a/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts
+++ b/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts
@@ -59,17 +59,19 @@ const pasteSelection = (withEdgesToCopiedNodes?: boolean) => {
for (const edge of copiedEdges) {
if (edge.source === node.id) {
edge.source = id;
- edge.id = edge.id.replace(node.data.id, id);
- }
- if (edge.target === node.id) {
+ } else if (edge.target === node.id) {
edge.target = id;
- edge.id = edge.id.replace(node.data.id, id);
}
}
node.id = id;
node.data.id = id;
});
+ copiedEdges.forEach((edge) => {
+ // Copied edges need a fresh id too
+ edge.id = uuidv4();
+ });
+
const nodeChanges: NodeChange[] = [];
const edgeChanges: EdgeChange[] = [];
// Deselect existing nodes
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocPostProcessingGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocPostProcessingGraph.ts
new file mode 100644
index 0000000000..7e943500cb
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocPostProcessingGraph.ts
@@ -0,0 +1,40 @@
+import type { RootState } from 'app/store/store';
+import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
+import type { GraphType } from 'features/nodes/util/graph/generation/Graph';
+import { Graph } from 'features/nodes/util/graph/generation/Graph';
+import { getBoardField } from 'features/nodes/util/graph/graphBuilderUtils';
+import type { ImageDTO } from 'services/api/types';
+import { isSpandrelImageToImageModelConfig } from 'services/api/types';
+import { assert } from 'tsafe';
+
+import { getModelMetadataField } from './canvas/metadata';
+import { SPANDREL } from './constants';
+
+type Arg = {
+ image: ImageDTO;
+ state: RootState;
+};
+
+export const buildAdHocPostProcessingGraph = async ({ image, state }: Arg): Promise => {
+ const { postProcessingModel } = state.upscale;
+
+ assert(postProcessingModel, 'No post-processing model found in state');
+
+ const g = new Graph('adhoc-post-processing-graph');
+ g.addNode({
+ id: SPANDREL,
+ type: 'spandrel_image_to_image',
+ image_to_image_model: postProcessingModel,
+ image,
+ board: getBoardField(state),
+ is_intermediate: false,
+ });
+
+ const modelConfig = await fetchModelConfigWithTypeGuard(postProcessingModel.key, isSpandrelImageToImageModelConfig);
+
+ g.upsertMetadata({
+ upscale_model: getModelMetadataField(modelConfig),
+ });
+
+ return g.getGraph();
+};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocUpscaleGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocUpscaleGraph.ts
deleted file mode 100644
index 60343c5e89..0000000000
--- a/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocUpscaleGraph.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { RootState } from 'app/store/store';
-import { getBoardField } from 'features/nodes/util/graph/graphBuilderUtils';
-import type { Graph, Invocation, NonNullableGraph } from 'services/api/types';
-
-import { addCoreMetadataNode, upsertMetadata } from './canvas/metadata';
-import { ESRGAN } from './constants';
-
-type Arg = {
- image_name: string;
- state: RootState;
-};
-
-export const buildAdHocUpscaleGraph = ({ image_name, state }: Arg): Graph => {
- const { esrganModelName } = state.postprocessing;
-
- const realesrganNode: Invocation<'esrgan'> = {
- id: ESRGAN,
- type: 'esrgan',
- image: { image_name },
- model_name: esrganModelName,
- is_intermediate: false,
- board: getBoardField(state),
- };
-
- const graph: NonNullableGraph = {
- id: `adhoc-esrgan-graph`,
- nodes: {
- [ESRGAN]: realesrganNode,
- },
- edges: [],
- };
-
- addCoreMetadataNode(graph, {}, ESRGAN);
- upsertMetadata(graph, {
- esrgan_model: esrganModelName,
- });
-
- return graph;
-};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts
new file mode 100644
index 0000000000..551cc45113
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts
@@ -0,0 +1,246 @@
+import type { RootState } from 'app/store/store';
+import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
+import type { GraphType } from 'features/nodes/util/graph/generation/Graph';
+import { Graph } from 'features/nodes/util/graph/generation/Graph';
+import { isNonRefinerMainModelConfig, isSpandrelImageToImageModelConfig } from 'services/api/types';
+import { assert } from 'tsafe';
+
+import {
+ CLIP_SKIP,
+ CONTROL_NET_COLLECT,
+ IMAGE_TO_LATENTS,
+ LATENTS_TO_IMAGE,
+ MAIN_MODEL_LOADER,
+ NEGATIVE_CONDITIONING,
+ NOISE,
+ POSITIVE_CONDITIONING,
+ SDXL_MODEL_LOADER,
+ SPANDREL,
+ TILED_MULTI_DIFFUSION_DENOISE_LATENTS,
+ UNSHARP_MASK,
+ VAE_LOADER,
+} from './constants';
+import { addLoRAs } from './generation/addLoRAs';
+import { addSDXLLoRas } from './generation/addSDXLLoRAs';
+import { getBoardField, getSDXLStylePrompts } from './graphBuilderUtils';
+
+export const buildMultidiffusionUpscaleGraph = async (state: RootState): Promise => {
+ const { model, cfgScale: cfg_scale, scheduler, steps, vaePrecision, seed, vae } = state.generation;
+ const { positivePrompt, negativePrompt } = state.controlLayers.present;
+ const { upscaleModel, upscaleInitialImage, structure, creativity, tileControlnetModel, scale } = state.upscale;
+
+ assert(model, 'No model found in state');
+ assert(upscaleModel, 'No upscale model found in state');
+ assert(upscaleInitialImage, 'No initial image found in state');
+ assert(tileControlnetModel, 'Tile controlnet is required');
+
+ const g = new Graph();
+
+ const upscaleNode = g.addNode({
+ id: SPANDREL,
+ type: 'spandrel_image_to_image_autoscale',
+ image: upscaleInitialImage,
+ image_to_image_model: upscaleModel,
+ fit_to_multiple_of_8: true,
+ scale,
+ });
+
+ const unsharpMaskNode2 = g.addNode({
+ id: `${UNSHARP_MASK}_2`,
+ type: 'unsharp_mask',
+ radius: 2,
+ strength: 60,
+ });
+
+ g.addEdge(upscaleNode, 'image', unsharpMaskNode2, 'image');
+
+ const noiseNode = g.addNode({
+ id: NOISE,
+ type: 'noise',
+ seed,
+ });
+
+ g.addEdge(unsharpMaskNode2, 'width', noiseNode, 'width');
+ g.addEdge(unsharpMaskNode2, 'height', noiseNode, 'height');
+
+ const i2lNode = g.addNode({
+ id: IMAGE_TO_LATENTS,
+ type: 'i2l',
+ fp32: vaePrecision === 'fp32',
+ tiled: true,
+ });
+
+ g.addEdge(unsharpMaskNode2, 'image', i2lNode, 'image');
+
+ const l2iNode = g.addNode({
+ type: 'l2i',
+ id: LATENTS_TO_IMAGE,
+ fp32: vaePrecision === 'fp32',
+ tiled: true,
+ board: getBoardField(state),
+ is_intermediate: false,
+ });
+
+ const tiledMultidiffusionNode = g.addNode({
+ id: TILED_MULTI_DIFFUSION_DENOISE_LATENTS,
+ type: 'tiled_multi_diffusion_denoise_latents',
+ tile_height: 1024, // is this dependent on base model
+ tile_width: 1024, // is this dependent on base model
+ tile_overlap: 128,
+ steps,
+ cfg_scale,
+ scheduler,
+ denoising_start: ((creativity * -1 + 10) * 4.99) / 100,
+ denoising_end: 1,
+ });
+
+ let posCondNode;
+ let negCondNode;
+ let modelNode;
+
+ if (model.base === 'sdxl') {
+ const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state);
+
+ posCondNode = g.addNode({
+ type: 'sdxl_compel_prompt',
+ id: POSITIVE_CONDITIONING,
+ prompt: positivePrompt,
+ style: positiveStylePrompt,
+ });
+ negCondNode = g.addNode({
+ type: 'sdxl_compel_prompt',
+ id: NEGATIVE_CONDITIONING,
+ prompt: negativePrompt,
+ style: negativeStylePrompt,
+ });
+ modelNode = g.addNode({
+ type: 'sdxl_model_loader',
+ id: SDXL_MODEL_LOADER,
+ model,
+ });
+ g.addEdge(modelNode, 'clip', posCondNode, 'clip');
+ g.addEdge(modelNode, 'clip', negCondNode, 'clip');
+ g.addEdge(modelNode, 'clip2', posCondNode, 'clip2');
+ g.addEdge(modelNode, 'clip2', negCondNode, 'clip2');
+ g.addEdge(modelNode, 'unet', tiledMultidiffusionNode, 'unet');
+ addSDXLLoRas(state, g, tiledMultidiffusionNode, modelNode, null, posCondNode, negCondNode);
+
+ const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
+
+ g.upsertMetadata({
+ cfg_scale,
+ positive_prompt: positivePrompt,
+ negative_prompt: negativePrompt,
+ positive_style_prompt: positiveStylePrompt,
+ negative_style_prompt: negativeStylePrompt,
+ model: Graph.getModelMetadataField(modelConfig),
+ seed,
+ steps,
+ scheduler,
+ vae: vae ?? undefined,
+ });
+ } else {
+ posCondNode = g.addNode({
+ type: 'compel',
+ id: POSITIVE_CONDITIONING,
+ prompt: positivePrompt,
+ });
+ negCondNode = g.addNode({
+ type: 'compel',
+ id: NEGATIVE_CONDITIONING,
+ prompt: negativePrompt,
+ });
+ modelNode = g.addNode({
+ type: 'main_model_loader',
+ id: MAIN_MODEL_LOADER,
+ model,
+ });
+ const clipSkipNode = g.addNode({
+ type: 'clip_skip',
+ id: CLIP_SKIP,
+ });
+
+ g.addEdge(modelNode, 'clip', clipSkipNode, 'clip');
+ g.addEdge(clipSkipNode, 'clip', posCondNode, 'clip');
+ g.addEdge(clipSkipNode, 'clip', negCondNode, 'clip');
+ g.addEdge(modelNode, 'unet', tiledMultidiffusionNode, 'unet');
+ addLoRAs(state, g, tiledMultidiffusionNode, modelNode, null, clipSkipNode, posCondNode, negCondNode);
+
+ const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
+ const upscaleModelConfig = await fetchModelConfigWithTypeGuard(upscaleModel.key, isSpandrelImageToImageModelConfig);
+
+ g.upsertMetadata({
+ cfg_scale,
+ positive_prompt: positivePrompt,
+ negative_prompt: negativePrompt,
+ model: Graph.getModelMetadataField(modelConfig),
+ seed,
+ steps,
+ scheduler,
+ vae: vae ?? undefined,
+ upscale_model: Graph.getModelMetadataField(upscaleModelConfig),
+ creativity,
+ structure,
+ });
+ }
+
+ g.setMetadataReceivingNode(l2iNode);
+ g.addEdgeToMetadata(upscaleNode, 'width', 'width');
+ g.addEdgeToMetadata(upscaleNode, 'height', 'height');
+
+ let vaeNode;
+ if (vae) {
+ vaeNode = g.addNode({
+ id: VAE_LOADER,
+ type: 'vae_loader',
+ vae_model: vae,
+ });
+ }
+
+ g.addEdge(vaeNode || modelNode, 'vae', i2lNode, 'vae');
+ g.addEdge(vaeNode || modelNode, 'vae', l2iNode, 'vae');
+
+ g.addEdge(noiseNode, 'noise', tiledMultidiffusionNode, 'noise');
+ g.addEdge(i2lNode, 'latents', tiledMultidiffusionNode, 'latents');
+ g.addEdge(posCondNode, 'conditioning', tiledMultidiffusionNode, 'positive_conditioning');
+ g.addEdge(negCondNode, 'conditioning', tiledMultidiffusionNode, 'negative_conditioning');
+
+ g.addEdge(tiledMultidiffusionNode, 'latents', l2iNode, 'latents');
+
+ const controlnetNode1 = g.addNode({
+ id: 'controlnet_1',
+ type: 'controlnet',
+ control_model: tileControlnetModel,
+ control_mode: 'balanced',
+ resize_mode: 'just_resize',
+ control_weight: (structure + 10) * 0.0325 + 0.3,
+ begin_step_percent: 0,
+ end_step_percent: (structure + 10) * 0.025 + 0.3,
+ });
+
+ g.addEdge(unsharpMaskNode2, 'image', controlnetNode1, 'image');
+
+ const controlnetNode2 = g.addNode({
+ id: 'controlnet_2',
+ type: 'controlnet',
+ control_model: tileControlnetModel,
+ control_mode: 'balanced',
+ resize_mode: 'just_resize',
+ control_weight: ((structure + 10) * 0.0325 + 0.15) * 0.45,
+ begin_step_percent: (structure + 10) * 0.025 + 0.3,
+ end_step_percent: 0.85,
+ });
+
+ g.addEdge(unsharpMaskNode2, 'image', controlnetNode2, 'image');
+
+ const collectNode = g.addNode({
+ id: CONTROL_NET_COLLECT,
+ type: 'collect',
+ });
+ g.addEdge(controlnetNode1, 'control', collectNode, 'item');
+ g.addEdge(controlnetNode2, 'control', collectNode, 'item');
+
+ g.addEdge(collectNode, 'collection', tiledMultidiffusionNode, 'control');
+
+ return g.getGraph();
+};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/constants.ts b/invokeai/frontend/web/src/features/nodes/util/graph/constants.ts
index 53d7d742ab..5bc2192f53 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/constants.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/constants.ts
@@ -36,7 +36,7 @@ export const CONTROL_NET_COLLECT = 'control_net_collect';
export const IP_ADAPTER_COLLECT = 'ip_adapter_collect';
export const T2I_ADAPTER_COLLECT = 't2i_adapter_collect';
export const METADATA = 'core_metadata';
-export const ESRGAN = 'esrgan';
+export const SPANDREL = 'spandrel';
export const SDXL_MODEL_LOADER = 'sdxl_model_loader';
export const SDXL_DENOISE_LATENTS = 'sdxl_denoise_latents';
export const SDXL_REFINER_MODEL_LOADER = 'sdxl_refiner_model_loader';
@@ -53,6 +53,8 @@ export const PROMPT_REGION_NEGATIVE_COND_PREFIX = 'prompt_region_negative_cond';
export const PROMPT_REGION_POSITIVE_COND_INVERTED_PREFIX = 'prompt_region_positive_cond_inverted';
export const POSITIVE_CONDITIONING_COLLECT = 'positive_conditioning_collect';
export const NEGATIVE_CONDITIONING_COLLECT = 'negative_conditioning_collect';
+export const UNSHARP_MASK = 'unsharp_mask';
+export const TILED_MULTI_DIFFUSION_DENOISE_LATENTS = 'tiled_multi_diffusion_denoise_latents';
// friendly graph ids
export const CONTROL_LAYERS_GRAPH = 'control_layers_graph';
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts
index a8be96e484..e5a97bb50b 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts
@@ -522,6 +522,21 @@ describe('Graph', () => {
});
});
+ describe('addEdgeToMetadata', () => {
+ it('should add an edge to the metadata node', () => {
+ const g = new Graph();
+ const n1 = g.addNode({
+ id: 'n1',
+ type: 'img_resize',
+ });
+ g.upsertMetadata({ test: 'test' });
+ g.addEdgeToMetadata(n1, 'width', 'width');
+ const metadata = g._getMetadataNode();
+ expect(g.getEdgesFrom(n1).length).toBe(1);
+ expect(g.getEdgesTo(metadata as unknown as AnyInvocation).length).toBe(1);
+ });
+ });
+
describe('setMetadataReceivingNode', () => {
it('should set the metadata receiving node', () => {
const g = new Graph();
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts
index 008f86918a..41142e5628 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts
@@ -372,6 +372,21 @@ export class Graph {
return metadataNode;
}
+ /**
+ * Adds an edge from a node to a metadata field. Use this when the metadata value is dynamic depending on a node.
+ * @param fromNode The node to add an edge from
+ * @param fromField The field of the node to add an edge from
+ * @param metadataField The metadata field to add an edge to (will overwrite hard-coded metadata)
+ * @returns
+ */
+ addEdgeToMetadata(
+ fromNode: TFrom,
+ fromField: OutputFields,
+ metadataField: string
+ ): Edge {
+ // @ts-expect-error `Graph` excludes `core_metadata` nodes due to its excessively wide typing
+ return this.addEdge(fromNode, fromField, this._getMetadataNode(), metadataField);
+ }
/**
* Set the node that should receive metadata. All other edges from the metadata node are deleted.
* @param node The node to set as the receiving node
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts
index 3623343367..3335e0f80d 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts
@@ -8,7 +8,7 @@ import type { Invocation, S } from 'services/api/types';
export const addLoRAs = (
state: RootState,
g: Graph,
- denoise: Invocation<'denoise_latents'>,
+ denoise: Invocation<'denoise_latents'> | Invocation<'tiled_multi_diffusion_denoise_latents'>,
modelLoader: Invocation<'main_model_loader'>,
seamless: Invocation<'seamless'> | null,
clipSkip: Invocation<'clip_skip'>,
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts
index f38e8de570..3125ab5ac3 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts
@@ -8,7 +8,7 @@ import type { Invocation, S } from 'services/api/types';
export const addSDXLLoRas = (
state: RootState,
g: Graph,
- denoise: Invocation<'denoise_latents'>,
+ denoise: Invocation<'denoise_latents'> | Invocation<'tiled_multi_diffusion_denoise_latents'>,
modelLoader: Invocation<'sdxl_model_loader'>,
seamless: Invocation<'seamless'> | null,
posCond: Invocation<'sdxl_compel_prompt'>,
diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
index 26bf80af7b..c9410dd7a4 100644
--- a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
@@ -51,7 +51,7 @@ const ParamMainModelSelect = () => {
{t('modelManager.model')}
-
+
{
+ const { t } = useTranslation();
+ const [modelConfigs, { isLoading }] = useSpandrelImageToImageModels();
+
+ const model = useAppSelector((s) => s.upscale.postProcessingModel);
+ const dispatch = useAppDispatch();
+
+ const tooltipLabel = useMemo(() => {
+ if (!modelConfigs.length || !model) {
+ return;
+ }
+ return modelConfigs.find((m) => m.key === model?.key)?.description;
+ }, [modelConfigs, model]);
+
+ const _onChange = useCallback(
+ (v: SpandrelImageToImageModelConfig | null) => {
+ dispatch(postProcessingModelChanged(v));
+ },
+ [dispatch]
+ );
+
+ const { options, value, onChange, placeholder, noOptionsMessage } = useModelCombobox({
+ modelConfigs,
+ onChange: _onChange,
+ selectedModel: model,
+ isLoading,
+ });
+
+ return (
+
+ {t('upscaling.postProcessingModel')}
+
+
+
+
+
+
+ );
+};
+
+export default memo(ParamPostProcessingModel);
diff --git a/invokeai/frontend/web/src/features/parameters/components/PostProcessing/PostProcessingPopover.tsx b/invokeai/frontend/web/src/features/parameters/components/PostProcessing/PostProcessingPopover.tsx
new file mode 100644
index 0000000000..e9092fbee8
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/PostProcessing/PostProcessingPopover.tsx
@@ -0,0 +1,90 @@
+import {
+ Button,
+ Flex,
+ IconButton,
+ Popover,
+ PopoverBody,
+ PopoverContent,
+ PopoverTrigger,
+ Text,
+ useDisclosure,
+} from '@invoke-ai/ui-library';
+import { adHocPostProcessingRequested } from 'app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { $installModelsTab } from 'features/modelManagerV2/subpanels/InstallModels';
+import ParamPostProcessingModel from 'features/parameters/components/PostProcessing/ParamPostProcessingModel';
+import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
+import { setActiveTab } from 'features/ui/store/uiSlice';
+import { memo, useCallback } from 'react';
+import { Trans, useTranslation } from 'react-i18next';
+import { PiFrameCornersBold } from 'react-icons/pi';
+import type { ImageDTO } from 'services/api/types';
+
+type Props = { imageDTO?: ImageDTO };
+
+export const PostProcessingPopover = memo((props: Props) => {
+ const { imageDTO } = props;
+ const dispatch = useAppDispatch();
+ const { postProcessingModel } = useAppSelector((s) => s.upscale);
+ const inProgress = useIsQueueMutationInProgress();
+ const { t } = useTranslation();
+ const { isOpen, onOpen, onClose } = useDisclosure();
+
+ const handleClickUpscale = useCallback(() => {
+ onClose();
+ if (!imageDTO) {
+ return;
+ }
+ dispatch(adHocPostProcessingRequested({ imageDTO }));
+ }, [dispatch, imageDTO, onClose]);
+
+ return (
+
+
+ }
+ aria-label={t('parameters.postProcessing')}
+ />
+
+
+
+
+
+ {!postProcessingModel && }
+
+
+
+
+
+ );
+});
+
+PostProcessingPopover.displayName = 'PostProcessingPopover';
+
+const MissingModelWarning = () => {
+ const dispatch = useAppDispatch();
+
+ const handleGoToModelManager = useCallback(() => {
+ dispatch(setActiveTab('models'));
+ $installModelsTab.set(3);
+ }, [dispatch]);
+
+ return (
+
+
+
+ ),
+ }}
+ />
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamCreativity.tsx b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamCreativity.tsx
new file mode 100644
index 0000000000..0304a732cb
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamCreativity.tsx
@@ -0,0 +1,55 @@
+import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
+import { creativityChanged } from 'features/parameters/store/upscaleSlice';
+import { memo, useCallback, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+
+const ParamCreativity = () => {
+ const creativity = useAppSelector((s) => s.upscale.creativity);
+ const initial = 0;
+ const sliderMin = -10;
+ const sliderMax = 10;
+ const numberInputMin = -10;
+ const numberInputMax = 10;
+ const coarseStep = 1;
+ const fineStep = 1;
+ const dispatch = useAppDispatch();
+ const { t } = useTranslation();
+ const marks = useMemo(() => [sliderMin, 0, sliderMax], [sliderMax, sliderMin]);
+ const onChange = useCallback(
+ (v: number) => {
+ dispatch(creativityChanged(v));
+ },
+ [dispatch]
+ );
+
+ return (
+
+
+ {t('upscaling.creativity')}
+
+
+
+
+ );
+};
+
+export default memo(ParamCreativity);
diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamRealESRGANModel.tsx b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamRealESRGANModel.tsx
deleted file mode 100644
index 6c1a3ab3a7..0000000000
--- a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamRealESRGANModel.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
-import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import type { GroupBase } from 'chakra-react-select';
-import { esrganModelNameChanged, isParamESRGANModelName } from 'features/parameters/store/postprocessingSlice';
-import { memo, useCallback, useMemo } from 'react';
-import { useTranslation } from 'react-i18next';
-
-const options: GroupBase[] = [
- {
- label: 'x2 Upscalers',
- options: [
- {
- label: 'RealESRGAN x2 Plus',
- value: 'RealESRGAN_x2plus.pth',
- description: 'Attempts to retain sharpness, low smoothing',
- },
- ],
- },
- {
- label: 'x4 Upscalers',
- options: [
- {
- label: 'RealESRGAN x4 Plus',
- value: 'RealESRGAN_x4plus.pth',
- description: 'Best for photos and highly detailed images, medium smoothing',
- },
- {
- label: 'RealESRGAN x4 Plus (anime 6B)',
- value: 'RealESRGAN_x4plus_anime_6B.pth',
- description: 'Best for anime/manga, high smoothing',
- },
- {
- label: 'ESRGAN SRx4',
- value: 'ESRGAN_SRx4_DF2KOST_official-ff704c30.pth',
- description: 'Retains sharpness, low smoothing',
- },
- ],
- },
-];
-
-const ParamESRGANModel = () => {
- const { t } = useTranslation();
-
- const esrganModelName = useAppSelector((s) => s.postprocessing.esrganModelName);
-
- const dispatch = useAppDispatch();
-
- const onChange = useCallback(
- (v) => {
- if (!isParamESRGANModelName(v?.value)) {
- return;
- }
- dispatch(esrganModelNameChanged(v.value));
- },
- [dispatch]
- );
-
- const value = useMemo(
- () => options.flatMap((o) => o.options).find((m) => m.value === esrganModelName),
- [esrganModelName]
- );
-
- return (
-
- {t('models.esrganModel')}
-
-
- );
-};
-
-export default memo(ParamESRGANModel);
diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx
new file mode 100644
index 0000000000..501245d77b
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx
@@ -0,0 +1,60 @@
+import { Box, Combobox, FormControl, FormLabel, Tooltip } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
+import { useModelCombobox } from 'common/hooks/useModelCombobox';
+import { upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
+import { memo, useCallback, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useSpandrelImageToImageModels } from 'services/api/hooks/modelsByType';
+import type { SpandrelImageToImageModelConfig } from 'services/api/types';
+
+const ParamSpandrelModel = () => {
+ const { t } = useTranslation();
+ const [modelConfigs, { isLoading }] = useSpandrelImageToImageModels();
+
+ const model = useAppSelector((s) => s.upscale.upscaleModel);
+ const dispatch = useAppDispatch();
+
+ const tooltipLabel = useMemo(() => {
+ if (!modelConfigs.length || !model) {
+ return;
+ }
+ return modelConfigs.find((m) => m.key === model?.key)?.description;
+ }, [modelConfigs, model]);
+
+ const _onChange = useCallback(
+ (v: SpandrelImageToImageModelConfig | null) => {
+ dispatch(upscaleModelChanged(v));
+ },
+ [dispatch]
+ );
+
+ const { options, value, onChange, placeholder, noOptionsMessage } = useModelCombobox({
+ modelConfigs,
+ onChange: _onChange,
+ selectedModel: model,
+ isLoading,
+ });
+
+ return (
+
+
+ {t('upscaling.upscaleModel')}
+
+
+
+
+
+
+
+ );
+};
+
+export default memo(ParamSpandrelModel);
diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamStructure.tsx b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamStructure.tsx
new file mode 100644
index 0000000000..d3cd85c846
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamStructure.tsx
@@ -0,0 +1,55 @@
+import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
+import { structureChanged } from 'features/parameters/store/upscaleSlice';
+import { memo, useCallback, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+
+const ParamStructure = () => {
+ const structure = useAppSelector((s) => s.upscale.structure);
+ const initial = 0;
+ const sliderMin = -10;
+ const sliderMax = 10;
+ const numberInputMin = -10;
+ const numberInputMax = 10;
+ const coarseStep = 1;
+ const fineStep = 1;
+ const dispatch = useAppDispatch();
+ const { t } = useTranslation();
+ const marks = useMemo(() => [sliderMin, 0, sliderMax], [sliderMax, sliderMin]);
+ const onChange = useCallback(
+ (v: number) => {
+ dispatch(structureChanged(v));
+ },
+ [dispatch]
+ );
+
+ return (
+
+
+ {t('upscaling.structure')}
+
+
+
+
+ );
+};
+
+export default memo(ParamStructure);
diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamUpscaleSettings.tsx b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamUpscaleSettings.tsx
deleted file mode 100644
index c0309bebe4..0000000000
--- a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamUpscaleSettings.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import {
- Button,
- Flex,
- IconButton,
- Popover,
- PopoverBody,
- PopoverContent,
- PopoverTrigger,
- useDisclosure,
-} from '@invoke-ai/ui-library';
-import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { useIsAllowedToUpscale } from 'features/parameters/hooks/useIsAllowedToUpscale';
-import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-import { PiFrameCornersBold } from 'react-icons/pi';
-import type { ImageDTO } from 'services/api/types';
-
-import ParamESRGANModel from './ParamRealESRGANModel';
-
-type Props = { imageDTO?: ImageDTO };
-
-const ParamUpscalePopover = (props: Props) => {
- const { imageDTO } = props;
- const dispatch = useAppDispatch();
- const inProgress = useIsQueueMutationInProgress();
- const { t } = useTranslation();
- const { isOpen, onOpen, onClose } = useDisclosure();
- const { isAllowedToUpscale, detail } = useIsAllowedToUpscale(imageDTO);
-
- const handleClickUpscale = useCallback(() => {
- onClose();
- if (!imageDTO || !isAllowedToUpscale) {
- return;
- }
- dispatch(upscaleRequested({ imageDTO }));
- }, [dispatch, imageDTO, isAllowedToUpscale, onClose]);
-
- return (
-
-
- }
- aria-label={t('parameters.upscale')}
- />
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default memo(ParamUpscalePopover);
diff --git a/invokeai/frontend/web/src/features/parameters/hooks/useIsAllowedToUpscale.ts b/invokeai/frontend/web/src/features/parameters/hooks/useIsAllowedToUpscale.ts
deleted file mode 100644
index 95fc52df22..0000000000
--- a/invokeai/frontend/web/src/features/parameters/hooks/useIsAllowedToUpscale.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
-import { useAppSelector } from 'app/store/storeHooks';
-import { selectPostprocessingSlice } from 'features/parameters/store/postprocessingSlice';
-import { selectConfigSlice } from 'features/system/store/configSlice';
-import { useMemo } from 'react';
-import { useTranslation } from 'react-i18next';
-import type { ImageDTO } from 'services/api/types';
-
-const getUpscaledPixels = (imageDTO?: ImageDTO, maxUpscalePixels?: number) => {
- if (!imageDTO) {
- return;
- }
- if (!maxUpscalePixels) {
- return;
- }
- const { width, height } = imageDTO;
- const x4 = height * 4 * width * 4;
- const x2 = height * 2 * width * 2;
- return { x4, x2 };
-};
-
-const getIsAllowedToUpscale = (upscaledPixels?: ReturnType, maxUpscalePixels?: number) => {
- if (!upscaledPixels || !maxUpscalePixels) {
- return { x4: true, x2: true };
- }
- const isAllowedToUpscale = { x4: false, x2: false };
- if (upscaledPixels.x4 <= maxUpscalePixels) {
- isAllowedToUpscale.x4 = true;
- }
- if (upscaledPixels.x2 <= maxUpscalePixels) {
- isAllowedToUpscale.x2 = true;
- }
-
- return isAllowedToUpscale;
-};
-
-const getDetailTKey = (isAllowedToUpscale?: ReturnType, scaleFactor?: number) => {
- if (!isAllowedToUpscale || !scaleFactor) {
- return;
- }
-
- if (isAllowedToUpscale.x4 && isAllowedToUpscale.x2) {
- return;
- }
-
- if (!isAllowedToUpscale.x2 && !isAllowedToUpscale.x4) {
- return 'parameters.isAllowedToUpscale.tooLarge';
- }
-
- if (!isAllowedToUpscale.x4 && isAllowedToUpscale.x2 && scaleFactor === 4) {
- return 'parameters.isAllowedToUpscale.useX2Model';
- }
-
- return;
-};
-
-export const createIsAllowedToUpscaleSelector = (imageDTO?: ImageDTO) =>
- createMemoizedSelector(selectPostprocessingSlice, selectConfigSlice, (postprocessing, config) => {
- const { esrganModelName } = postprocessing;
- const { maxUpscalePixels } = config;
-
- const upscaledPixels = getUpscaledPixels(imageDTO, maxUpscalePixels);
- const isAllowedToUpscale = getIsAllowedToUpscale(upscaledPixels, maxUpscalePixels);
- const scaleFactor = esrganModelName.includes('x2') ? 2 : 4;
- const detailTKey = getDetailTKey(isAllowedToUpscale, scaleFactor);
- return {
- isAllowedToUpscale: scaleFactor === 2 ? isAllowedToUpscale.x2 : isAllowedToUpscale.x4,
- detailTKey,
- };
- });
-
-export const useIsAllowedToUpscale = (imageDTO?: ImageDTO) => {
- const { t } = useTranslation();
- const selectIsAllowedToUpscale = useMemo(() => createIsAllowedToUpscaleSelector(imageDTO), [imageDTO]);
- const { isAllowedToUpscale, detailTKey } = useAppSelector(selectIsAllowedToUpscale);
-
- return {
- isAllowedToUpscale,
- detail: detailTKey ? t(detailTKey) : undefined,
- };
-};
diff --git a/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts b/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts
deleted file mode 100644
index c15bf982b8..0000000000
--- a/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import type { PayloadAction } from '@reduxjs/toolkit';
-import { createSlice } from '@reduxjs/toolkit';
-import type { PersistConfig, RootState } from 'app/store/store';
-import { z } from 'zod';
-
-const zParamESRGANModelName = z.enum([
- 'RealESRGAN_x4plus.pth',
- 'RealESRGAN_x4plus_anime_6B.pth',
- 'ESRGAN_SRx4_DF2KOST_official-ff704c30.pth',
- 'RealESRGAN_x2plus.pth',
-]);
-type ParamESRGANModelName = z.infer;
-export const isParamESRGANModelName = (v: unknown): v is ParamESRGANModelName =>
- zParamESRGANModelName.safeParse(v).success;
-
-interface PostprocessingState {
- _version: 1;
- esrganModelName: ParamESRGANModelName;
-}
-
-const initialPostprocessingState: PostprocessingState = {
- _version: 1,
- esrganModelName: 'RealESRGAN_x4plus.pth',
-};
-
-export const postprocessingSlice = createSlice({
- name: 'postprocessing',
- initialState: initialPostprocessingState,
- reducers: {
- esrganModelNameChanged: (state, action: PayloadAction) => {
- state.esrganModelName = action.payload;
- },
- },
-});
-
-export const { esrganModelNameChanged } = postprocessingSlice.actions;
-
-export const selectPostprocessingSlice = (state: RootState) => state.postprocessing;
-
-/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
-const migratePostprocessingState = (state: any): any => {
- if (!('_version' in state)) {
- state._version = 1;
- }
- return state;
-};
-
-export const postprocessingPersistConfig: PersistConfig = {
- name: postprocessingSlice.name,
- initialState: initialPostprocessingState,
- migrate: migratePostprocessingState,
- persistDenylist: [],
-};
diff --git a/invokeai/frontend/web/src/features/parameters/store/upscaleSlice.ts b/invokeai/frontend/web/src/features/parameters/store/upscaleSlice.ts
new file mode 100644
index 0000000000..0ea4523a15
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/store/upscaleSlice.ts
@@ -0,0 +1,82 @@
+import type { PayloadAction } from '@reduxjs/toolkit';
+import { createSlice } from '@reduxjs/toolkit';
+import type { PersistConfig, RootState } from 'app/store/store';
+import type { ParameterSpandrelImageToImageModel } from 'features/parameters/types/parameterSchemas';
+import type { ControlNetModelConfig, ImageDTO } from 'services/api/types';
+
+interface UpscaleState {
+ _version: 1;
+ upscaleModel: ParameterSpandrelImageToImageModel | null;
+ upscaleInitialImage: ImageDTO | null;
+ structure: number;
+ creativity: number;
+ tileControlnetModel: ControlNetModelConfig | null;
+ scale: number;
+ postProcessingModel: ParameterSpandrelImageToImageModel | null;
+}
+
+const initialUpscaleState: UpscaleState = {
+ _version: 1,
+ upscaleModel: null,
+ upscaleInitialImage: null,
+ structure: 0,
+ creativity: 0,
+ tileControlnetModel: null,
+ scale: 4,
+ postProcessingModel: null,
+};
+
+export const upscaleSlice = createSlice({
+ name: 'upscale',
+ initialState: initialUpscaleState,
+ reducers: {
+ upscaleModelChanged: (state, action: PayloadAction) => {
+ state.upscaleModel = action.payload;
+ },
+ upscaleInitialImageChanged: (state, action: PayloadAction) => {
+ state.upscaleInitialImage = action.payload;
+ },
+ structureChanged: (state, action: PayloadAction) => {
+ state.structure = action.payload;
+ },
+ creativityChanged: (state, action: PayloadAction) => {
+ state.creativity = action.payload;
+ },
+ tileControlnetModelChanged: (state, action: PayloadAction) => {
+ state.tileControlnetModel = action.payload;
+ },
+ scaleChanged: (state, action: PayloadAction) => {
+ state.scale = action.payload;
+ },
+ postProcessingModelChanged: (state, action: PayloadAction) => {
+ state.postProcessingModel = action.payload;
+ },
+ },
+});
+
+export const {
+ upscaleModelChanged,
+ upscaleInitialImageChanged,
+ structureChanged,
+ creativityChanged,
+ tileControlnetModelChanged,
+ scaleChanged,
+ postProcessingModelChanged,
+} = upscaleSlice.actions;
+
+export const selectUpscalelice = (state: RootState) => state.upscale;
+
+/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
+const migrateUpscaleState = (state: any): any => {
+ if (!('_version' in state)) {
+ state._version = 1;
+ }
+ return state;
+};
+
+export const upscalePersistConfig: PersistConfig = {
+ name: upscaleSlice.name,
+ initialState: initialUpscaleState,
+ migrate: migrateUpscaleState,
+ persistDenylist: [],
+};
diff --git a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
index 8a808ed0c5..82620e488e 100644
--- a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
+++ b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
@@ -126,6 +126,11 @@ const zParameterT2IAdapterModel = zModelIdentifierField;
export type ParameterT2IAdapterModel = z.infer;
// #endregion
+// #region VAE Model
+const zParameterSpandrelImageToImageModel = zModelIdentifierField;
+export type ParameterSpandrelImageToImageModel = z.infer;
+// #endregion
+
// #region Strength (l2l strength)
export const zParameterStrength = z.number().min(0).max(1);
export type ParameterStrength = z.infer;
diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx
index 619f55f5f8..e18ccbfbb2 100644
--- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx
+++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx
@@ -1,4 +1,4 @@
-import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
+import { Box, Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
@@ -37,18 +37,20 @@ const ParamSDXLRefinerModelSelect = () => {
optionsFilter,
});
return (
-
+
{t('sdxl.refinermodel')}
-
+
+
+
);
};
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
index 087efba616..7ed7eaf5cc 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
@@ -7,10 +7,14 @@ import ParamCFGRescaleMultiplier from 'features/parameters/components/Advanced/P
import ParamClipSkip from 'features/parameters/components/Advanced/ParamClipSkip';
import ParamSeamlessXAxis from 'features/parameters/components/Seamless/ParamSeamlessXAxis';
import ParamSeamlessYAxis from 'features/parameters/components/Seamless/ParamSeamlessYAxis';
+import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput';
+import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize';
+import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle';
import ParamVAEModelSelect from 'features/parameters/components/VAEModel/ParamVAEModelSelect';
import ParamVAEPrecision from 'features/parameters/components/VAEModel/ParamVAEPrecision';
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
+import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetModelConfigQuery } from 'services/api/endpoints/models';
@@ -26,6 +30,8 @@ const formLabelProps2: FormLabelProps = {
export const AdvancedSettingsAccordion = memo(() => {
const vaeKey = useAppSelector((state) => state.generation.vae?.key);
const { currentData: vaeConfig } = useGetModelConfigQuery(vaeKey ?? skipToken);
+ const activeTabName = useAppSelector(activeTabNameSelector);
+
const selectBadges = useMemo(
() =>
createMemoizedSelector(selectGenerationSlice, (generation) => {
@@ -48,14 +54,17 @@ export const AdvancedSettingsAccordion = memo(() => {
if (generation.seamlessXAxis || generation.seamlessYAxis) {
badges.push('seamless');
}
+ if (activeTabName === 'upscaling' && !generation.shouldRandomizeSeed) {
+ badges.push('Manual Seed');
+ }
return badges;
}),
- [vaeConfig]
+ [vaeConfig, activeTabName]
);
const badges = useAppSelector(selectBadges);
const { t } = useTranslation();
const { isOpen, onToggle } = useStandaloneAccordionToggle({
- id: 'advanced-settings',
+ id: `'advanced-settings-${activeTabName}`,
defaultIsOpen: false,
});
@@ -66,16 +75,27 @@ export const AdvancedSettingsAccordion = memo(() => {
-
-
-
-
-
-
-
-
-
-
+ {activeTabName === 'upscaling' && (
+
+
+
+
+
+ )}
+ {activeTabName !== 'upscaling' && (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
);
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
index 1d9eee7cd8..a809a35587 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
@@ -14,6 +14,7 @@ import ParamMainModelSelect from 'features/parameters/components/MainModel/Param
import { UseDefaultSettingsButton } from 'features/parameters/components/MainModel/UseDefaultSettingsButton';
import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle';
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
+import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { filter } from 'lodash-es';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -26,6 +27,7 @@ const formLabelProps: FormLabelProps = {
export const GenerationSettingsAccordion = memo(() => {
const { t } = useTranslation();
const modelConfig = useSelectedModelConfig();
+ const activeTabName = useAppSelector(activeTabNameSelector);
const selectBadges = useMemo(
() =>
createMemoizedSelector(selectLoraSlice, (lora) => {
@@ -42,8 +44,8 @@ export const GenerationSettingsAccordion = memo(() => {
defaultIsOpen: false,
});
const { isOpen: isOpenAccordion, onToggle: onToggleAccordion } = useStandaloneAccordionToggle({
- id: 'generation-settings',
- defaultIsOpen: true,
+ id: `generation-settings-${activeTabName}`,
+ defaultIsOpen: activeTabName !== 'upscaling',
});
return (
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx
index bf5bdf13e4..eb73ebe961 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx
@@ -62,7 +62,7 @@ const RefinerSettingsAccordionContent: React.FC = memo(() => {
return (
-
+
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleInitialImage.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleInitialImage.tsx
new file mode 100644
index 0000000000..4f40adfdb3
--- /dev/null
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleInitialImage.tsx
@@ -0,0 +1,55 @@
+import { Flex } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import IAIDndImage from 'common/components/IAIDndImage';
+import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
+import type { TypesafeDroppableData } from 'features/dnd/types';
+import { upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
+import { t } from 'i18next';
+import { useCallback, useMemo } from 'react';
+import { PiArrowCounterClockwiseBold } from 'react-icons/pi';
+import type { PostUploadAction } from 'services/api/types';
+
+export const UpscaleInitialImage = () => {
+ const dispatch = useAppDispatch();
+ const imageDTO = useAppSelector((s) => s.upscale.upscaleInitialImage);
+
+ const droppableData = useMemo(
+ () => ({
+ actionType: 'SET_UPSCALE_INITIAL_IMAGE',
+ id: 'upscale-intial-image',
+ }),
+ []
+ );
+
+ const postUploadAction = useMemo(
+ () => ({
+ type: 'SET_UPSCALE_INITIAL_IMAGE',
+ }),
+ []
+ );
+
+ const onReset = useCallback(() => {
+ dispatch(upscaleInitialImageChanged(null));
+ }, [dispatch]);
+
+ return (
+
+
+
+ {imageDTO && (
+
+ }
+ tooltip={t('controlnet.resetControlImage')}
+ />
+
+ )}
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider.tsx
new file mode 100644
index 0000000000..dd4f46557f
--- /dev/null
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider.tsx
@@ -0,0 +1,53 @@
+import { CompositeNumberInput, CompositeSlider, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
+import { scaleChanged } from 'features/parameters/store/upscaleSlice';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+const marks = [2, 4, 8];
+
+const formatValue = (val: string | number) => `${val}x`;
+
+export const UpscaleScaleSlider = memo(() => {
+ const dispatch = useAppDispatch();
+ const { t } = useTranslation();
+ const scale = useAppSelector((s) => s.upscale.scale);
+
+ const onChange = useCallback(
+ (val: number) => {
+ dispatch(scaleChanged(val));
+ },
+ [dispatch]
+ );
+
+ return (
+
+
+ {t('upscaling.scale')}
+
+
+
+
+
+
+ );
+});
+
+UpscaleScaleSlider.displayName = 'UpscaleScaleSlider';
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion.tsx
new file mode 100644
index 0000000000..7aa8c8c250
--- /dev/null
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion.tsx
@@ -0,0 +1,74 @@
+import { Expander, Flex, StandaloneAccordion } from '@invoke-ai/ui-library';
+import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
+import { useAppSelector } from 'app/store/storeHooks';
+import ParamCreativity from 'features/parameters/components/Upscale/ParamCreativity';
+import ParamSpandrelModel from 'features/parameters/components/Upscale/ParamSpandrelModel';
+import ParamStructure from 'features/parameters/components/Upscale/ParamStructure';
+import { selectUpscalelice } from 'features/parameters/store/upscaleSlice';
+import { UpscaleScaleSlider } from 'features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider';
+import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle';
+import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
+import { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { UpscaleInitialImage } from './UpscaleInitialImage';
+import { UpscaleWarning } from './UpscaleWarning';
+
+const selector = createMemoizedSelector([selectUpscalelice], (upscaleSlice) => {
+ const { upscaleModel, upscaleInitialImage, scale } = upscaleSlice;
+
+ const badges: string[] = [];
+
+ if (upscaleModel) {
+ badges.push(upscaleModel.name);
+ }
+
+ if (upscaleInitialImage) {
+ // Output height and width are scaled and rounded down to the nearest multiple of 8
+ const outputWidth = Math.floor((upscaleInitialImage.width * scale) / 8) * 8;
+ const outputHeight = Math.floor((upscaleInitialImage.height * scale) / 8) * 8;
+
+ badges.push(`${outputWidth}×${outputHeight}`);
+ }
+
+ return { badges };
+});
+
+export const UpscaleSettingsAccordion = memo(() => {
+ const { t } = useTranslation();
+ const { badges } = useAppSelector(selector);
+ const { isOpen: isOpenAccordion, onToggle: onToggleAccordion } = useStandaloneAccordionToggle({
+ id: 'upscale-settings',
+ defaultIsOpen: true,
+ });
+
+ const { isOpen: isOpenExpander, onToggle: onToggleExpander } = useExpanderToggle({
+ id: 'upscale-settings-advanced',
+ defaultIsOpen: false,
+ });
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+});
+
+UpscaleSettingsAccordion.displayName = 'UpscaleSettingsAccordion';
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleWarning.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleWarning.tsx
new file mode 100644
index 0000000000..0c2c16d302
--- /dev/null
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleWarning.tsx
@@ -0,0 +1,70 @@
+import { Button, Flex, ListItem, Text, UnorderedList } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { $installModelsTab } from 'features/modelManagerV2/subpanels/InstallModels';
+import { tileControlnetModelChanged } from 'features/parameters/store/upscaleSlice';
+import { setActiveTab } from 'features/ui/store/uiSlice';
+import { useCallback, useEffect, useMemo } from 'react';
+import { Trans, useTranslation } from 'react-i18next';
+import { useControlNetModels } from 'services/api/hooks/modelsByType';
+
+export const UpscaleWarning = () => {
+ const { t } = useTranslation();
+ const model = useAppSelector((s) => s.generation.model);
+ const upscaleModel = useAppSelector((s) => s.upscale.upscaleModel);
+ const tileControlnetModel = useAppSelector((s) => s.upscale.tileControlnetModel);
+ const dispatch = useAppDispatch();
+ const [modelConfigs, { isLoading }] = useControlNetModels();
+ const disabledTabs = useAppSelector((s) => s.config.disabledTabs);
+ const shouldShowButton = useMemo(() => !disabledTabs.includes('models'), [disabledTabs]);
+
+ useEffect(() => {
+ const validModel = modelConfigs.find((cnetModel) => {
+ return cnetModel.base === model?.base && cnetModel.name.toLowerCase().includes('tile');
+ });
+ dispatch(tileControlnetModelChanged(validModel || null));
+ }, [model?.base, modelConfigs, dispatch]);
+
+ const warnings = useMemo(() => {
+ const _warnings: string[] = [];
+ if (!model) {
+ _warnings.push(t('upscaling.mainModelDesc'));
+ }
+ if (!tileControlnetModel) {
+ _warnings.push(t('upscaling.tileControlNetModelDesc'));
+ }
+ if (!upscaleModel) {
+ _warnings.push(t('upscaling.upscaleModelDesc'));
+ }
+
+ return _warnings;
+ }, [model, tileControlnetModel, upscaleModel, t]);
+
+ const handleGoToModelManager = useCallback(() => {
+ dispatch(setActiveTab('models'));
+ $installModelsTab.set(3);
+ }, [dispatch]);
+
+ if (!warnings.length || isLoading || !shouldShowButton) {
+ return null;
+ }
+
+ return (
+
+
+
+ ),
+ }}
+ />
+
+
+ {warnings.map((warning) => (
+ {warning}
+ ))}
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts b/invokeai/frontend/web/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts
index d69d08d17d..ac094a093f 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts
+++ b/invokeai/frontend/web/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts
@@ -18,5 +18,6 @@ export const useStandaloneAccordionToggle = (arg: UseStandaloneAccordionToggleAr
const onToggle = useCallback(() => {
dispatch(accordionStateChanged({ id: arg.id, isOpen: !isOpen }));
}, [arg.id, dispatch, isOpen]);
+
return { isOpen, onToggle };
};
diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts b/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts
index 79d957d7d3..76590db443 100644
--- a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts
+++ b/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts
@@ -102,8 +102,8 @@ export const useHotkeyData = (): HotkeyGroup[] => {
hotkeys: [['A']],
},
{
- title: t('hotkeys.upscale.title'),
- desc: t('hotkeys.upscale.desc'),
+ title: t('hotkeys.postProcess.title'),
+ desc: t('hotkeys.postProcess.desc'),
hotkeys: [['Shift', 'U']],
},
{
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx
index b424a129ee..33455e50fa 100644
--- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx
@@ -5,6 +5,7 @@ import {
MenuGroup,
MenuItem,
MenuList,
+ Portal,
useDisclosure,
useGlobalMenuClose,
} from '@invoke-ai/ui-library';
@@ -14,10 +15,18 @@ import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { discordLink, githubLink } from 'features/system/store/constants';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiBugBeetleBold, PiInfoBold, PiKeyboardBold, PiToggleRightFill } from 'react-icons/pi';
+import {
+ PiBugBeetleBold,
+ PiInfoBold,
+ PiKeyboardBold,
+ PiShareNetworkFill,
+ PiToggleRightFill,
+ PiUsersBold,
+} from 'react-icons/pi';
import { RiDiscordFill, RiGithubFill, RiSettings4Line } from 'react-icons/ri';
import SettingsModal from './SettingsModal';
+import { SettingsUpsellMenuItem } from './SettingsUpsellMenuItem';
const SettingsMenu = () => {
const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure();
@@ -28,7 +37,7 @@ const SettingsMenu = () => {
const isGithubLinkEnabled = useFeatureStatus('githubLink');
return (
-
@@ -46,4 +44,4 @@ const ParametersPanel = () => {
);
};
-export default memo(ParametersPanel);
+export default memo(ParametersPanelCanvas);
diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanelTextToImage.tsx b/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelTextToImage.tsx
similarity index 100%
rename from invokeai/frontend/web/src/features/ui/components/ParametersPanelTextToImage.tsx
rename to invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelTextToImage.tsx
diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelUpscale.tsx b/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelUpscale.tsx
new file mode 100644
index 0000000000..19979dea2f
--- /dev/null
+++ b/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelUpscale.tsx
@@ -0,0 +1,41 @@
+import { Box, Flex } from '@invoke-ai/ui-library';
+import { useAppSelector } from 'app/store/storeHooks';
+import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
+import { Prompts } from 'features/parameters/components/Prompts/Prompts';
+import QueueControls from 'features/queue/components/QueueControls';
+import { SDXLPrompts } from 'features/sdxl/components/SDXLPrompts/SDXLPrompts';
+import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
+import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
+import { UpscaleSettingsAccordion } from 'features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion';
+import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
+import type { CSSProperties } from 'react';
+import { memo } from 'react';
+
+const overlayScrollbarsStyles: CSSProperties = {
+ height: '100%',
+ width: '100%',
+};
+
+const ParametersPanelUpscale = () => {
+ const isSDXL = useAppSelector((s) => s.generation.model?.base === 'sdxl');
+
+ return (
+
+
+
+
+
+
+ {isSDXL ? : }
+
+
+
+
+
+
+
+
+ );
+};
+
+export default memo(ParametersPanelUpscale);
diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UpscalingTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UpscalingTab.tsx
new file mode 100644
index 0000000000..e2da68ceb7
--- /dev/null
+++ b/invokeai/frontend/web/src/features/ui/components/tabs/UpscalingTab.tsx
@@ -0,0 +1,13 @@
+import { Box } from '@invoke-ai/ui-library';
+import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
+import { memo } from 'react';
+
+const UpscalingTab = () => {
+ return (
+
+
+
+ );
+};
+
+export default memo(UpscalingTab);
diff --git a/invokeai/frontend/web/src/features/ui/store/tabMap.tsx b/invokeai/frontend/web/src/features/ui/store/tabMap.tsx
index 526a55b069..5cf97b2d3e 100644
--- a/invokeai/frontend/web/src/features/ui/store/tabMap.tsx
+++ b/invokeai/frontend/web/src/features/ui/store/tabMap.tsx
@@ -1,3 +1,3 @@
-export const TAB_NUMBER_MAP = ['generation', 'canvas', 'workflows', 'models', 'queue'] as const;
+export const TAB_NUMBER_MAP = ['generation', 'canvas', 'upscaling', 'workflows', 'models', 'queue'] as const;
export type InvokeTabName = (typeof TAB_NUMBER_MAP)[number];
diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts
index 2040021d6d..9672acc149 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/images.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts
@@ -104,6 +104,10 @@ export const imagesApi = api.injectEndpoints({
type: 'Board',
id: boardId,
},
+ {
+ type: 'BoardImagesTotal',
+ id: boardId,
+ },
];
},
}),
@@ -136,6 +140,10 @@ export const imagesApi = api.injectEndpoints({
type: 'Board',
id: boardId,
},
+ {
+ type: 'BoardImagesTotal',
+ id: boardId,
+ },
];
return tags;
@@ -169,6 +177,10 @@ export const imagesApi = api.injectEndpoints({
type: 'Board',
id: boardId,
},
+ {
+ type: 'BoardImagesTotal',
+ id: boardId,
+ },
];
},
}),
@@ -300,6 +312,10 @@ export const imagesApi = api.injectEndpoints({
type: 'Board',
id: boardId,
},
+ {
+ type: 'BoardImagesTotal',
+ id: boardId,
+ },
];
},
}),
@@ -362,6 +378,14 @@ export const imagesApi = api.injectEndpoints({
},
{ type: 'Board', id: board_id },
{ type: 'Board', id: imageDTO.board_id ?? 'none' },
+ {
+ type: 'BoardImagesTotal',
+ id: imageDTO.board_id ?? 'none',
+ },
+ {
+ type: 'BoardImagesTotal',
+ id: board_id,
+ },
];
},
}),
@@ -393,6 +417,11 @@ export const imagesApi = api.injectEndpoints({
},
{ type: 'Board', id: imageDTO.board_id ?? 'none' },
{ type: 'Board', id: 'none' },
+ {
+ type: 'BoardImagesTotal',
+ id: imageDTO.board_id ?? 'none',
+ },
+ { type: 'BoardImagesTotal', id: 'none' },
];
},
}),
@@ -429,11 +458,19 @@ export const imagesApi = api.injectEndpoints({
}),
});
tags.push({ type: 'Board', id: imageDTOs[0].board_id ?? 'none' });
+ tags.push({
+ type: 'BoardImagesTotal',
+ id: imageDTOs[0].board_id ?? 'none',
+ });
}
for (const imageDTO of imageDTOs) {
tags.push({ type: 'Image', id: imageDTO.image_name });
}
tags.push({ type: 'Board', id: board_id });
+ tags.push({
+ type: 'BoardImagesTotal',
+ id: board_id ?? 'none',
+ });
return tags;
},
}),
@@ -469,6 +506,10 @@ export const imagesApi = api.injectEndpoints({
categories: getCategories(imageDTOs[0]),
}),
});
+ tags.push({
+ type: 'BoardImagesTotal',
+ id: 'none',
+ });
}
result?.removed_image_names.forEach((image_name) => {
@@ -480,6 +521,10 @@ export const imagesApi = api.injectEndpoints({
}
tags.push({ type: 'Image', id: image_name });
tags.push({ type: 'Board', id: board_id });
+ tags.push({
+ type: 'BoardImagesTotal',
+ id: board_id ?? 'none',
+ });
});
return tags;
diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts
index 21ecc5657f..881a24a68d 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/models.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts
@@ -39,9 +39,10 @@ type DeleteModelImageResponse = void;
type ConvertMainModelResponse =
paths['/api/v2/models/convert/{key}']['put']['responses']['200']['content']['application/json'];
-type InstallModelArg = {
+export type InstallModelArg = {
source: paths['/api/v2/models/install']['post']['parameters']['query']['source'];
inplace?: paths['/api/v2/models/install']['post']['parameters']['query']['inplace'];
+ config?: paths['/api/v2/models/install']['post']['requestBody']['content']['application/json'];
};
type InstallModelResponse = paths['/api/v2/models/install']['post']['responses']['201']['content']['application/json'];
@@ -124,11 +125,12 @@ export const modelsApi = api.injectEndpoints({
invalidatesTags: [{ type: 'ModelConfig', id: LIST_TAG }],
}),
installModel: build.mutation({
- query: ({ source, inplace = true }) => {
+ query: ({ source, inplace = true, config }) => {
return {
url: buildModelsUrl('install'),
params: { source, inplace },
method: 'POST',
+ body: config,
};
},
invalidatesTags: ['ModelInstalls'],
@@ -240,7 +242,6 @@ export const modelsApi = api.injectEndpoints({
}
return tags;
},
- keepUnusedDataFor: 60 * 60 * 1000 * 24, // 1 day (infinite)
transformResponse: (response: GetModelConfigsResponse) => {
return modelConfigsAdapter.setAll(modelConfigsAdapter.getInitialState(), response.models);
},
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 890de69e35..59f9897f74 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -103,8 +103,9 @@ export type paths = {
* - model/name:fp16:path/to/model.safetensors
* - model/name::path/to/model.safetensors
*
- * `config` is an optional dict containing model configuration values that will override
- * the ones that are probed automatically.
+ * `config` is a ModelRecordChanges object. Fields in this object will override
+ * the ones that are probed automatically. Pass an empty object to accept
+ * all the defaults.
*
* `access_token` is an optional access token for use with Urls that require
* authentication.
@@ -1589,6 +1590,7 @@ export type components = {
cover_image?: string | null;
/**
* Format
+ * @default diffusers
* @constant
* @enum {string}
*/
@@ -4789,7 +4791,7 @@ export type components = {
* @description The nodes in this graph
*/
nodes?: {
- [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
+ [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
};
/**
* Edges
@@ -6568,7 +6570,7 @@ export type components = {
tiled?: boolean;
/**
* Tile Size
- * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the
+ * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage.
* @default 0
*/
tile_size?: number;
@@ -7160,7 +7162,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -7206,7 +7208,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -7269,7 +7271,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -7304,146 +7306,147 @@ export type components = {
project_id: string | null;
};
InvocationOutputMap: {
- noise: components["schemas"]["NoiseOutput"];
- pair_tile_image: components["schemas"]["PairTileImageOutput"];
+ dynamic_prompt: components["schemas"]["StringCollectionOutput"];
+ img_ilerp: components["schemas"]["ImageOutput"];
+ random_range: components["schemas"]["IntegerCollectionOutput"];
+ ideal_size: components["schemas"]["IdealSizeOutput"];
+ lblend: components["schemas"]["LatentsOutput"];
+ tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
color_correct: components["schemas"]["ImageOutput"];
- tile_to_properties: components["schemas"]["TileToPropertiesOutput"];
- float_to_int: components["schemas"]["IntegerOutput"];
- rand_int: components["schemas"]["IntegerOutput"];
- latents: components["schemas"]["LatentsOutput"];
+ sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
+ img_hue_adjust: components["schemas"]["ImageOutput"];
+ lscale: components["schemas"]["LatentsOutput"];
+ infill_patchmatch: components["schemas"]["ImageOutput"];
+ mask_edge: components["schemas"]["ImageOutput"];
+ spandrel_image_to_image: components["schemas"]["ImageOutput"];
+ round_float: components["schemas"]["FloatOutput"];
+ color: components["schemas"]["ColorOutput"];
+ img_channel_offset: components["schemas"]["ImageOutput"];
+ create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
+ string_join_three: components["schemas"]["StringOutput"];
+ unsharp_mask: components["schemas"]["ImageOutput"];
canvas_paste_back: components["schemas"]["ImageOutput"];
- controlnet: components["schemas"]["ControlOutput"];
- img_blur: components["schemas"]["ImageOutput"];
- freeu: components["schemas"]["UNetOutput"];
- string: components["schemas"]["StringOutput"];
- boolean_collection: components["schemas"]["BooleanCollectionOutput"];
- boolean: components["schemas"]["BooleanOutput"];
- lresize: components["schemas"]["LatentsOutput"];
+ string_collection: components["schemas"]["StringCollectionOutput"];
+ face_mask_detection: components["schemas"]["FaceMaskOutput"];
+ color_map_image_processor: components["schemas"]["ImageOutput"];
+ normalbae_image_processor: components["schemas"]["ImageOutput"];
+ create_gradient_mask: components["schemas"]["GradientMaskOutput"];
+ vae_loader: components["schemas"]["VAEOutput"];
+ integer: components["schemas"]["IntegerOutput"];
+ image_mask_to_tensor: components["schemas"]["MaskOutput"];
+ esrgan: components["schemas"]["ImageOutput"];
+ mlsd_image_processor: components["schemas"]["ImageOutput"];
+ blank_image: components["schemas"]["ImageOutput"];
+ zoe_depth_image_processor: components["schemas"]["ImageOutput"];
+ merge_metadata: components["schemas"]["MetadataOutput"];
+ infill_rgba: components["schemas"]["ImageOutput"];
mask_from_id: components["schemas"]["ImageOutput"];
string_split: components["schemas"]["String2Output"];
- create_gradient_mask: components["schemas"]["GradientMaskOutput"];
- seamless: components["schemas"]["SeamlessModeOutput"];
- merge_tiles_to_image: components["schemas"]["ImageOutput"];
- canny_image_processor: components["schemas"]["ImageOutput"];
- crop_latents: components["schemas"]["LatentsOutput"];
- mask_edge: components["schemas"]["ImageOutput"];
- img_paste: components["schemas"]["ImageOutput"];
- zoe_depth_image_processor: components["schemas"]["ImageOutput"];
- img_nsfw: components["schemas"]["ImageOutput"];
- img_mul: components["schemas"]["ImageOutput"];
- spandrel_image_to_image: components["schemas"]["ImageOutput"];
- tomask: components["schemas"]["ImageOutput"];
- color_map_image_processor: components["schemas"]["ImageOutput"];
- sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
- infill_rgba: components["schemas"]["ImageOutput"];
- model_identifier: components["schemas"]["ModelIdentifierOutput"];
- metadata: components["schemas"]["MetadataOutput"];
- img_ilerp: components["schemas"]["ImageOutput"];
+ boolean_collection: components["schemas"]["BooleanCollectionOutput"];
+ img_resize: components["schemas"]["ImageOutput"];
+ float_collection: components["schemas"]["FloatCollectionOutput"];
+ img_conv: components["schemas"]["ImageOutput"];
+ boolean: components["schemas"]["BooleanOutput"];
+ mul: components["schemas"]["IntegerOutput"];
+ img_watermark: components["schemas"]["ImageOutput"];
+ heuristic_resize: components["schemas"]["ImageOutput"];
+ tile_image_processor: components["schemas"]["ImageOutput"];
+ face_identifier: components["schemas"]["ImageOutput"];
+ denoise_latents: components["schemas"]["LatentsOutput"];
+ img_lerp: components["schemas"]["ImageOutput"];
+ pair_tile_image: components["schemas"]["PairTileImageOutput"];
add: components["schemas"]["IntegerOutput"];
+ range_of_size: components["schemas"]["IntegerCollectionOutput"];
+ img_scale: components["schemas"]["ImageOutput"];
+ latents_collection: components["schemas"]["LatentsCollectionOutput"];
+ canny_image_processor: components["schemas"]["ImageOutput"];
+ float_math: components["schemas"]["FloatOutput"];
+ infill_tile: components["schemas"]["ImageOutput"];
+ controlnet: components["schemas"]["ControlOutput"];
+ dw_openpose_image_processor: components["schemas"]["ImageOutput"];
+ img_chan: components["schemas"]["ImageOutput"];
+ conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
+ lresize: components["schemas"]["LatentsOutput"];
+ sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
+ noise: components["schemas"]["NoiseOutput"];
+ ip_adapter: components["schemas"]["IPAdapterOutput"];
+ sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
+ sub: components["schemas"]["IntegerOutput"];
+ seamless: components["schemas"]["SeamlessModeOutput"];
+ div: components["schemas"]["IntegerOutput"];
img_channel_multiply: components["schemas"]["ImageOutput"];
- integer: components["schemas"]["IntegerOutput"];
- integer_collection: components["schemas"]["IntegerCollectionOutput"];
- img_crop: components["schemas"]["ImageOutput"];
- show_image: components["schemas"]["ImageOutput"];
- string_replace: components["schemas"]["StringOutput"];
- prompt_from_file: components["schemas"]["StringCollectionOutput"];
+ compel: components["schemas"]["ConditioningOutput"];
+ freeu: components["schemas"]["UNetOutput"];
+ conditioning: components["schemas"]["ConditioningOutput"];
+ rand_int: components["schemas"]["IntegerOutput"];
+ rectangle_mask: components["schemas"]["MaskOutput"];
+ string_split_neg: components["schemas"]["StringPosNegOutput"];
+ collect: components["schemas"]["CollectInvocationOutput"];
+ float_range: components["schemas"]["FloatCollectionOutput"];
string_join: components["schemas"]["StringOutput"];
metadata_item: components["schemas"]["MetadataItemOutput"];
- lblend: components["schemas"]["LatentsOutput"];
- t2i_adapter: components["schemas"]["T2IAdapterOutput"];
- infill_cv2: components["schemas"]["ImageOutput"];
- sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
- core_metadata: components["schemas"]["MetadataOutput"];
+ l2i: components["schemas"]["ImageOutput"];
+ img_paste: components["schemas"]["ImageOutput"];
+ calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
+ calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"];
+ show_image: components["schemas"]["ImageOutput"];
invert_tensor_mask: components["schemas"]["MaskOutput"];
- integer_math: components["schemas"]["IntegerOutput"];
+ spandrel_image_to_image_autoscale: components["schemas"]["ImageOutput"];
+ face_off: components["schemas"]["FaceOffOutput"];
+ image_collection: components["schemas"]["ImageCollectionOutput"];
+ i2l: components["schemas"]["LatentsOutput"];
+ mediapipe_face_processor: components["schemas"]["ImageOutput"];
+ scheduler: components["schemas"]["SchedulerOutput"];
content_shuffle_image_processor: components["schemas"]["ImageOutput"];
- dynamic_prompt: components["schemas"]["StringCollectionOutput"];
lineart_anime_image_processor: components["schemas"]["ImageOutput"];
- string_split_neg: components["schemas"]["StringPosNegOutput"];
- round_float: components["schemas"]["FloatOutput"];
- rand_float: components["schemas"]["FloatOutput"];
+ float_to_int: components["schemas"]["IntegerOutput"];
+ pidi_image_processor: components["schemas"]["ImageOutput"];
+ leres_image_processor: components["schemas"]["ImageOutput"];
+ lora_selector: components["schemas"]["LoRASelectorOutput"];
+ depth_anything_image_processor: components["schemas"]["ImageOutput"];
+ img_pad_crop: components["schemas"]["ImageOutput"];
+ calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
+ prompt_from_file: components["schemas"]["StringCollectionOutput"];
+ t2i_adapter: components["schemas"]["T2IAdapterOutput"];
lora_collection_loader: components["schemas"]["LoRALoaderOutput"];
+ img_crop: components["schemas"]["ImageOutput"];
+ img_mul: components["schemas"]["ImageOutput"];
+ sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
+ tomask: components["schemas"]["ImageOutput"];
+ tile_to_properties: components["schemas"]["TileToPropertiesOutput"];
+ string_replace: components["schemas"]["StringOutput"];
+ merge_tiles_to_image: components["schemas"]["ImageOutput"];
+ integer_collection: components["schemas"]["IntegerCollectionOutput"];
+ crop_latents: components["schemas"]["LatentsOutput"];
+ lineart_image_processor: components["schemas"]["ImageOutput"];
midas_depth_image_processor: components["schemas"]["ImageOutput"];
- random_range: components["schemas"]["IntegerCollectionOutput"];
- sub: components["schemas"]["IntegerOutput"];
- infill_lama: components["schemas"]["ImageOutput"];
- float_range: components["schemas"]["FloatCollectionOutput"];
+ segment_anything_processor: components["schemas"]["ImageOutput"];
save_image: components["schemas"]["ImageOutput"];
+ infill_cv2: components["schemas"]["ImageOutput"];
+ cv_inpaint: components["schemas"]["ImageOutput"];
+ lora_loader: components["schemas"]["LoRALoaderOutput"];
+ latents: components["schemas"]["LatentsOutput"];
+ model_identifier: components["schemas"]["ModelIdentifierOutput"];
+ step_param_easing: components["schemas"]["FloatCollectionOutput"];
+ metadata: components["schemas"]["MetadataOutput"];
+ range: components["schemas"]["IntegerCollectionOutput"];
+ img_blur: components["schemas"]["ImageOutput"];
+ core_metadata: components["schemas"]["MetadataOutput"];
+ mask_combine: components["schemas"]["ImageOutput"];
+ sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
+ infill_lama: components["schemas"]["ImageOutput"];
iterate: components["schemas"]["IterateInvocationOutput"];
hed_image_processor: components["schemas"]["ImageOutput"];
- dw_openpose_image_processor: components["schemas"]["ImageOutput"];
- scheduler: components["schemas"]["SchedulerOutput"];
- string_collection: components["schemas"]["StringCollectionOutput"];
- lineart_image_processor: components["schemas"]["ImageOutput"];
- image: components["schemas"]["ImageOutput"];
- merge_metadata: components["schemas"]["MetadataOutput"];
- image_collection: components["schemas"]["ImageCollectionOutput"];
- img_watermark: components["schemas"]["ImageOutput"];
- pidi_image_processor: components["schemas"]["ImageOutput"];
- sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
- collect: components["schemas"]["CollectInvocationOutput"];
- lora_selector: components["schemas"]["LoRASelectorOutput"];
- tile_image_processor: components["schemas"]["ImageOutput"];
- denoise_latents: components["schemas"]["LatentsOutput"];
- sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"];
- img_conv: components["schemas"]["ImageOutput"];
- face_mask_detection: components["schemas"]["FaceMaskOutput"];
- infill_patchmatch: components["schemas"]["ImageOutput"];
- rectangle_mask: components["schemas"]["MaskOutput"];
- img_lerp: components["schemas"]["ImageOutput"];
- tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
- face_identifier: components["schemas"]["ImageOutput"];
- step_param_easing: components["schemas"]["FloatCollectionOutput"];
- unsharp_mask: components["schemas"]["ImageOutput"];
- mediapipe_face_processor: components["schemas"]["ImageOutput"];
- calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
- lscale: components["schemas"]["LatentsOutput"];
- color: components["schemas"]["ColorOutput"];
- lora_loader: components["schemas"]["LoRALoaderOutput"];
- sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
- calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
- conditioning: components["schemas"]["ConditioningOutput"];
- float_collection: components["schemas"]["FloatCollectionOutput"];
- img_pad_crop: components["schemas"]["ImageOutput"];
- mul: components["schemas"]["IntegerOutput"];
- heuristic_resize: components["schemas"]["ImageOutput"];
- create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
- img_chan: components["schemas"]["ImageOutput"];
- leres_image_processor: components["schemas"]["ImageOutput"];
- infill_tile: components["schemas"]["ImageOutput"];
- i2l: components["schemas"]["LatentsOutput"];
- string_join_three: components["schemas"]["StringOutput"];
- ip_adapter: components["schemas"]["IPAdapterOutput"];
+ img_nsfw: components["schemas"]["ImageOutput"];
main_model_loader: components["schemas"]["ModelLoaderOutput"];
+ string: components["schemas"]["StringOutput"];
+ sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"];
+ image: components["schemas"]["ImageOutput"];
+ rand_float: components["schemas"]["FloatOutput"];
float: components["schemas"]["FloatOutput"];
- compel: components["schemas"]["ConditioningOutput"];
- range_of_size: components["schemas"]["IntegerCollectionOutput"];
- normalbae_image_processor: components["schemas"]["ImageOutput"];
- ideal_size: components["schemas"]["IdealSizeOutput"];
- conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
- depth_anything_image_processor: components["schemas"]["ImageOutput"];
- mask_combine: components["schemas"]["ImageOutput"];
- l2i: components["schemas"]["ImageOutput"];
- latents_collection: components["schemas"]["LatentsCollectionOutput"];
- float_math: components["schemas"]["FloatOutput"];
- img_hue_adjust: components["schemas"]["ImageOutput"];
- img_scale: components["schemas"]["ImageOutput"];
- esrgan: components["schemas"]["ImageOutput"];
- vae_loader: components["schemas"]["VAEOutput"];
- sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
- clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
- segment_anything_processor: components["schemas"]["ImageOutput"];
- img_resize: components["schemas"]["ImageOutput"];
- range: components["schemas"]["IntegerCollectionOutput"];
- calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"];
- mlsd_image_processor: components["schemas"]["ImageOutput"];
- img_channel_offset: components["schemas"]["ImageOutput"];
- cv_inpaint: components["schemas"]["ImageOutput"];
- image_mask_to_tensor: components["schemas"]["MaskOutput"];
- blank_image: components["schemas"]["ImageOutput"];
- div: components["schemas"]["IntegerOutput"];
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
- face_off: components["schemas"]["FaceOffOutput"];
+ integer_math: components["schemas"]["IntegerOutput"];
+ clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
};
/**
* InvocationStartedEvent
@@ -7479,7 +7482,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -7783,7 +7786,7 @@ export type components = {
tiled?: boolean;
/**
* Tile Size
- * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the
+ * @description The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage.
* @default 0
*/
tile_size?: number;
@@ -9594,11 +9597,8 @@ export type components = {
* @description Information about why the job failed
*/
error_reason?: string | null;
- /**
- * Config In
- * @description Configuration information (e.g. 'description') to apply to model.
- */
- config_in?: Record;
+ /** @description Configuration information (e.g. 'description') to apply to model. */
+ config_in?: components["schemas"]["ModelRecordChanges"];
/**
* Config Out
* @description After successful installation, this will hold the configuration object.
@@ -9750,6 +9750,18 @@ export type components = {
* @description A set of changes to apply to a model.
*/
ModelRecordChanges: {
+ /**
+ * Source
+ * @description original source of the model
+ */
+ source?: string | null;
+ /** @description type of model source */
+ source_type?: components["schemas"]["ModelSourceType"] | null;
+ /**
+ * Source Api Response
+ * @description metadata from remote source
+ */
+ source_api_response?: string | null;
/**
* Name
* @description Name of the model.
@@ -9767,6 +9779,18 @@ export type components = {
description?: string | null;
/** @description The base model. */
base?: components["schemas"]["BaseModelType"] | null;
+ /** @description Type of model */
+ type?: components["schemas"]["ModelType"] | null;
+ /**
+ * Key
+ * @description Database ID for this model
+ */
+ key?: string | null;
+ /**
+ * Hash
+ * @description hash of model file
+ */
+ hash?: string | null;
/**
* Trigger Phrases
* @description Set of trigger phrases for this model
@@ -11875,6 +11899,75 @@ export type components = {
*/
type: "show_image";
};
+ /**
+ * Image-to-Image (Autoscale)
+ * @description Run any spandrel image-to-image model (https://github.com/chaiNNer-org/spandrel) until the target scale is reached.
+ */
+ SpandrelImageToImageAutoscaleInvocation: {
+ /**
+ * @description The board to save the image to
+ * @default null
+ */
+ board?: components["schemas"]["BoardField"] | null;
+ /**
+ * @description Optional metadata to be saved with the image
+ * @default null
+ */
+ metadata?: components["schemas"]["MetadataField"] | null;
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description The input image
+ * @default null
+ */
+ image?: components["schemas"]["ImageField"];
+ /**
+ * Image-to-Image Model
+ * @description Image-to-Image model
+ * @default null
+ */
+ image_to_image_model?: components["schemas"]["ModelIdentifierField"];
+ /**
+ * Tile Size
+ * @description The tile size for tiled image-to-image. Set to 0 to disable tiling.
+ * @default 512
+ */
+ tile_size?: number;
+ /**
+ * type
+ * @default spandrel_image_to_image_autoscale
+ * @constant
+ * @enum {string}
+ */
+ type: "spandrel_image_to_image_autoscale";
+ /**
+ * Scale
+ * @description The final scale of the output image. If the model does not upscale the image, this will be ignored.
+ * @default 4
+ */
+ scale?: number;
+ /**
+ * Fit To Multiple Of 8
+ * @description If true, the output image will be resized to the nearest multiple of 8 in both dimensions.
+ * @default false
+ */
+ fit_to_multiple_of_8?: boolean;
+ };
/**
* SpandrelImageToImageConfig
* @description Model config for Spandrel Image to Image models.
@@ -11982,6 +12075,12 @@ export type components = {
* @default null
*/
image_to_image_model?: components["schemas"]["ModelIdentifierField"];
+ /**
+ * Tile Size
+ * @description The tile size for tiled image-to-image. Set to 0 to disable tiling.
+ * @default 512
+ */
+ tile_size?: number;
/**
* type
* @default spandrel_image_to_image
@@ -12592,6 +12691,7 @@ export type components = {
cover_image?: string | null;
/**
* Format
+ * @default diffusers
* @constant
* @enum {string}
*/
@@ -14285,8 +14385,9 @@ export type operations = {
* - model/name:fp16:path/to/model.safetensors
* - model/name::path/to/model.safetensors
*
- * `config` is an optional dict containing model configuration values that will override
- * the ones that are probed automatically.
+ * `config` is a ModelRecordChanges object. Fields in this object will override
+ * the ones that are probed automatically. Pass an empty object to accept
+ * all the defaults.
*
* `access_token` is an optional access token for use with Urls that require
* authentication.
@@ -14305,10 +14406,11 @@ export type operations = {
source: string;
/** @description Whether or not to install a local model in place */
inplace?: boolean | null;
+ /** @description access token for the remote resource */
access_token?: string | null;
};
};
- requestBody?: {
+ requestBody: {
content: {
/**
* @example {
@@ -14316,7 +14418,7 @@ export type operations = {
* "description": "string"
* }
*/
- "application/json": Record | null;
+ "application/json": components["schemas"]["ModelRecordChanges"];
};
};
responses: {
diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts
index b8ffa46c82..bfe42f3f9a 100644
--- a/invokeai/frontend/web/src/services/api/types.ts
+++ b/invokeai/frontend/web/src/services/api/types.ts
@@ -54,7 +54,7 @@ export type T2IAdapterModelConfig = S['T2IAdapterConfig'];
export type SpandrelImageToImageModelConfig = S['SpandrelImageToImageConfig'];
type TextualInversionModelConfig = S['TextualInversionFileConfig'] | S['TextualInversionFolderConfig'];
type DiffusersModelConfig = S['MainDiffusersConfig'];
-type CheckpointModelConfig = S['MainCheckpointConfig'];
+export type CheckpointModelConfig = S['MainCheckpointConfig'];
type CLIPVisionDiffusersConfig = S['CLIPVisionDiffusersConfig'];
export type MainModelConfig = DiffusersModelConfig | CheckpointModelConfig;
export type AnyModelConfig =
@@ -205,6 +205,10 @@ type CanvasInitialImageAction = {
type: 'SET_CANVAS_INITIAL_IMAGE';
};
+type UpscaleInitialImageAction = {
+ type: 'SET_UPSCALE_INITIAL_IMAGE';
+};
+
type ToastAction = {
type: 'TOAST';
title?: string;
@@ -223,4 +227,5 @@ export type PostUploadAction =
| CALayerImagePostUploadAction
| IPALayerImagePostUploadAction
| RGLayerIPAdapterImagePostUploadAction
- | IILayerImagePostUploadAction;
+ | IILayerImagePostUploadAction
+ | UpscaleInitialImageAction;
diff --git a/invokeai/version/invokeai_version.py b/invokeai/version/invokeai_version.py
index 09545bce26..6e96fc64ae 100644
--- a/invokeai/version/invokeai_version.py
+++ b/invokeai/version/invokeai_version.py
@@ -1 +1 @@
-__version__ = "4.2.6post1"
+__version__ = "4.2.7"
diff --git a/pyproject.toml b/pyproject.toml
index 9953c1c1a0..9acaa17e44 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -55,7 +55,7 @@ dependencies = [
"transformers==4.41.1",
# Core application dependencies, pinned for reproducible builds.
- "fastapi-events==0.11.0",
+ "fastapi-events==0.11.1",
"fastapi==0.111.0",
"huggingface-hub==0.23.1",
"pydantic-settings==2.2.1",
diff --git a/tests/app/services/model_install/test_model_install.py b/tests/app/services/model_install/test_model_install.py
index 5e4f2a389f..a144356b47 100644
--- a/tests/app/services/model_install/test_model_install.py
+++ b/tests/app/services/model_install/test_model_install.py
@@ -72,14 +72,16 @@ def test_registration_meta(mm2_installer: ModelInstallServiceBase, embedding_fil
def test_registration_meta_override_fail(mm2_installer: ModelInstallServiceBase, embedding_file: Path) -> None:
key = None
with pytest.raises((ValidationError, InvalidModelConfigException)):
- key = mm2_installer.register_path(embedding_file, {"name": "banana_sushi", "type": ModelType("lora")})
+ key = mm2_installer.register_path(
+ embedding_file, ModelRecordChanges(name="banana_sushi", type=ModelType("lora"))
+ )
assert key is None
def test_registration_meta_override_succeed(mm2_installer: ModelInstallServiceBase, embedding_file: Path) -> None:
store = mm2_installer.record_store
key = mm2_installer.register_path(
- embedding_file, {"name": "banana_sushi", "source": "fake/repo_id", "key": "xyzzy"}
+ embedding_file, ModelRecordChanges(name="banana_sushi", source="fake/repo_id", key="xyzzy")
)
model_record = store.get_model(key)
assert model_record.name == "banana_sushi"
@@ -131,7 +133,7 @@ def test_background_install(
path: Path = request.getfixturevalue(fixture_name)
description = "Test of metadata assignment"
source = LocalModelSource(path=path, inplace=False)
- job = mm2_installer.import_model(source, config={"description": description})
+ job = mm2_installer.import_model(source, config=ModelRecordChanges(description=description))
assert job is not None
assert isinstance(job, ModelInstallJob)