diff --git a/README.md b/README.md index c06a1dddf30..048e5b4dced 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Invoke features an organized gallery system for easily storing, accessing, and r - Flux.2 Klein 9B - Z-Image Turbo - Z-Image Base +- Krea 2 Turbo - Anima - Qwen Image - Qwen Image Edit diff --git a/docs/src/content/docs/features/krea-2.mdx b/docs/src/content/docs/features/krea-2.mdx new file mode 100644 index 00000000000..755a8581d40 --- /dev/null +++ b/docs/src/content/docs/features/krea-2.mdx @@ -0,0 +1,71 @@ +--- +title: Krea-2 +description: Generate images with the Krea-2 text-to-image models (Turbo and Raw), including the GGUF / single-file workflow and the conditioning enhancers. +lastUpdated: 2026-07-13 +sidebar: + order: 5 +--- + +Krea-2 is a ~12B single-stream diffusion-transformer text-to-image family. InvokeAI supports both +published checkpoints: + +- **Krea-2-Turbo** — distilled for fast, low-step generation. Runs at **8 steps** with **CFG disabled** + (CFG Scale `1.0`). This is the recommended checkpoint for everyday use. +- **Krea-2-Raw** — the undistilled base checkpoint. Runs at **more steps (~28)** with **CFG enabled** + (CFG Scale ~`5.5`, equivalent to the reference pipeline's guidance `4.5`) and supports negative prompts. It is primarily intended as a base for + finetuning / LoRA training, but full inference is supported. + +The variant is detected automatically on install, and selecting a Krea-2 model sets sensible defaults +(steps, CFG, 1024×1024) for that variant. + +## Hardware + +Krea-2 is a large model. See the [System Requirements](../../start-here/system-requirements) table for +details. In short, on a 24 GB card enable **FP8** in the model's Default Settings to fit 1024² (with a +LoRA). For lower VRAM, use a **GGUF** transformer (Q4_K ≈ 12 GB total). + +## Installing + +The easiest path is the **Krea-2 launchpad bundle** in the Model Manager, which installs the models and +their dependencies together. + +Krea-2 needs three components: + +| Component | Diffusers install | GGUF / single-file install | +| ----------------------- | ----------------------- | ------------------------------------ | +| Transformer | bundled in the pipeline | the `.gguf` / single-file checkpoint | +| VAE (Qwen-Image) | bundled | installed separately | +| Text encoder (Qwen3-VL) | bundled | installed separately | + +- **Diffusers** (e.g. `krea/Krea-2-Turbo`, `krea/Krea-2-Raw`): a single ~26 GB install that bundles the + VAE and text encoder. Nothing else is required. +- **GGUF / single-file**: the checkpoint ships **only the transformer**. You must also install a + standalone **Qwen-Image VAE** and a **Qwen3-VL encoder** (both included in the launchpad bundle). + +When you select a GGUF/single-file Krea-2 model, InvokeAI auto-selects an installed VAE and Qwen3-VL +encoder if you have them. If none are installed, you'll be prompted to pick them (in the model dropdowns) +before you can generate. Selecting a Diffusers Krea-2 model clears those standalone selections and uses +the bundled components. + +:::note[Qwen3-VL encoder download] +Single-file Qwen3-VL encoders bundle only the weights. The tokenizer/config are fetched once from +HuggingFace (`Qwen/Qwen3-VL-4B-Instruct`) on first use, then cached for offline use. A single-file fp8 +encoder is kept resident in fp8 (roughly half the VRAM of the bf16 encoder). +::: + +## Conditioning enhancers + +Two optional, off-by-default toggles are available under **Advanced Options** (below CFG Scale). They +transform the text conditioning and are especially useful for the distilled Turbo checkpoint: + +- **Conditioning Rebalance** — per-layer weighting of the text embedding to improve prompt adherence. +- **Seed Variance Enhancer** — injects controlled noise into the conditioning to restore per-seed + diversity (the distilled model otherwise produces near-identical images across seeds), trading some + prompt adherence for variety. + +Both are recorded in image metadata and can be recalled. + +## LoRA + +Krea-2 LoRAs (diffusers PEFT format) are supported and apply to both the transformer and — where the +LoRA includes text-encoder layers — the Qwen3-VL encoder. diff --git a/docs/src/content/docs/start-here/system-requirements.mdx b/docs/src/content/docs/start-here/system-requirements.mdx index 114698ce158..4e57d106340 100644 --- a/docs/src/content/docs/start-here/system-requirements.mdx +++ b/docs/src/content/docs/start-here/system-requirements.mdx @@ -28,6 +28,7 @@ The requirements below are rough guidelines for best performance. GPUs with less | FLUX.2 Klein 4B | 1024x1024 | Nvidia 30xx+ | 12GB | 16GB | FP8 works with 8GB+; Diffusers + encoder | | FLUX.2 Klein 9B | 1024x1024 | Nvidia 40xx | 24GB | 32GB | FP8 works with 12GB+; Diffusers + encoder | | Z-Image Turbo | 1024x1024 | Nvidia 20xx+ | 8GB | 16GB | Q4_K 8GB; Q8/BF16 16GB+ | +| Krea-2 (Turbo / Raw) | 1024x1024 | Nvidia 40xx | 24GB | 32GB | FP8 works with 16GB+; GGUF Q4_K ~12GB. Diffusers ~26GB; GGUF needs a standalone VAE + Qwen3-VL encoder | :::tip[`tmpfs` on Linux] If your temporary directory is mounted as a `tmpfs`, ensure it has sufficient space. diff --git a/invokeai/app/api/dependencies.py b/invokeai/app/api/dependencies.py index 3092f5ab71a..b2a60a0541a 100644 --- a/invokeai/app/api/dependencies.py +++ b/invokeai/app/api/dependencies.py @@ -60,6 +60,7 @@ CogView4ConditioningInfo, ConditioningFieldData, FLUXConditioningInfo, + Krea2ConditioningInfo, QwenImageConditioningInfo, SD3ConditioningInfo, SDXLConditioningInfo, @@ -153,6 +154,7 @@ def initialize( CogView4ConditioningInfo, ZImageConditioningInfo, QwenImageConditioningInfo, + Krea2ConditioningInfo, AnimaConditioningInfo, ], ephemeral=True, diff --git a/invokeai/app/invocations/fields.py b/invokeai/app/invocations/fields.py index 4418c86371a..dcbfe1f08b9 100644 --- a/invokeai/app/invocations/fields.py +++ b/invokeai/app/invocations/fields.py @@ -156,6 +156,7 @@ class FieldDescriptions: t5_encoder = "T5 tokenizer and text encoder" glm_encoder = "GLM (THUDM) tokenizer and text encoder" qwen3_encoder = "Qwen3 tokenizer and text encoder" + qwen3_vl_encoder = "Qwen3-VL tokenizer and text encoder" clip_embed_model = "CLIP Embed loader" clip_g_model = "CLIP-G Embed loader" unet = "UNet (scheduler, LoRAs)" @@ -172,6 +173,7 @@ class FieldDescriptions: sd3_model = "SD3 model (MMDiTX) to load" cogview4_model = "CogView4 model (Transformer) to load" z_image_model = "Z-Image model (Transformer) to load" + krea2_model = "Krea-2 model (Transformer) to load" qwen_image_model = "Qwen Image Edit model (Transformer) to load" qwen_vl_encoder = "Qwen2.5-VL tokenizer, processor and text/vision encoder" sdxl_main_model = "SDXL Main model (UNet, VAE, CLIP1, CLIP2) to load" @@ -350,6 +352,12 @@ class QwenImageConditioningField(BaseModel): conditioning_name: str = Field(description="The name of conditioning tensor") +class Krea2ConditioningField(BaseModel): + """A Krea-2 conditioning tensor primitive value""" + + conditioning_name: str = Field(description="The name of conditioning tensor") + + class AnimaConditioningField(BaseModel): """An Anima conditioning tensor primitive value. diff --git a/invokeai/app/invocations/krea2_conditioning_rebalance.py b/invokeai/app/invocations/krea2_conditioning_rebalance.py new file mode 100644 index 00000000000..ab52a5033ae --- /dev/null +++ b/invokeai/app/invocations/krea2_conditioning_rebalance.py @@ -0,0 +1,79 @@ +import math + +import torch + +from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation +from invokeai.app.invocations.fields import FieldDescriptions, Input, InputField, Krea2ConditioningField +from invokeai.app.invocations.primitives import Krea2ConditioningOutput +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.krea2.sampling_utils import KREA2_SELECT_LAYERS +from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ( + ConditioningFieldData, + Krea2ConditioningInfo, +) + +_NUM_TEXT_LAYERS = len(KREA2_SELECT_LAYERS) # 12 + + +@invocation( + "krea2_conditioning_rebalance", + title="Conditioning Rebalance - Krea-2", + tags=["conditioning", "krea2", "krea-2"], + category="conditioning", + version="1.0.0", + classification=Classification.Prototype, +) +class Krea2ConditioningRebalanceInvocation(BaseInvocation): + """Per-layer rebalancing of Krea-2 text conditioning (improves prompt adherence). + + Krea-2 conditioning stacks 12 Qwen3-VL hidden-state layers per token. Weighting those layers + individually (and applying an overall multiplier) lets you push the model harder toward the prompt, + counteracting the quality-dilution from distillation. Ported from the ComfyUI + `ConditioningKrea2Rebalance` node. This is an optional pass between the text encoder and denoise. + """ + + conditioning: Krea2ConditioningField = InputField( + description=FieldDescriptions.cond, input=Input.Connection, title="Conditioning" + ) + per_layer_weights: str = InputField( + default="1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0", + description=f"Comma-separated gains for the {_NUM_TEXT_LAYERS} tapped encoder layers (exactly " + f"{_NUM_TEXT_LAYERS} values).", + ) + multiplier: float = InputField( + default=4.0, + allow_inf_nan=False, + description="Overall multiplier applied to the conditioning after per-layer weighting.", + ) + + def _parse_weights(self) -> list[float]: + try: + weights = [float(x.strip()) for x in self.per_layer_weights.split(",") if x.strip() != ""] + except ValueError as e: + raise ValueError(f"per_layer_weights must be comma-separated numbers: {e}") from e + if len(weights) != _NUM_TEXT_LAYERS: + raise ValueError(f"per_layer_weights must have exactly {_NUM_TEXT_LAYERS} values, got {len(weights)}.") + if not all(math.isfinite(weight) for weight in weights): + raise ValueError("per_layer_weights must contain only finite values.") + return weights + + @torch.no_grad() + def invoke(self, context: InvocationContext) -> Krea2ConditioningOutput: + weights = self._parse_weights() + + cond_data = context.conditioning.load(self.conditioning.conditioning_name) + assert len(cond_data.conditionings) == 1 + conditioning = cond_data.conditionings[0] + assert isinstance(conditioning, Krea2ConditioningInfo) + + embeds = conditioning.prompt_embeds # (B, seq, 12, hidden) + gains = torch.tensor(weights, dtype=embeds.dtype, device=embeds.device).view(1, 1, _NUM_TEXT_LAYERS, 1) + embeds = embeds * gains * self.multiplier + + new_data = ConditioningFieldData( + conditionings=[ + Krea2ConditioningInfo(prompt_embeds=embeds, prompt_embeds_mask=conditioning.prompt_embeds_mask) + ] + ) + conditioning_name = context.conditioning.save(new_data) + return Krea2ConditioningOutput.build(conditioning_name) diff --git a/invokeai/app/invocations/krea2_denoise.py b/invokeai/app/invocations/krea2_denoise.py new file mode 100644 index 00000000000..7191bd463de --- /dev/null +++ b/invokeai/app/invocations/krea2_denoise.py @@ -0,0 +1,479 @@ +import json +import math +from contextlib import ExitStack +from pathlib import Path +from typing import Callable, Iterator, Optional, Tuple + +import torch +import torchvision.transforms as tv_transforms +from pydantic import field_validator +from torchvision.transforms.functional import resize as tv_resize +from tqdm import tqdm + +from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation +from invokeai.app.invocations.constants import LATENT_SCALE_FACTOR +from invokeai.app.invocations.fields import ( + DenoiseMaskField, + FieldDescriptions, + Input, + InputField, + Krea2ConditioningField, + LatentsField, + WithBoard, + WithMetadata, +) +from invokeai.app.invocations.model import TransformerField +from invokeai.app.invocations.primitives import LatentsOutput +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.krea2.sampling_utils import ( + KREA2_BASE_IMAGE_SEQ_LEN, + KREA2_BASE_SHIFT, + KREA2_DISTILLED_MU, + KREA2_MAX_IMAGE_SEQ_LEN, + KREA2_MAX_SHIFT, + build_sigmas, + calculate_shift, + pack_latents, + prepare_position_ids, + unpack_latents, +) +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat +from invokeai.backend.patches.layer_patcher import LayerPatcher +from invokeai.backend.patches.lora_conversions.krea2_lora_constants import KREA2_LORA_TRANSFORMER_PREFIX +from invokeai.backend.patches.model_patch_raw import ModelPatchRaw +from invokeai.backend.rectified_flow.rectified_flow_inpaint_extension import RectifiedFlowInpaintExtension +from invokeai.backend.stable_diffusion.diffusers_pipeline import PipelineIntermediateState +from invokeai.backend.stable_diffusion.diffusion.conditioning_data import Krea2ConditioningInfo +from invokeai.backend.util.devices import TorchDevice + +# Krea-2 latent channels (Qwen-Image VAE z_dim). The packed transformer in_channels is 16 * patch_size**2 = 64. +KREA2_LATENT_CHANNELS = 16 + + +@invocation( + "krea2_denoise", + title="Denoise - Krea-2", + tags=["image", "krea2", "krea-2"], + category="image", + version="1.0.0", + classification=Classification.Prototype, +) +class Krea2DenoiseInvocation(BaseInvocation, WithMetadata, WithBoard): + """Run the denoising process with a Krea-2 model.""" + + # If latents is provided, this means we are doing image-to-image. + latents: Optional[LatentsField] = InputField( + default=None, description=FieldDescriptions.latents, input=Input.Connection + ) + # denoise_mask is used for image-to-image inpainting. Only the masked region is modified. + denoise_mask: Optional[DenoiseMaskField] = InputField( + default=None, description=FieldDescriptions.denoise_mask, input=Input.Connection + ) + denoising_start: float = InputField(default=0.0, ge=0, le=1, description=FieldDescriptions.denoising_start) + denoising_end: float = InputField(default=1.0, ge=0, le=1, description=FieldDescriptions.denoising_end) + transformer: TransformerField = InputField( + description=FieldDescriptions.krea2_model, input=Input.Connection, title="Transformer" + ) + positive_conditioning: Krea2ConditioningField = InputField( + description=FieldDescriptions.positive_cond, input=Input.Connection + ) + negative_conditioning: Optional[Krea2ConditioningField] = InputField( + default=None, description=FieldDescriptions.negative_cond, input=Input.Connection + ) + # CFG uses the standard formulation (uncond + cfg_scale*(cond-uncond)); cfg_scale <= 1 disables it. + # Krea-2-Turbo is distilled and runs with CFG disabled (cfg_scale=1.0). + cfg_scale: float | list[float] = InputField(default=1.0, description=FieldDescriptions.cfg_scale, title="CFG Scale") + width: int = InputField(default=1024, gt=0, multiple_of=16, description="Width of the generated image.") + height: int = InputField(default=1024, gt=0, multiple_of=16, description="Height of the generated image.") + steps: int = InputField(default=8, gt=0, description=FieldDescriptions.steps) + seed: int = InputField(default=0, description="Randomness seed for reproducibility.") + shift: Optional[float] = InputField( + default=None, + description="Override the resolution-aware timestep shift (mu). Leave unset to use the model default " + "(mu=1.15 for the distilled Turbo checkpoint).", + ) + + @field_validator("cfg_scale") + @classmethod + def validate_cfg_scale_is_finite(cls, value: float | list[float]) -> float | list[float]: + values = value if isinstance(value, list) else [value] + if not all(math.isfinite(item) for item in values): + raise ValueError("cfg_scale values must be finite.") + return value + + @field_validator("shift") + @classmethod + def validate_shift_is_finite(cls, value: float | None) -> float | None: + if value is not None and not math.isfinite(value): + raise ValueError("shift must be finite.") + return value + + @torch.no_grad() + def invoke(self, context: InvocationContext) -> LatentsOutput: + latents = self._run_diffusion(context) + latents = latents.detach().to("cpu") + name = context.tensors.save(tensor=latents) + return LatentsOutput.build(latents_name=name, latents=latents, seed=None) + + def _prep_inpaint_mask(self, context: InvocationContext, latents: torch.Tensor) -> torch.Tensor | None: + if self.denoise_mask is None: + return None + mask = context.tensors.load(self.denoise_mask.mask_name) + mask = 1.0 - mask + _, _, latent_height, latent_width = latents.shape + mask = tv_resize( + img=mask, + size=[latent_height, latent_width], + interpolation=tv_transforms.InterpolationMode.BILINEAR, + antialias=False, + ) + mask = mask.to(device=latents.device, dtype=latents.dtype) + return mask + + def _load_text_conditioning( + self, + context: InvocationContext, + conditioning_name: str, + dtype: torch.dtype, + device: torch.device, + ) -> tuple[torch.Tensor, torch.Tensor | None]: + cond_data = context.conditioning.load(conditioning_name) + assert len(cond_data.conditionings) == 1 + conditioning = cond_data.conditionings[0] + assert isinstance(conditioning, Krea2ConditioningInfo) + conditioning = conditioning.to(dtype=dtype, device=device) + return conditioning.prompt_embeds, conditioning.prompt_embeds_mask + + def _get_noise(self, height: int, width: int, dtype: torch.dtype, device: torch.device, seed: int) -> torch.Tensor: + rand_device = "cpu" + return torch.randn( + 1, + KREA2_LATENT_CHANNELS, + int(height) // LATENT_SCALE_FACTOR, + int(width) // LATENT_SCALE_FACTOR, + device=rand_device, + dtype=torch.float32, + generator=torch.Generator(device=rand_device).manual_seed(seed), + ).to(device=device, dtype=dtype) + + def _prepare_cfg_scale(self, num_timesteps: int) -> list[float]: + if isinstance(self.cfg_scale, float): + return [self.cfg_scale] * num_timesteps + if isinstance(self.cfg_scale, list): + if len(self.cfg_scale) != num_timesteps: + raise ValueError( + f"cfg_scale list has {len(self.cfg_scale)} values but the model is configured for " + f"{num_timesteps} steps. Provide one CFG value per configured step (or a single float)." + ) + return self.cfg_scale + raise ValueError(f"Invalid CFG scale type: {type(self.cfg_scale)}") + + @staticmethod + def _should_apply_cfg_for_step(cfg_scale: float, *, has_negative_conditioning: bool) -> bool: + return has_negative_conditioning and cfg_scale > 1.0 + + @staticmethod + def _validate_effective_schedule(*, start_idx: int, end_idx: int) -> None: + if end_idx <= start_idx: + raise ValueError( + "The requested denoising range does not contain any effective denoising steps at the configured " + "step count. Increase denoising_end, decrease denoising_start, or increase steps." + ) + + def _validate_inputs(self) -> None: + if self.denoising_start >= self.denoising_end: + raise ValueError("denoising_start must be less than denoising_end.") + if self.denoise_mask is not None and self.latents is None: + raise ValueError("Initial latents are required when a denoise mask is provided.") + + def _is_distilled(self, context: InvocationContext) -> bool: + """Whether the transformer is the distilled Turbo checkpoint (fixed mu) vs. Raw (dynamic mu). + + Prefer the classified variant (works for diffusers, single-file and GGUF alike); fall back to + the pipeline-level ``is_distilled`` flag in model_index.json, then default to distilled. + + A failed config lookup is a real error and is allowed to propagate — silently defaulting to the + Turbo shift would apply the wrong sampling schedule to a Raw model. + """ + from invokeai.backend.model_manager.taxonomy import Krea2VariantType + + config = context.models.get_config(self.transformer.transformer) + variant = getattr(config, "variant", None) + if variant is not None: + return variant != Krea2VariantType.Base + # No classified variant (unexpected for Krea-2) — fall back to the pipeline-level flag. Only a + # missing/malformed model_index.json is tolerated here; it defaults to the distilled behavior. + try: + model_index = context.models.get_absolute_path(config) / "model_index.json" + if model_index.is_file(): + with open(model_index) as f: + return bool(json.load(f).get("is_distilled", False)) + except (OSError, ValueError): + pass + return True + + def _run_diffusion(self, context: InvocationContext): + from diffusers.schedulers.scheduling_flow_match_euler_discrete import FlowMatchEulerDiscreteScheduler + + self._validate_inputs() + + device = TorchDevice.choose_torch_device() + inference_dtype = TorchDevice.choose_bfloat16_safe_dtype(device) + + transformer_info = context.models.load(self.transformer.transformer) + + pos_prompt_embeds, pos_prompt_mask = self._load_text_conditioning( + context, self.positive_conditioning.conditioning_name, inference_dtype, device + ) + + latent_height = self.height // LATENT_SCALE_FACTOR + latent_width = self.width // LATENT_SCALE_FACTOR + grid_height = latent_height // 2 + grid_width = latent_width // 2 + image_seq_len = grid_height * grid_width + + # Scheduler: load from the model's scheduler/ dir if present, else construct with Krea-2 defaults. + model_path = context.models.get_absolute_path(context.models.get_config(self.transformer.transformer)) + scheduler_path = Path(model_path) / "scheduler" + if scheduler_path.is_dir() and (scheduler_path / "scheduler_config.json").exists(): + scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained(str(scheduler_path), local_files_only=True) + else: + scheduler = FlowMatchEulerDiscreteScheduler( + use_dynamic_shifting=True, + base_shift=0.5, + max_shift=1.15, + base_image_seq_len=256, + max_image_seq_len=6400, + num_train_timesteps=1000, + time_shift_type="exponential", + ) + + if self.shift is not None: + mu = self.shift + elif self._is_distilled(context): + mu = KREA2_DISTILLED_MU + else: + # Resolution-aware shift. Honor the loaded scheduler's config so a Raw checkpoint that ships a + # customized scheduler_config.json is sampled with its own shift params, falling back to the + # Krea-2 defaults for the stock scheduler (and for any field the config omits). + mu = calculate_shift( + image_seq_len, + base_image_seq_len=getattr(scheduler.config, "base_image_seq_len", KREA2_BASE_IMAGE_SEQ_LEN), + max_image_seq_len=getattr(scheduler.config, "max_image_seq_len", KREA2_MAX_IMAGE_SEQ_LEN), + base_shift=getattr(scheduler.config, "base_shift", KREA2_BASE_SHIFT), + max_shift=getattr(scheduler.config, "max_shift", KREA2_MAX_SHIFT), + ) + + init_sigmas = build_sigmas(self.steps) + scheduler.set_timesteps(sigmas=init_sigmas, mu=mu, device=device) + + # Clip the schedule based on denoising_start/denoising_end for img2img strength. + sigmas_sched = scheduler.sigmas # (N+1,) including terminal 0 + total_sigmas = len(sigmas_sched) - 1 # == self.steps + is_clipped = self.denoising_start > 0 or self.denoising_end < 1 + start_idx = int(round(self.denoising_start * total_sigmas)) + end_idx = int(round(self.denoising_end * total_sigmas)) + self._validate_effective_schedule(start_idx=start_idx, end_idx=end_idx) + if is_clipped: + sigmas_sched = sigmas_sched[start_idx : end_idx + 1] + timesteps_sched = sigmas_sched[:-1] * scheduler.config.num_train_timesteps + else: + timesteps_sched = scheduler.timesteps + + total_steps = len(timesteps_sched) + + # Build the CFG schedule against the FULL step count, then clip it to the active window. This way a + # caller-supplied per-step CFG list (one value per configured step) survives the reduction caused + # by denoising_start/denoising_end; a scalar is simply broadcast. + full_cfg_scale = self._prepare_cfg_scale(total_sigmas) + cfg_scale = full_cfg_scale[start_idx:end_idx] if is_clipped else full_cfg_scale + + # Load negative conditioning only if at least one active step actually uses CFG. CFG is still + # decided per step below so values at or below 1.0 use the conditional prediction directly. + has_negative_conditioning = self.negative_conditioning is not None + do_cfg = has_negative_conditioning and any(value > 1.0 for value in cfg_scale) + neg_prompt_embeds = None + neg_prompt_mask = None + if do_cfg and self.negative_conditioning is not None: + neg_prompt_embeds, neg_prompt_mask = self._load_text_conditioning( + context, self.negative_conditioning.conditioning_name, inference_dtype, device + ) + + # Load initial latents (img2img). + init_latents = context.tensors.load(self.latents.latents_name) if self.latents else None + if init_latents is not None: + init_latents = init_latents.to(device=device, dtype=inference_dtype) + if init_latents.dim() == 5: + init_latents = init_latents.squeeze(2) + + noise = self._get_noise(self.height, self.width, inference_dtype, device, self.seed) + + if init_latents is not None: + s_0 = sigmas_sched[0].item() + latents = s_0 * noise + (1.0 - s_0) * init_latents + else: + if self.denoising_start > 1e-5: + raise ValueError("denoising_start should be 0 when initial latents are not provided.") + latents = noise + + # Pack latents into 2x2 patches: (B, C, H, W) -> (B, grid_h*grid_w, C*4). + latents = pack_latents(latents, 1, KREA2_LATENT_CHANNELS, latent_height, latent_width) + + # Position ids: text tokens at origin, image tokens carry their grid coords. + text_seq_len = pos_prompt_embeds.shape[1] + position_ids = prepare_position_ids(text_seq_len, grid_height, grid_width, device) + # The negative prompt can tokenize to a different length than the positive prompt, so it needs its + # own position ids. Reusing the positive ids would leave the rotary embedding (text + image tokens) + # a different length than the uncond query sequence and crash in the transformer's apply_rotary_emb. + neg_position_ids = ( + prepare_position_ids(neg_prompt_embeds.shape[1], grid_height, grid_width, device) + if neg_prompt_embeds is not None + else None + ) + + # Inpaint extension operates in 4D, so unpack/repack around each merge. + inpaint_mask = self._prep_inpaint_mask(context, noise) + inpaint_extension: RectifiedFlowInpaintExtension | None = None + if inpaint_mask is not None: + assert init_latents is not None + inpaint_extension = RectifiedFlowInpaintExtension( + init_latents=init_latents, inpaint_mask=inpaint_mask, noise=noise + ) + + step_callback = self._build_step_callback(context) + step_callback( + PipelineIntermediateState( + step=0, + order=1, + total_steps=total_steps, + timestep=int(timesteps_sched[0].item()) if total_steps > 0 else 0, + latents=unpack_latents(latents, latent_height, latent_width), + ), + ) + + transformer_config = context.models.get_config(self.transformer.transformer) + model_is_quantized = transformer_config.format in (ModelFormat.GGUFQuantized,) + num_train_timesteps = scheduler.config.num_train_timesteps + + # Estimate the peak working memory (activations) the transformer forward needs and ask the model + # cache to keep that much VRAM free. The cache offloads as much of the (resident) model to RAM as + # required to honor this — only consequential at higher resolutions, where the activation footprint + # over text+image tokens grows enough that a fully-resident ~12B model would otherwise leave no + # headroom. Without this hint the cache reserves only the small default working memory and places + # the model before LoRA patches are applied, so a model+LoRA combination that just fits the base + # forward OOMs once the LoRA's extra activations are added. + estimated_working_memory = self._estimate_working_memory( + image_seq_len=image_seq_len, + do_cfg=do_cfg, + num_loras=len(self.transformer.loras), + ) + + with ExitStack() as exit_stack: + (cached_weights, transformer) = exit_stack.enter_context( + transformer_info.model_on_device(working_mem_bytes=estimated_working_memory) + ) + + exit_stack.enter_context( + LayerPatcher.apply_smart_model_patches( + model=transformer, + patches=self._lora_iterator(context), + prefix=KREA2_LORA_TRANSFORMER_PREFIX, + dtype=inference_dtype, + cached_weights=cached_weights, + force_sidecar_patching=model_is_quantized, + ) + ) + + for step_idx, t in enumerate(tqdm(timesteps_sched)): + # The pipeline passes timestep / num_train_timesteps to the transformer. + timestep = (t / num_train_timesteps).expand(latents.shape[0]).to(inference_dtype) + + noise_pred_cond = transformer( + hidden_states=latents, + encoder_hidden_states=pos_prompt_embeds, + encoder_attention_mask=pos_prompt_mask, + timestep=timestep, + position_ids=position_ids, + return_dict=False, + )[0] + + if self._should_apply_cfg_for_step( + cfg_scale[step_idx], has_negative_conditioning=neg_prompt_embeds is not None + ): + noise_pred_uncond = transformer( + hidden_states=latents, + encoder_hidden_states=neg_prompt_embeds, + encoder_attention_mask=neg_prompt_mask, + timestep=timestep, + position_ids=neg_position_ids, + return_dict=False, + )[0] + noise_pred = noise_pred_uncond + cfg_scale[step_idx] * (noise_pred_cond - noise_pred_uncond) + else: + noise_pred = noise_pred_cond + + # Euler step using the (possibly clipped) sigma schedule. + sigma_curr = sigmas_sched[step_idx] + sigma_next = sigmas_sched[step_idx + 1] + dt = sigma_next - sigma_curr + latents = latents.to(torch.float32) + dt * noise_pred.to(torch.float32) + latents = latents.to(inference_dtype) + + if inpaint_extension is not None: + sigma_next_f = sigmas_sched[step_idx + 1].item() + latents_4d = unpack_latents(latents, latent_height, latent_width) + latents_4d = inpaint_extension.merge_intermediate_latents_with_init_latents( + latents_4d, sigma_next_f + ) + latents = pack_latents(latents_4d, 1, KREA2_LATENT_CHANNELS, latent_height, latent_width) + + step_callback( + PipelineIntermediateState( + step=step_idx + 1, + order=1, + total_steps=total_steps, + timestep=int(t.item()), + latents=unpack_latents(latents, latent_height, latent_width), + ), + ) + + # Unpack to 4D then add a frame dim for the Qwen-Image VAE: (B, C, 1, H, W). + latents = unpack_latents(latents, latent_height, latent_width) + latents = latents.unsqueeze(2) + return latents + + def _estimate_working_memory(self, image_seq_len: int, do_cfg: bool, num_loras: int) -> int: + """Estimate peak transformer activation memory (bytes) so the model cache reserves enough headroom. + + The MMDiT activation footprint scales with the number of image tokens. The per-token figure is + calibrated empirically against the Krea-2-Turbo transformer in bf16 (~2.6 MiB/token covers the + attention + feed-forward intermediates and the transient fp8->bf16 weight casts). LoRA sidecar + patches add their own (small) weights plus an extra activation branch per patched layer, so we add + a fixed margin per LoRA on top. + """ + GB = 1024**3 + per_token_bytes = int(2.6 * 1024 * 1024) + estimated = image_seq_len * per_token_bytes + if do_cfg: + # Conditional/unconditional passes are sequential, but the larger combined sequence and extra + # transient buffers warrant a modest bump. + estimated = int(estimated * 1.1) + if num_loras > 0: + estimated += int((1.5 + 0.5 * num_loras) * GB) + return estimated + + def _build_step_callback(self, context: InvocationContext) -> Callable[[PipelineIntermediateState], None]: + def step_callback(state: PipelineIntermediateState) -> None: + context.util.sd_step_callback(state, BaseModelType.Krea2) + + return step_callback + + def _lora_iterator(self, context: InvocationContext) -> Iterator[Tuple[ModelPatchRaw, float]]: + for lora in self.transformer.loras: + lora_info = context.models.load(lora.lora) + if not isinstance(lora_info.model, ModelPatchRaw): + raise TypeError( + f"Expected ModelPatchRaw for LoRA '{lora.lora.key}', got {type(lora_info.model).__name__}." + ) + yield (lora_info.model, lora.weight) + del lora_info diff --git a/invokeai/app/invocations/krea2_lora_loader.py b/invokeai/app/invocations/krea2_lora_loader.py new file mode 100644 index 00000000000..a63dd4b3252 --- /dev/null +++ b/invokeai/app/invocations/krea2_lora_loader.py @@ -0,0 +1,185 @@ +from typing import Optional + +from invokeai.app.invocations.baseinvocation import ( + BaseInvocation, + BaseInvocationOutput, + invocation, + invocation_output, +) +from invokeai.app.invocations.fields import FieldDescriptions, Input, InputField, OutputField +from invokeai.app.invocations.model import LoRAField, ModelIdentifierField, Qwen3VLEncoderField, TransformerField +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType + + +@invocation_output("krea2_lora_loader_output") +class Krea2LoRALoaderOutput(BaseInvocationOutput): + """Krea-2 LoRA Loader Output""" + + transformer: Optional[TransformerField] = OutputField( + default=None, description=FieldDescriptions.transformer, title="Krea-2 Transformer" + ) + qwen3_vl_encoder: Optional[Qwen3VLEncoderField] = OutputField( + default=None, description=FieldDescriptions.qwen3_vl_encoder, title="Qwen3-VL Encoder" + ) + + +@invocation( + "krea2_lora_loader", + title="Apply LoRA - Krea-2", + tags=["lora", "model", "krea2", "krea-2"], + category="model", + version="1.0.0", +) +class Krea2LoRALoaderInvocation(BaseInvocation): + """Apply a LoRA model to a Krea-2 transformer and/or Qwen3-VL text encoder.""" + + lora: ModelIdentifierField = InputField( + description=FieldDescriptions.lora_model, + title="LoRA", + ui_model_base=BaseModelType.Krea2, + ui_model_type=ModelType.LoRA, + ) + weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight) + transformer: TransformerField | None = InputField( + default=None, + description=FieldDescriptions.transformer, + input=Input.Connection, + title="Krea-2 Transformer", + ) + qwen3_vl_encoder: Qwen3VLEncoderField | None = InputField( + default=None, + title="Qwen3-VL Encoder", + description=FieldDescriptions.qwen3_vl_encoder, + input=Input.Connection, + ) + + def invoke(self, context: InvocationContext) -> Krea2LoRALoaderOutput: + lora_key = self.lora.key + + if not context.models.exists(lora_key): + raise ValueError(f"Unknown lora: {lora_key}!") + + stored_config = context.models.get_config(lora_key) + if ( + self.lora.base is not BaseModelType.Krea2 + or stored_config.base is not BaseModelType.Krea2 + or stored_config.type is not ModelType.LoRA + ): + raise ValueError( + f"LoRA '{lora_key}' is for {stored_config.base.value if stored_config.base else 'unknown'} models, " + "not Krea-2 models. Ensure you are using a Krea-2 compatible LoRA." + ) + + output = Krea2LoRALoaderOutput() + + if self.transformer is not None: + output.transformer = self.transformer.model_copy(deep=True) + if self.qwen3_vl_encoder is not None: + output.qwen3_vl_encoder = self.qwen3_vl_encoder.model_copy(deep=True) + + transformer_lora = ( + next((item for item in output.transformer.loras if item.lora.key == lora_key), None) + if output.transformer is not None + else None + ) + encoder_lora = ( + next((item for item in output.qwen3_vl_encoder.loras if item.lora.key == lora_key), None) + if output.qwen3_vl_encoder is not None + else None + ) + if transformer_lora is not None and encoder_lora is not None and transformer_lora.weight != encoder_lora.weight: + raise ValueError( + f"LoRA '{lora_key}' has conflicting weights on the transformer ({transformer_lora.weight}) and " + f"Qwen3-VL encoder ({encoder_lora.weight})." + ) + effective_lora = transformer_lora or encoder_lora or LoRAField(lora=self.lora, weight=self.weight) + + if output.transformer is not None and transformer_lora is None: + output.transformer.loras.append(effective_lora.model_copy(deep=True)) + if output.qwen3_vl_encoder is not None and encoder_lora is None: + output.qwen3_vl_encoder.loras.append(effective_lora.model_copy(deep=True)) + + return output + + +@invocation( + "krea2_lora_collection_loader", + title="Apply LoRA Collection - Krea-2", + tags=["lora", "model", "krea2", "krea-2"], + category="model", + version="1.0.0", +) +class Krea2LoRACollectionLoader(BaseInvocation): + """Applies a collection of LoRAs to a Krea-2 transformer and/or Qwen3-VL encoder.""" + + loras: Optional[LoRAField | list[LoRAField]] = InputField( + default=None, description="LoRA models and weights. May be a single LoRA or collection.", title="LoRAs" + ) + transformer: Optional[TransformerField] = InputField( + default=None, + description=FieldDescriptions.transformer, + input=Input.Connection, + title="Transformer", + ) + qwen3_vl_encoder: Qwen3VLEncoderField | None = InputField( + default=None, + title="Qwen3-VL Encoder", + description=FieldDescriptions.qwen3_vl_encoder, + input=Input.Connection, + ) + + def invoke(self, context: InvocationContext) -> Krea2LoRALoaderOutput: + output = Krea2LoRALoaderOutput() + loras = self.loras if isinstance(self.loras, list) else [self.loras] + if self.transformer is not None: + output.transformer = self.transformer.model_copy(deep=True) + if self.qwen3_vl_encoder is not None: + output.qwen3_vl_encoder = self.qwen3_vl_encoder.model_copy(deep=True) + + for lora in loras: + if lora is None: + continue + if not context.models.exists(lora.lora.key): + raise ValueError(f"Unknown lora: {lora.lora.key}!") + stored_config = context.models.get_config(lora.lora.key) + if ( + lora.lora.base is not BaseModelType.Krea2 + or stored_config.base is not BaseModelType.Krea2 + or stored_config.type is not ModelType.LoRA + ): + raise ValueError( + f"LoRA '{lora.lora.key}' is for " + f"{stored_config.base.value if stored_config.base else 'unknown'} models, " + "not Krea-2 models. Ensure you are using a Krea-2 compatible LoRA." + ) + + transformer_lora = ( + next((item for item in output.transformer.loras if item.lora.key == lora.lora.key), None) + if output.transformer is not None + else None + ) + encoder_lora = ( + next((item for item in output.qwen3_vl_encoder.loras if item.lora.key == lora.lora.key), None) + if output.qwen3_vl_encoder is not None + else None + ) + if ( + transformer_lora is not None + and encoder_lora is not None + and transformer_lora.weight != encoder_lora.weight + ): + raise ValueError( + f"LoRA '{lora.lora.key}' has conflicting weights on the transformer " + f"({transformer_lora.weight}) and Qwen3-VL encoder ({encoder_lora.weight})." + ) + effective_lora = transformer_lora or encoder_lora or lora + + if self.transformer is not None and output.transformer is not None: + if transformer_lora is None: + output.transformer.loras.append(effective_lora.model_copy(deep=True)) + if self.qwen3_vl_encoder is not None and output.qwen3_vl_encoder is not None: + if encoder_lora is None: + output.qwen3_vl_encoder.loras.append(effective_lora.model_copy(deep=True)) + + return output diff --git a/invokeai/app/invocations/krea2_model_loader.py b/invokeai/app/invocations/krea2_model_loader.py new file mode 100644 index 00000000000..5a010f726cf --- /dev/null +++ b/invokeai/app/invocations/krea2_model_loader.py @@ -0,0 +1,128 @@ +from typing import Optional + +from invokeai.app.invocations.baseinvocation import ( + BaseInvocation, + BaseInvocationOutput, + Classification, + invocation, + invocation_output, +) +from invokeai.app.invocations.fields import FieldDescriptions, Input, InputField, OutputField +from invokeai.app.invocations.model import ( + ModelIdentifierField, + Qwen3VLEncoderField, + TransformerField, + VAEField, +) +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType, SubModelType + + +@invocation_output("krea2_model_loader_output") +class Krea2ModelLoaderOutput(BaseInvocationOutput): + """Krea-2 base model loader output.""" + + transformer: TransformerField = OutputField(description=FieldDescriptions.transformer, title="Transformer") + qwen3_vl_encoder: Qwen3VLEncoderField = OutputField( + description=FieldDescriptions.qwen3_vl_encoder, title="Qwen3-VL Encoder" + ) + vae: VAEField = OutputField(description=FieldDescriptions.vae, title="VAE") + + +@invocation( + "krea2_model_loader", + title="Main Model - Krea-2", + tags=["model", "krea2", "krea-2"], + category="model", + version="1.0.0", + classification=Classification.Prototype, +) +class Krea2ModelLoaderInvocation(BaseInvocation): + """Loads a Krea-2 model, outputting its submodels. + + By default the VAE (Qwen-Image VAE) and Qwen3-VL text encoder are extracted from the Krea-2 + diffusers pipeline. Standalone overrides may be supplied (e.g. when the transformer is a + single-file checkpoint that has no bundled VAE / encoder). + """ + + model: ModelIdentifierField = InputField( + description=FieldDescriptions.krea2_model, + input=Input.Direct, + ui_model_base=BaseModelType.Krea2, + ui_model_type=ModelType.Main, + title="Transformer", + ) + + vae_model: Optional[ModelIdentifierField] = InputField( + default=None, + description="Standalone VAE model. Krea-2 uses the Qwen-Image VAE (16-channel). " + "If not provided, the VAE is loaded from the Krea-2 (diffusers) model.", + input=Input.Direct, + ui_model_base=[BaseModelType.QwenImage, BaseModelType.Anima], + ui_model_type=ModelType.VAE, + title="VAE", + ) + + qwen3_vl_encoder_model: Optional[ModelIdentifierField] = InputField( + default=None, + description="Standalone Qwen3-VL Encoder model. " + "If not provided, the encoder is loaded from the Krea-2 (diffusers) model.", + input=Input.Direct, + ui_model_type=ModelType.Qwen3VLEncoder, + title="Qwen3-VL Encoder", + ) + + def invoke(self, context: InvocationContext) -> Krea2ModelLoaderOutput: + main_config = context.models.get_config(self.model) + if main_config.base is not BaseModelType.Krea2 or main_config.type is not ModelType.Main: + raise ValueError( + f"Model '{main_config.name}' is not a Krea-2 main model. Select a Krea-2 transformer model." + ) + + # Transformer always comes from the main model. + transformer = self.model.model_copy(update={"submodel_type": SubModelType.Transformer}) + + # Determine VAE source. + if self.vae_model is not None: + vae_config = context.models.get_config(self.vae_model) + if vae_config.type is not ModelType.VAE or vae_config.base not in ( + BaseModelType.QwenImage, + BaseModelType.Anima, + ): + raise ValueError( + f"VAE '{vae_config.name}' is not compatible with Krea-2. Select a Qwen Image or Anima VAE." + ) + vae = self.vae_model.model_copy(update={"submodel_type": SubModelType.VAE}) + else: + self._validate_diffusers_format(context, self.model, "Krea-2") + vae = self.model.model_copy(update={"submodel_type": SubModelType.VAE}) + + # Determine Qwen3-VL Encoder source. + if self.qwen3_vl_encoder_model is not None: + encoder_config = context.models.get_config(self.qwen3_vl_encoder_model) + if encoder_config.type is not ModelType.Qwen3VLEncoder: + raise ValueError(f"Encoder '{encoder_config.name}' is not a Qwen3-VL encoder compatible with Krea-2.") + tokenizer = self.qwen3_vl_encoder_model.model_copy(update={"submodel_type": SubModelType.Tokenizer}) + text_encoder = self.qwen3_vl_encoder_model.model_copy(update={"submodel_type": SubModelType.TextEncoder}) + else: + self._validate_diffusers_format(context, self.model, "Krea-2") + tokenizer = self.model.model_copy(update={"submodel_type": SubModelType.Tokenizer}) + text_encoder = self.model.model_copy(update={"submodel_type": SubModelType.TextEncoder}) + + return Krea2ModelLoaderOutput( + transformer=TransformerField(transformer=transformer, loras=[]), + qwen3_vl_encoder=Qwen3VLEncoderField(tokenizer=tokenizer, text_encoder=text_encoder, loras=[]), + vae=VAEField(vae=vae), + ) + + def _validate_diffusers_format( + self, context: InvocationContext, model: ModelIdentifierField, model_name: str + ) -> None: + """Validate that a model is in Diffusers format (required to extract VAE / encoder submodels).""" + config = context.models.get_config(model) + if config.format != ModelFormat.Diffusers: + raise ValueError( + f"To extract the VAE and Qwen3-VL encoder, the {model_name} model must be in Diffusers format. " + f"The selected model '{config.name}' is in {config.format.value} format — provide a standalone " + "VAE and Qwen3-VL Encoder instead." + ) diff --git a/invokeai/app/invocations/krea2_seed_variance.py b/invokeai/app/invocations/krea2_seed_variance.py new file mode 100644 index 00000000000..a7ef38b1017 --- /dev/null +++ b/invokeai/app/invocations/krea2_seed_variance.py @@ -0,0 +1,70 @@ +import torch + +from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation +from invokeai.app.invocations.fields import FieldDescriptions, Input, InputField, Krea2ConditioningField +from invokeai.app.invocations.primitives import Krea2ConditioningOutput +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ( + ConditioningFieldData, + Krea2ConditioningInfo, +) + + +@invocation( + "krea2_seed_variance", + title="Seed Variance - Krea-2", + tags=["conditioning", "krea2", "krea-2", "variance"], + category="conditioning", + version="1.0.0", + classification=Classification.Prototype, +) +class Krea2SeedVarianceInvocation(BaseInvocation): + """Inject per-seed diversity into Krea-2 text conditioning. + + Distilled few-step models (like Krea-2-Turbo) suffer from low seed variance — different seeds give + near-identical images. This adds seeded uniform noise to a random subset of the text-embedding + values, trading some prompt adherence for variety (the same idea as the Z-Image-Turbo + `SeedVarianceEnhancer`). Optional pass between the text encoder and denoise; the defaults are + aggressive and may need tuning for Krea-2. + """ + + conditioning: Krea2ConditioningField = InputField( + description=FieldDescriptions.cond, input=Input.Connection, title="Conditioning" + ) + strength: float = InputField( + default=20.0, + allow_inf_nan=False, + description="Magnitude of the uniform noise added to the embeddings (noise in [-strength, +strength]).", + ) + randomize_percent: float = InputField( + default=50.0, + ge=1.0, + le=100.0, + description="Percentage of embedding values that get perturbed (Bernoulli mask).", + ) + variance_seed: int = InputField(default=0, description="Seed for the variance noise (vary this to get variety).") + + @torch.no_grad() + def invoke(self, context: InvocationContext) -> Krea2ConditioningOutput: + cond_data = context.conditioning.load(self.conditioning.conditioning_name) + assert len(cond_data.conditionings) == 1 + conditioning = cond_data.conditionings[0] + assert isinstance(conditioning, Krea2ConditioningInfo) + + embeds = conditioning.prompt_embeds # (B, seq, 12, hidden) + generator = torch.Generator(device=embeds.device).manual_seed(self.variance_seed) + noise = torch.rand(embeds.shape, generator=generator, dtype=torch.float32, device=embeds.device) * 2.0 - 1.0 + noise = noise * self.strength + mask = torch.bernoulli( + torch.full(embeds.shape, self.randomize_percent / 100.0, dtype=torch.float32, device=embeds.device), + generator=generator, + ) + embeds = (embeds.to(torch.float32) + noise * mask).to(embeds.dtype) + + new_data = ConditioningFieldData( + conditionings=[ + Krea2ConditioningInfo(prompt_embeds=embeds, prompt_embeds_mask=conditioning.prompt_embeds_mask) + ] + ) + conditioning_name = context.conditioning.save(new_data) + return Krea2ConditioningOutput.build(conditioning_name) diff --git a/invokeai/app/invocations/krea2_text_encoder.py b/invokeai/app/invocations/krea2_text_encoder.py new file mode 100644 index 00000000000..2e0f649c1d1 --- /dev/null +++ b/invokeai/app/invocations/krea2_text_encoder.py @@ -0,0 +1,163 @@ +from contextlib import ExitStack +from typing import Iterator, Tuple + +import torch + +from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation +from invokeai.app.invocations.fields import ( + FieldDescriptions, + Input, + InputField, + UIComponent, +) +from invokeai.app.invocations.model import Qwen3VLEncoderField +from invokeai.app.invocations.primitives import Krea2ConditioningOutput +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.krea2.sampling_utils import ( + KREA2_MAX_SEQ_LEN, + KREA2_NUM_SUFFIX_TOKENS, + KREA2_SELECT_LAYERS, + KREA2_START_IDX, +) +from invokeai.backend.model_manager.load.model_cache.utils import get_effective_device +from invokeai.backend.patches.layer_patcher import LayerPatcher +from invokeai.backend.patches.lora_conversions.krea2_lora_constants import KREA2_LORA_QWEN3VL_PREFIX +from invokeai.backend.patches.model_patch_raw import ModelPatchRaw +from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ( + ConditioningFieldData, + Krea2ConditioningInfo, +) +from invokeai.backend.util.devices import TorchDevice + +# Prompt template from diffusers Krea2Pipeline.get_text_hidden_states. The prefix (a system turn that +# instructs the model to describe the image) is the same "generate" template used by Qwen-Image, which +# is why the first KREA2_START_IDX (34) tokens are dropped from the encoder output. +_KREA2_PREFIX = ( + "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, " + "spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n" +) +_KREA2_SUFFIX = "<|im_end|>\n<|im_start|>assistant\n" + + +@invocation( + "krea2_text_encoder", + title="Prompt - Krea-2", + tags=["prompt", "conditioning", "krea2", "krea-2"], + category="conditioning", + version="1.0.0", + classification=Classification.Prototype, +) +class Krea2TextEncoderInvocation(BaseInvocation): + """Encodes a text prompt for Krea-2 using the Qwen3-VL text encoder. + + The encoder taps 12 decoder hidden-state layers and stacks them per token, producing a 4D + conditioning tensor (B, seq, 12, hidden) that the Krea-2 transformer's text-fusion stage consumes. + """ + + prompt: str = InputField(description="Text prompt describing the desired image.", ui_component=UIComponent.Textarea) + qwen3_vl_encoder: Qwen3VLEncoderField = InputField( + title="Qwen3-VL Encoder", + description=FieldDescriptions.qwen3_vl_encoder, + input=Input.Connection, + ) + + @torch.no_grad() + def invoke(self, context: InvocationContext) -> Krea2ConditioningOutput: + prompt_embeds, prompt_mask = self._encode(context) + prompt_embeds = prompt_embeds.detach().to("cpu") + prompt_mask = prompt_mask.detach().to("cpu") if prompt_mask is not None else None + + conditioning_data = ConditioningFieldData( + conditionings=[Krea2ConditioningInfo(prompt_embeds=prompt_embeds, prompt_embeds_mask=prompt_mask)] + ) + conditioning_name = context.conditioning.save(conditioning_data) + return Krea2ConditioningOutput.build(conditioning_name) + + def _encode(self, context: InvocationContext) -> tuple[torch.Tensor, torch.Tensor | None]: + tokenizer_info = context.models.load(self.qwen3_vl_encoder.tokenizer) + text_encoder_info = context.models.load(self.qwen3_vl_encoder.text_encoder) + + # diffusers tokenizes (prefix + prompt) and the assistant-turn suffix separately, then + # concatenates - so the suffix always survives truncation. Building one string and truncating it + # (right-truncation) drops the suffix for long (>~500-token) prompts, corrupting the trained token + # layout that the fixed prefix-drop (KREA2_START_IDX) and suffix accounting depend on. + body_text = _KREA2_PREFIX + self.prompt + # Reserve room for the suffix (diffusers: max_sequence_length + start_idx - num_suffix_tokens). + body_max_length = KREA2_MAX_SEQ_LEN + KREA2_START_IDX - KREA2_NUM_SUFFIX_TOKENS + + context.util.signal_progress("Running Qwen3-VL text encoder") + + with ExitStack() as exit_stack: + tokenizer = exit_stack.enter_context(tokenizer_info) + (cached_weights, text_encoder) = exit_stack.enter_context(text_encoder_info.model_on_device()) + device = get_effective_device(text_encoder) + + # Apply any Qwen3-VL text-encoder LoRA patches (smart/sidecar patching, fp8-aware). Without + # this, the encoder portion of a Krea-2 LoRA would be silently ignored. + exit_stack.enter_context( + LayerPatcher.apply_smart_model_patches( + model=text_encoder, + patches=self._lora_iterator(context), + prefix=KREA2_LORA_QWEN3VL_PREFIX, + dtype=TorchDevice.choose_bfloat16_safe_dtype(device), + cached_weights=cached_weights, + ) + ) + + body_inputs = tokenizer( + body_text, + max_length=body_max_length, + truncation=True, + return_tensors="pt", + ) + # Append the suffix AFTER truncation so it can never be cut. add_special_tokens=False keeps it + # to exactly the assistant-turn tokens (no extra BOS), matching the reference token layout. + suffix_inputs = tokenizer(_KREA2_SUFFIX, add_special_tokens=False, return_tensors="pt") + input_ids = torch.cat([body_inputs.input_ids, suffix_inputs.input_ids], dim=1).to(device=device) + attention_mask = torch.cat([body_inputs.attention_mask, suffix_inputs.attention_mask], dim=1).to( + device=device + ) + + outputs = text_encoder( + input_ids=input_ids, + attention_mask=attention_mask, + output_hidden_states=True, + use_cache=False, + return_dict=True, + ) + + # Some VL models nest the language-model output; fall back to that if needed. + hidden_states_tuple = getattr(outputs, "hidden_states", None) + if hidden_states_tuple is None: + lm_output = getattr(outputs, "language_model_outputs", None) + hidden_states_tuple = getattr(lm_output, "hidden_states", None) + if hidden_states_tuple is None: + raise RuntimeError("Qwen3-VL encoder did not return hidden_states; cannot build Krea-2 conditioning.") + + # Stack the selected layers along a new layer axis: (B, seq, 12, hidden). + stacked = torch.stack([hidden_states_tuple[i] for i in KREA2_SELECT_LAYERS], dim=2) + + # Drop the system-prompt prefix tokens. + prompt_embeds = stacked[:, KREA2_START_IDX:] + prompt_mask = attention_mask[:, KREA2_START_IDX:].bool() + + # Match the device-safe compute dtype used by the denoise loop (falls back from bf16 to + # fp16/fp32 on devices without bf16 support) rather than forcing bfloat16. + prompt_embeds = prompt_embeds.to(dtype=TorchDevice.choose_bfloat16_safe_dtype(device)) + + # If every token is valid (no padding), the mask is unnecessary. + if prompt_mask is not None and bool(prompt_mask.all()): + prompt_mask = None + + return prompt_embeds, prompt_mask + + def _lora_iterator(self, context: InvocationContext) -> Iterator[Tuple[ModelPatchRaw, float]]: + """Iterate over the LoRA models to apply to the Qwen3-VL text encoder.""" + for lora in self.qwen3_vl_encoder.loras: + lora_info = context.models.load(lora.lora) + if not isinstance(lora_info.model, ModelPatchRaw): + raise TypeError( + f"Expected ModelPatchRaw for LoRA '{lora.lora.key}', got {type(lora_info.model).__name__}." + ) + yield (lora_info.model, lora.weight) + del lora_info diff --git a/invokeai/app/invocations/metadata.py b/invokeai/app/invocations/metadata.py index da24d8802bb..26adaa04527 100644 --- a/invokeai/app/invocations/metadata.py +++ b/invokeai/app/invocations/metadata.py @@ -174,6 +174,10 @@ def invoke(self, context: InvocationContext) -> MetadataOutput: "anima_img2img", "anima_inpaint", "anima_outpaint", + "krea2_txt2img", + "krea2_img2img", + "krea2_inpaint", + "krea2_outpaint", ] diff --git a/invokeai/app/invocations/model.py b/invokeai/app/invocations/model.py index c82b4369bd8..d4fa50a2ea4 100644 --- a/invokeai/app/invocations/model.py +++ b/invokeai/app/invocations/model.py @@ -87,6 +87,14 @@ class Qwen3EncoderField(BaseModel): loras: List[LoRAField] = Field(default_factory=list, description="LoRAs to apply on model loading") +class Qwen3VLEncoderField(BaseModel): + """Field for the Qwen3-VL text encoder used by Krea-2 models.""" + + tokenizer: ModelIdentifierField = Field(description="Info to load tokenizer submodel") + text_encoder: ModelIdentifierField = Field(description="Info to load text_encoder submodel") + loras: List[LoRAField] = Field(default_factory=list, description="LoRAs to apply on model loading") + + class VAEField(BaseModel): vae: ModelIdentifierField = Field(description="Info to load vae submodel") seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless') diff --git a/invokeai/app/invocations/primitives.py b/invokeai/app/invocations/primitives.py index 6249de0cd8e..a825096449c 100644 --- a/invokeai/app/invocations/primitives.py +++ b/invokeai/app/invocations/primitives.py @@ -23,6 +23,7 @@ ImageField, Input, InputField, + Krea2ConditioningField, LatentsField, OutputField, QwenImageConditioningField, @@ -499,6 +500,17 @@ def build(cls, conditioning_name: str) -> "QwenImageConditioningOutput": return cls(conditioning=QwenImageConditioningField(conditioning_name=conditioning_name)) +@invocation_output("krea2_conditioning_output") +class Krea2ConditioningOutput(BaseInvocationOutput): + """Base class for nodes that output a Krea-2 conditioning tensor.""" + + conditioning: Krea2ConditioningField = OutputField(description=FieldDescriptions.cond) + + @classmethod + def build(cls, conditioning_name: str) -> "Krea2ConditioningOutput": + return cls(conditioning=Krea2ConditioningField(conditioning_name=conditioning_name)) + + @invocation_output("anima_conditioning_output") class AnimaConditioningOutput(BaseInvocationOutput): """Base class for nodes that output an Anima text conditioning tensor.""" diff --git a/invokeai/app/invocations/qwen_image_image_to_latents.py b/invokeai/app/invocations/qwen_image_image_to_latents.py index ffae5470f68..f56b0695dc3 100644 --- a/invokeai/app/invocations/qwen_image_image_to_latents.py +++ b/invokeai/app/invocations/qwen_image_image_to_latents.py @@ -1,6 +1,5 @@ import einops import torch -from diffusers.models.autoencoders.autoencoder_kl_qwenimage import AutoencoderKLQwenImage from PIL import Image as PILImage from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation @@ -15,6 +14,7 @@ from invokeai.app.invocations.model import VAEField from invokeai.app.invocations.primitives import LatentsOutput from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.krea2.vae_compat import as_qwen_image_vae from invokeai.backend.model_manager.load.load_base import LoadedModel from invokeai.backend.stable_diffusion.diffusers_pipeline import image_resized_to_grid_as_tensor from invokeai.backend.util.devices import TorchDevice @@ -45,14 +45,18 @@ class QwenImageImageToLatentsInvocation(BaseInvocation, WithMetadata, WithBoard) @staticmethod def vae_encode(vae_info: LoadedModel, image_tensor: torch.Tensor) -> torch.Tensor: - assert isinstance(vae_info.model, AutoencoderKLQwenImage) + # NOTE: vae_info.model may be an AutoencoderKLWan (a native-layout qwen_image_vae single file is + # classified with the Anima base); it is reinterpreted as AutoencoderKLQwenImage inside the + # model_on_device context below. The working-memory estimate only reads tensor shape + element + # size, so it is safe to run on either class here. estimated_working_memory = estimate_vae_working_memory_qwen_image( operation="encode", image_tensor=image_tensor, vae=vae_info.model, ) with vae_info.model_on_device(working_mem_bytes=estimated_working_memory) as (_, vae): - assert isinstance(vae, AutoencoderKLQwenImage) + # Reinterpret an Anima-classified Wan VAE as AutoencoderKLQwenImage (identical weights). + vae = as_qwen_image_vae(vae) vae.disable_tiling() diff --git a/invokeai/app/invocations/qwen_image_latents_to_image.py b/invokeai/app/invocations/qwen_image_latents_to_image.py index 072185f147b..ffa913536d3 100644 --- a/invokeai/app/invocations/qwen_image_latents_to_image.py +++ b/invokeai/app/invocations/qwen_image_latents_to_image.py @@ -1,7 +1,6 @@ from contextlib import nullcontext import torch -from diffusers.models.autoencoders.autoencoder_kl_qwenimage import AutoencoderKLQwenImage from einops import rearrange from PIL import Image @@ -17,6 +16,7 @@ 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.krea2.vae_compat import as_qwen_image_vae from invokeai.backend.stable_diffusion.extensions.seamless import SeamlessExt from invokeai.backend.util.devices import TorchDevice from invokeai.backend.util.vae_working_memory import estimate_vae_working_memory_qwen_image @@ -41,48 +41,51 @@ def invoke(self, context: InvocationContext) -> ImageOutput: latents = context.tensors.load(self.latents.latents_name) vae_info = context.models.load(self.vae.vae) - assert isinstance(vae_info.model, AutoencoderKLQwenImage) + # NOTE: vae_info.model may be an AutoencoderKLWan (a native-layout qwen_image_vae single file is + # classified with the Anima base); it is reinterpreted as AutoencoderKLQwenImage inside the + # model_on_device context below. The working-memory estimate only reads tensor shape + element + # size, so it is safe to run on either class here. estimated_working_memory = estimate_vae_working_memory_qwen_image( operation="decode", image_tensor=latents, vae=vae_info.model, ) - with ( - SeamlessExt.static_patch_model(vae_info.model, self.vae.seamless_axes), - vae_info.model_on_device(working_mem_bytes=estimated_working_memory) as (_, vae), - ): + with vae_info.model_on_device(working_mem_bytes=estimated_working_memory) as (_, vae): context.util.signal_progress("Running VAE") - assert isinstance(vae, AutoencoderKLQwenImage) - latents = latents.to(device=TorchDevice.choose_torch_device(), dtype=vae.dtype) - - vae.disable_tiling() - - tiling_context = nullcontext() - - TorchDevice.empty_cache() - - with torch.inference_mode(), tiling_context: - # The Qwen Image VAE uses per-channel latents_mean / latents_std - # instead of a single scaling_factor. - # Latents are 5D: (B, C, num_frames, H, W) — the unpack from the - # denoise step already produces this shape. - latents_mean = ( - torch.tensor(vae.config.latents_mean) - .view(1, vae.config.z_dim, 1, 1, 1) - .to(latents.device, latents.dtype) - ) - latents_std = 1.0 / torch.tensor(vae.config.latents_std).view(1, vae.config.z_dim, 1, 1, 1).to( - latents.device, latents.dtype - ) - latents = latents / latents_std + latents_mean - - img = vae.decode(latents, return_dict=False)[0] - # Drop the temporal frame dimension: (B, C, 1, H, W) -> (B, C, H, W) - img = img[:, :, 0] - - img = img.clamp(-1, 1) - img = rearrange(img[0], "c h w -> h w c") - img_pil = Image.fromarray((127.5 * (img + 1.0)).byte().cpu().numpy()) + # A native-layout qwen_image_vae single file is classified with the Anima base and loaded + # as AutoencoderKLWan; reinterpret it as AutoencoderKLQwenImage (identical weights). + vae = as_qwen_image_vae(vae) + with SeamlessExt.static_patch_model(vae, self.vae.seamless_axes): + latents = latents.to(device=TorchDevice.choose_torch_device(), dtype=vae.dtype) + + vae.disable_tiling() + + tiling_context = nullcontext() + + TorchDevice.empty_cache() + + with torch.inference_mode(), tiling_context: + # The Qwen Image VAE uses per-channel latents_mean / latents_std + # instead of a single scaling_factor. + # Latents are 5D: (B, C, num_frames, H, W) — the unpack from the + # denoise step already produces this shape. + latents_mean = ( + torch.tensor(vae.config.latents_mean) + .view(1, vae.config.z_dim, 1, 1, 1) + .to(latents.device, latents.dtype) + ) + latents_std = 1.0 / torch.tensor(vae.config.latents_std).view(1, vae.config.z_dim, 1, 1, 1).to( + latents.device, latents.dtype + ) + latents = latents / latents_std + latents_mean + + img = vae.decode(latents, return_dict=False)[0] + # Drop the temporal frame dimension: (B, C, 1, H, W) -> (B, C, H, W) + img = img[:, :, 0] + + img = img.clamp(-1, 1) + img = rearrange(img[0], "c h w -> h w c") + img_pil = Image.fromarray((127.5 * (img + 1.0)).byte().cpu().numpy()) TorchDevice.empty_cache() diff --git a/invokeai/app/services/model_records/model_records_base.py b/invokeai/app/services/model_records/model_records_base.py index e06f8f2df91..86b81b0cf13 100644 --- a/invokeai/app/services/model_records/model_records_base.py +++ b/invokeai/app/services/model_records/model_records_base.py @@ -26,6 +26,7 @@ ClipVariantType, Flux2VariantType, FluxVariantType, + Krea2VariantType, ModelFormat, ModelSourceType, ModelType, @@ -135,6 +136,7 @@ def validate_source_url(cls, v: Any) -> Optional[str]: | ZImageVariantType | QwenImageVariantType | Qwen3VariantType + | Krea2VariantType ] = Field(description="The variant of the model.", default=None) prediction_type: Optional[SchedulerPredictionType] = Field( description="The prediction type of the model.", default=None diff --git a/invokeai/app/util/step_callback.py b/invokeai/app/util/step_callback.py index 08dc9a2265c..e8101e192e6 100644 --- a/invokeai/app/util/step_callback.py +++ b/invokeai/app/util/step_callback.py @@ -255,7 +255,8 @@ def diffusion_step_callback( latent_rgb_factors = SD3_5_LATENT_RGB_FACTORS elif base_model == BaseModelType.CogView4: latent_rgb_factors = COGVIEW4_LATENT_RGB_FACTORS - elif base_model == BaseModelType.QwenImage: + elif base_model in [BaseModelType.QwenImage, BaseModelType.Krea2]: + # Krea-2 decodes with the Qwen-Image VAE (16 latent channels), so it shares the preview factors. latent_rgb_factors = QWEN_IMAGE_LATENT_RGB_FACTORS latent_rgb_bias = QWEN_IMAGE_LATENT_RGB_BIAS elif base_model == BaseModelType.Flux: diff --git a/invokeai/backend/krea2/__init__.py b/invokeai/backend/krea2/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/invokeai/backend/krea2/sampling_utils.py b/invokeai/backend/krea2/sampling_utils.py new file mode 100644 index 00000000000..875c7cefd46 --- /dev/null +++ b/invokeai/backend/krea2/sampling_utils.py @@ -0,0 +1,97 @@ +"""Sampling/packing utilities for Krea-2 (Krea2Pipeline) inference. + +InvokeAI hand-writes its own denoise loop for Qwen-family models rather than calling the +diffusers pipeline ``__call__``. These helpers replicate the Krea-2 sampling math so the +``Krea2Transformer2DModel`` (loaded from diffusers) can be driven directly. + +Reference: ``diffusers/pipelines/krea2/pipeline_krea2.py`` (diffusers main / 0.39.0.dev0). +""" + +from typing import List + +import numpy as np +import torch + +# Krea-2 packs latents into 2x2 patches; the VAE (AutoencoderKLQwenImage) is f8. +PATCH_SIZE = 2 +VAE_SCALE_FACTOR = 8 + +# Hidden-state layers tapped from the Qwen3-VL text encoder (model_index.json +# text_encoder_select_layers). Stacked per token into prompt_embeds (B, seq, 12, hidden). +KREA2_SELECT_LAYERS = (2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35) + +# Text template constants (diffusers Krea2Pipeline.get_text_hidden_states). +KREA2_MAX_SEQ_LEN = 512 +KREA2_START_IDX = 34 # drop the system-prompt prefix tokens +KREA2_NUM_SUFFIX_TOKENS = 5 + +# Resolution-aware time-shift parameters (scheduler_config.json). +KREA2_BASE_SHIFT = 0.5 +KREA2_MAX_SHIFT = 1.15 +KREA2_BASE_IMAGE_SEQ_LEN = 256 +KREA2_MAX_IMAGE_SEQ_LEN = 6400 +# Fixed timestep shift for the distilled (Turbo) checkpoint. +KREA2_DISTILLED_MU = 1.15 + + +def pack_latents(latents: torch.Tensor, batch_size: int, num_channels: int, height: int, width: int) -> torch.Tensor: + """Pack 4D latents (B, C, H, W) into 2x2-patched 3D (B, H/2*W/2, C*4). + + Identical to the Qwen-Image / Krea-2 ``_pack_latents`` (patch_size=2). + """ + p = PATCH_SIZE + latents = latents.view(batch_size, num_channels, height // p, p, width // p, p) + latents = latents.permute(0, 2, 4, 1, 3, 5) + latents = latents.reshape(batch_size, (height // p) * (width // p), num_channels * p * p) + return latents + + +def unpack_latents(latents: torch.Tensor, height: int, width: int) -> torch.Tensor: + """Unpack 3D patched latents (B, seq, C*4) back to 4D (B, C, H, W). + + ``height``/``width`` are in latent space (i.e. pixels // vae_scale_factor). + """ + p = PATCH_SIZE + batch_size, _num_patches, channels = latents.shape + h = p * (height // p) + w = p * (width // p) + latents = latents.view(batch_size, h // p, w // p, channels // (p * p), p, p) + latents = latents.permute(0, 3, 1, 4, 2, 5) + latents = latents.reshape(batch_size, channels // (p * p), h, w) + return latents + + +def prepare_position_ids(text_seq_len: int, grid_height: int, grid_width: int, device: torch.device) -> torch.Tensor: + """Build the (text_seq_len + grid_h*grid_w, 3) rotary coordinates. + + Text tokens sit at the origin (0, 0, 0); image tokens carry their (0, h, w) latent-grid + coordinates. Matches diffusers ``Krea2Pipeline.prepare_position_ids``. + """ + text_ids = torch.zeros(text_seq_len, 3, device=device) + image_ids = torch.zeros(grid_height, grid_width, 3, device=device) + image_ids[..., 1] = torch.arange(grid_height, device=device)[:, None] + image_ids[..., 2] = torch.arange(grid_width, device=device)[None, :] + image_ids = image_ids.reshape(grid_height * grid_width, 3) + return torch.cat([text_ids, image_ids], dim=0) + + +def calculate_shift( + image_seq_len: int, + base_image_seq_len: int = KREA2_BASE_IMAGE_SEQ_LEN, + max_image_seq_len: int = KREA2_MAX_IMAGE_SEQ_LEN, + base_shift: float = KREA2_BASE_SHIFT, + max_shift: float = KREA2_MAX_SHIFT, +) -> float: + """Resolution-aware mu (linear interpolation by sequence length). + + NOTE: mu is fed straight into ``FlowMatchEulerDiscreteScheduler.set_timesteps(..., mu=mu)``; + the exponential time-shift happens inside the scheduler. Do NOT ``exp()`` this value. + """ + m = (max_shift - base_shift) / (max_image_seq_len - base_image_seq_len) + b = base_shift - m * base_image_seq_len + return image_seq_len * m + b + + +def build_sigmas(steps: int) -> List[float]: + """Krea-2 sigma schedule: linspace(1.0, 1/steps, steps).""" + return np.linspace(1.0, 1.0 / steps, steps).tolist() diff --git a/invokeai/backend/krea2/vae_compat.py b/invokeai/backend/krea2/vae_compat.py new file mode 100644 index 00000000000..913236c39be --- /dev/null +++ b/invokeai/backend/krea2/vae_compat.py @@ -0,0 +1,40 @@ +"""Compatibility helpers for the Qwen-Image VAE used by Krea-2. + +Krea-2 (and Qwen-Image) decode/encode with ``AutoencoderKLQwenImage``. A standalone single-file +``qwen_image_vae.safetensors`` in the native (ComfyUI/Wan) layout is byte-identical to the Anima VAE +and therefore classified with the Anima base, which loads it as ``AutoencoderKLWan``. The two classes +share the exact same diffusers state-dict (identical keys and shapes), so a Wan-loaded VAE can be +reinterpreted as ``AutoencoderKLQwenImage`` losslessly — and the default ``AutoencoderKLQwenImage`` +config carries the correct Qwen-Image ``latents_mean`` / ``latents_std`` / ``z_dim`` that the qwen +encode/decode nodes read. +""" + +from typing import Any + +import accelerate +from diffusers.models.autoencoders import AutoencoderKLWan +from diffusers.models.autoencoders.autoencoder_kl_qwenimage import AutoencoderKLQwenImage + + +def as_qwen_image_vae(model: Any) -> AutoencoderKLQwenImage: + """Return ``model`` if it is already an ``AutoencoderKLQwenImage``, else reinterpret it as one. + + The only expected non-matching input is ``AutoencoderKLWan`` (the same weights loaded via the + Anima single-file path). Its state dict is loaded — with ``assign=True`` so no tensors are copied + and device/dtype are preserved — into a freshly built ``AutoencoderKLQwenImage`` whose default + config provides the correct Qwen-Image latent statistics. + """ + if isinstance(model, AutoencoderKLQwenImage): + return model + if not isinstance(model, AutoencoderKLWan): + raise TypeError(f"Expected AutoencoderKLQwenImage or AutoencoderKLWan, got {type(model).__name__}.") + + src_state_dict = model.state_dict() + with accelerate.init_empty_weights(): + qwen_vae = AutoencoderKLQwenImage() + # assign=True shares the source tensors (no copy) and keeps their device/dtype. + qwen_vae.load_state_dict(src_state_dict, strict=True, assign=True) + # Match the eval/grad state of a normally-loaded VAE. + qwen_vae.eval() + qwen_vae.requires_grad_(False) + return qwen_vae diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py index 31f3b14619d..a60fec1b51a 100644 --- a/invokeai/backend/model_manager/configs/factory.py +++ b/invokeai/backend/model_manager/configs/factory.py @@ -1,3 +1,4 @@ +import json import logging from dataclasses import dataclass from pathlib import Path @@ -51,6 +52,7 @@ LoRA_LyCORIS_Anima_Config, LoRA_LyCORIS_Flux2_Config, LoRA_LyCORIS_FLUX_Config, + LoRA_LyCORIS_Krea2_Config, LoRA_LyCORIS_QwenImage_Config, LoRA_LyCORIS_SD1_Config, LoRA_LyCORIS_SD2_Config, @@ -65,6 +67,7 @@ Main_Checkpoint_Anima_Config, Main_Checkpoint_Flux2_Config, Main_Checkpoint_FLUX_Config, + Main_Checkpoint_Krea2_Config, Main_Checkpoint_QwenImage_Config, Main_Checkpoint_SD1_Config, Main_Checkpoint_SD2_Config, @@ -74,6 +77,7 @@ Main_Diffusers_CogView4_Config, Main_Diffusers_Flux2_Config, Main_Diffusers_FLUX_Config, + Main_Diffusers_Krea2_Config, Main_Diffusers_QwenImage_Config, Main_Diffusers_SD1_Config, Main_Diffusers_SD2_Config, @@ -83,6 +87,7 @@ Main_Diffusers_ZImage_Config, Main_GGUF_Flux2_Config, Main_GGUF_FLUX_Config, + Main_GGUF_Krea2_Config, Main_GGUF_QwenImage_Config, Main_GGUF_ZImage_Config, MainModelDefaultSettings, @@ -92,6 +97,10 @@ Qwen3Encoder_GGUF_Config, Qwen3Encoder_Qwen3Encoder_Config, ) +from invokeai.backend.model_manager.configs.qwen3_vl_encoder import ( + Qwen3VLEncoder_Checkpoint_Config, + Qwen3VLEncoder_Qwen3VLEncoder_Config, +) from invokeai.backend.model_manager.configs.qwen_vl_encoder import ( QwenVLEncoder_Checkpoint_Config, QwenVLEncoder_Diffusers_Config, @@ -160,6 +169,46 @@ # Maximum depth to search for model files in directories _MAX_SEARCH_DEPTH = 2 +# Classes introduced by the versions pinned by this checkout may not exist in the interpreter used by +# lightweight config tests. Keep explicit markers only for those newly supported classes; established +# classes are resolved from the installed Diffusers/Transformers exports below. +_PINNED_MODEL_CLASS_MARKERS = {"Krea2Pipeline"} + + +def _is_known_model_marker(config_name: str, config: Any) -> bool: + """Return whether a root config names a class/model type provided by our model libraries.""" + import diffusers + import transformers + from transformers.models.auto.configuration_auto import CONFIG_MAPPING_NAMES + + if not isinstance(config, dict): + return False + + def has_export(module: Any, name: Any) -> bool: + if not isinstance(name, str) or not name: + return False + try: + return getattr(module, name, None) is not None + except Exception: + return False + + if config_name == "model_index.json": + class_name = config.get("_class_name") + return class_name in _PINNED_MODEL_CLASS_MARKERS or has_export(diffusers, class_name) + + class_name = config.get("_class_name") + if ( + class_name in _PINNED_MODEL_CLASS_MARKERS + or has_export(diffusers, class_name) + or has_export(transformers, class_name) + ): + return True + model_type = config.get("model_type") + if isinstance(model_type, str) and model_type in CONFIG_MAPPING_NAMES: + return True + architectures = config.get("architectures") + return isinstance(architectures, list) and any(has_export(transformers, name) for name in architectures) + # The types are listed explicitly because IDEs/LSPs can't identify the correct types # when AnyModelConfig is constructed dynamically using ModelConfigBase.all_config_classes @@ -176,6 +225,7 @@ Annotated[Main_Diffusers_CogView4_Config, Main_Diffusers_CogView4_Config.get_tag()], Annotated[Main_Diffusers_QwenImage_Config, Main_Diffusers_QwenImage_Config.get_tag()], Annotated[Main_Diffusers_ZImage_Config, Main_Diffusers_ZImage_Config.get_tag()], + Annotated[Main_Diffusers_Krea2_Config, Main_Diffusers_Krea2_Config.get_tag()], # Main (Pipeline) - checkpoint format # IMPORTANT: FLUX.2 must be checked BEFORE FLUX.1 because FLUX.2 has specific validation # that will reject FLUX.1 models, but FLUX.1 validation may incorrectly match FLUX.2 models @@ -187,6 +237,7 @@ Annotated[Main_Checkpoint_FLUX_Config, Main_Checkpoint_FLUX_Config.get_tag()], Annotated[Main_Checkpoint_QwenImage_Config, Main_Checkpoint_QwenImage_Config.get_tag()], Annotated[Main_Checkpoint_ZImage_Config, Main_Checkpoint_ZImage_Config.get_tag()], + Annotated[Main_Checkpoint_Krea2_Config, Main_Checkpoint_Krea2_Config.get_tag()], Annotated[Main_Checkpoint_Anima_Config, Main_Checkpoint_Anima_Config.get_tag()], # Main (Pipeline) - quantized formats # IMPORTANT: FLUX.2 must be checked BEFORE FLUX.1 because FLUX.2 has specific validation @@ -196,6 +247,7 @@ Annotated[Main_GGUF_FLUX_Config, Main_GGUF_FLUX_Config.get_tag()], Annotated[Main_GGUF_QwenImage_Config, Main_GGUF_QwenImage_Config.get_tag()], Annotated[Main_GGUF_ZImage_Config, Main_GGUF_ZImage_Config.get_tag()], + Annotated[Main_GGUF_Krea2_Config, Main_GGUF_Krea2_Config.get_tag()], # VAE - checkpoint format Annotated[VAE_Checkpoint_SD1_Config, VAE_Checkpoint_SD1_Config.get_tag()], Annotated[VAE_Checkpoint_SD2_Config, VAE_Checkpoint_SD2_Config.get_tag()], @@ -229,6 +281,7 @@ Annotated[LoRA_LyCORIS_Flux2_Config, LoRA_LyCORIS_Flux2_Config.get_tag()], Annotated[LoRA_LyCORIS_FLUX_Config, LoRA_LyCORIS_FLUX_Config.get_tag()], Annotated[LoRA_LyCORIS_ZImage_Config, LoRA_LyCORIS_ZImage_Config.get_tag()], + Annotated[LoRA_LyCORIS_Krea2_Config, LoRA_LyCORIS_Krea2_Config.get_tag()], Annotated[LoRA_LyCORIS_QwenImage_Config, LoRA_LyCORIS_QwenImage_Config.get_tag()], Annotated[LoRA_LyCORIS_Anima_Config, LoRA_LyCORIS_Anima_Config.get_tag()], # LoRA - OMI format @@ -248,6 +301,11 @@ # T5 Encoder - all formats Annotated[T5Encoder_T5Encoder_Config, T5Encoder_T5Encoder_Config.get_tag()], Annotated[T5Encoder_BnBLLMint8_Config, T5Encoder_BnBLLMint8_Config.get_tag()], + # Qwen3-VL Encoder (Qwen3-VL multimodal encoder for Krea-2) - checked BEFORE the text-only Qwen3 + # encoder so single-file VL checkpoints (which also carry generic model.layers.* keys) are not + # misclassified as the Z-Image Qwen3 encoder. The VL probe requires the visual tower. + Annotated[Qwen3VLEncoder_Checkpoint_Config, Qwen3VLEncoder_Checkpoint_Config.get_tag()], + Annotated[Qwen3VLEncoder_Qwen3VLEncoder_Config, Qwen3VLEncoder_Qwen3VLEncoder_Config.get_tag()], # Qwen3 Encoder Annotated[Qwen3Encoder_Qwen3Encoder_Config, Qwen3Encoder_Qwen3Encoder_Config.get_tag()], Annotated[Qwen3Encoder_Checkpoint_Config, Qwen3Encoder_Checkpoint_Config.get_tag()], @@ -404,6 +462,26 @@ def _validate_path_looks_like_model(path: Path) -> None: f"Expected one of: {', '.join(sorted(_MODEL_EXTENSIONS))}" ) else: + # Recognized Diffusers/Transformers configs are safe model markers. A generic config.json + # is not sufficient because many large application directories contain one. + recognized_root_config = False + for config_name in _CONFIG_FILES: + config_path = path / config_name + if not config_path.exists(): + continue + try: + # Model config.json files are UTF-8; read explicitly so a non-ASCII value does not + # raise UnicodeDecodeError under a cp1252 (Windows) locale and get mis-treated as + # "unrecognized", which would wrongly reject a valid model directory. + config = json.loads(config_path.read_text(encoding="utf-8")) + except (OSError, ValueError): + continue + recognized_root_config = _is_known_model_marker(config_name, config) + if recognized_root_config: + break + if recognized_root_config: + return + # For directories, do a quick file count check with early exit total_files = 0 # Ignore hidden files and directories @@ -422,13 +500,6 @@ def _validate_path_looks_like_model(path: Path) -> None: "Please provide a path to a specific model file or model directory." ) - # Check if it has config files at root (diffusers/transformers marker) - has_root_config = any((path / config).exists() for config in _CONFIG_FILES) - - if has_root_config: - # Has a config file, looks like a valid model directory - return - # Otherwise, search for model files within depth limit def find_model_files(current_path: Path, depth: int) -> bool: if depth > _MAX_SEARCH_DEPTH: diff --git a/invokeai/backend/model_manager/configs/lora.py b/invokeai/backend/model_manager/configs/lora.py index fdaebe38565..2e5ba1a2606 100644 --- a/invokeai/backend/model_manager/configs/lora.py +++ b/invokeai/backend/model_manager/configs/lora.py @@ -853,6 +853,9 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: # (with the "transformer." prefix and "single_" variant) which would falsely match our check. # Flux Kohya LoRAs use lora_unet_double_blocks or lora_unet_single_blocks. has_z_image_keys = state_dict_has_any_keys_starting_with(state_dict, {"diffusion_model.layers."}) + # Krea-2 LoRAs also carry transformer.transformer_blocks. keys, but uniquely include the + # text-fusion stage. Exclude them here so they route to LoRA_LyCORIS_Krea2_Config. + has_krea2_keys = _has_krea2_lora_keys(state_dict) has_flux_keys = state_dict_has_any_keys_starting_with( state_dict, { @@ -866,7 +869,7 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: }, ) - if has_qwen_ie_keys and has_lora_suffix and not has_z_image_keys and not has_flux_keys: + if has_qwen_ie_keys and has_lora_suffix and not has_z_image_keys and not has_krea2_keys and not has_flux_keys: return raise NotAMatchError("model does not match Qwen Image LoRA heuristics") @@ -879,6 +882,7 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: {"transformer_blocks.", "transformer.transformer_blocks.", "lora_unet_transformer_blocks_"}, ) has_z_image_keys = state_dict_has_any_keys_starting_with(state_dict, {"diffusion_model.layers."}) + has_krea2_keys = _has_krea2_lora_keys(state_dict) has_flux_keys = state_dict_has_any_keys_starting_with( state_dict, { @@ -892,11 +896,83 @@ def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: }, ) - if has_qwen_ie_keys and not has_z_image_keys and not has_flux_keys: + if has_qwen_ie_keys and not has_z_image_keys and not has_krea2_keys and not has_flux_keys: return BaseModelType.QwenImage raise NotAMatchError("model does not look like a Qwen Image Edit LoRA") +def _has_krea2_lora_keys(state_dict: dict[str | int, Any]) -> bool: + """True if the state dict targets Krea-2's distinctive text-fusion / time-mod-proj modules.""" + return any(isinstance(k, str) and ("text_fusion" in k or "time_mod_proj" in k) for k in state_dict.keys()) + + +def _has_complete_lora_pair_for_prefixes(state_dict: dict[str | int, Any], prefixes: tuple[str, ...]) -> bool: + string_keys = {key for key in state_dict if isinstance(key, str)} + pairs = (("lora_A.weight", "lora_B.weight"), ("lora_down.weight", "lora_up.weight")) + for key in string_keys: + if not key.startswith(prefixes): + continue + for down_suffix, up_suffix in pairs: + if key.endswith(down_suffix) and f"{key[: -len(down_suffix)]}{up_suffix}" in string_keys: + return True + return False + + +class LoRA_LyCORIS_Krea2_Config(LoRA_LyCORIS_Config_Base, Config_Base): + """Model config for Krea-2 LoRA models in LyCORIS (single-file diffusers PEFT) format.""" + + base: Literal[BaseModelType.Krea2] = Field(default=BaseModelType.Krea2) + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_file(mod) + raise_for_override_fields(cls, override_fields) + + state_dict = mod.load_state_dict() + explicit_krea2_override = override_fields.get("base") is BaseModelType.Krea2 + has_supported_explicit_pair = _has_complete_lora_pair_for_prefixes( + state_dict, + ( + "transformer.transformer_blocks.", + "transformer_blocks.", + "base_model.model.transformer.transformer_blocks.", + "text_encoder.", + "base_model.model.text_encoder.", + ), + ) + if explicit_krea2_override and has_supported_explicit_pair: + return cls(**override_fields) + + cls._validate_looks_like_lora(mod) + cls._validate_base(mod) + return cls(**override_fields) + + @classmethod + def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: + """Krea-2 LoRAs have keys like transformer.text_fusion.* / transformer.transformer_blocks.* with + a lora_A/lora_B (or lora_down/lora_up) suffix. The text-fusion stage is unique to Krea-2.""" + state_dict = mod.load_state_dict() + has_lora_suffix = state_dict_has_any_keys_ending_with( + state_dict, + { + "lora_A.weight", + "lora_B.weight", + "lora_down.weight", + "lora_up.weight", + "dora_scale", + }, + ) + if _has_krea2_lora_keys(state_dict) and has_lora_suffix: + return + raise NotAMatchError("model does not match Krea-2 LoRA heuristics") + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + if _has_krea2_lora_keys(mod.load_state_dict()): + return BaseModelType.Krea2 + raise NotAMatchError("model does not look like a Krea-2 LoRA") + + class LoRA_LyCORIS_Anima_Config(LoRA_LyCORIS_Config_Base, Config_Base): """Model config for Anima LoRA models in LyCORIS format.""" diff --git a/invokeai/backend/model_manager/configs/main.py b/invokeai/backend/model_manager/configs/main.py index 10835b389fc..56e1cd35fe7 100644 --- a/invokeai/backend/model_manager/configs/main.py +++ b/invokeai/backend/model_manager/configs/main.py @@ -27,6 +27,7 @@ BaseModelType, Flux2VariantType, FluxVariantType, + Krea2VariantType, ModelFormat, ModelType, ModelVariantType, @@ -65,7 +66,12 @@ class MainModelDefaultSettings(BaseModel): def from_base( cls, base: BaseModelType, - variant: Flux2VariantType | FluxVariantType | ModelVariantType | ZImageVariantType | None = None, + variant: Flux2VariantType + | FluxVariantType + | ModelVariantType + | ZImageVariantType + | Krea2VariantType + | None = None, ) -> Self | None: match base: case BaseModelType.StableDiffusion1: @@ -95,6 +101,14 @@ def from_base( return cls(steps=4, cfg_scale=1.0, width=1024, height=1024) case BaseModelType.QwenImage: return cls(steps=40, cfg_scale=4.0, width=1024, height=1024) + case BaseModelType.Krea2: + # Krea-2-Raw (Base, undistilled) needs more steps and CFG; Turbo (distilled) uses 8 + # steps with CFG disabled. cfg_scale has a floor of 1 (ge=1); 1.0 means "no guidance". + if variant == Krea2VariantType.Base: + # Diffusers' Krea-2 guidance 4.5 uses cond + 4.5 * (cond - uncond), which is + # equivalent to InvokeAI's standard CFG convention at scale 5.5. + return cls(steps=28, cfg_scale=5.5, width=1024, height=1024) + return cls(steps=8, cfg_scale=1.0, width=1024, height=1024) case _: # TODO(psyche): Do we want defaults for other base types? return None @@ -190,6 +204,77 @@ def _has_z_image_keys(state_dict: dict[str | int, Any]) -> bool: return False +def _get_krea2_variant_from_name(name: str) -> Krea2VariantType: + """Guess the Krea-2 variant from a single-file/GGUF filename. + + Turbo and Raw (Base) share the identical transformer architecture, so a single-file checkpoint + cannot be distinguished from its weights. Filenames with a "raw"/"base" token (e.g. "Krea-2-Raw", + "krea2_base_q4") indicate the undistilled Base model; everything else defaults to the distilled + Turbo. The user can override the variant in the model manager. + """ + lowered = name.lower() + # "turbo" is a strong positive signal for the distilled checkpoint and wins outright, so a Turbo file + # whose name merely *contains* "base"/"raw" as a substring (e.g. "baseline", "database", "raw_export") + # is not misread as Base. + if "turbo" in lowered: + return Krea2VariantType.Turbo + # Otherwise match "raw"/"base" only as a whole token delimited by non-alphanumeric separators + # ("-", "_", ".") - not as an arbitrary substring. + tokens = re.split(r"[^a-z0-9]+", lowered) + if "raw" in tokens or "base" in tokens: + return Krea2VariantType.Base + return Krea2VariantType.Turbo + + +def _has_krea2_keys(state_dict: dict[str | int, Any]) -> bool: + """Check if state dict contains Krea-2 (Krea2Transformer2DModel) transformer keys. + + Krea-2's single-stream MMDiT has a distinctive text-fusion stage; the ``text_fusion.`` + prefix (with ``layerwise_blocks`` / ``refiner_blocks`` / ``projector``) is unique to it. + Returns True only for Krea-2 main models, not LoRAs. + """ + # The text-fusion stage is unique to Krea-2. Diffusers naming uses `text_fusion`/`time_mod_proj`; + # the native/ComfyUI GGUF conversion uses the compact `txtfusion`/`tproj` names instead. + krea2_specific_keys = { + "text_fusion", # text-fusion stage (diffusers naming) - unique to Krea-2 + "txtfusion", # text-fusion stage (native/ComfyUI GGUF naming) + "time_mod_proj", # timestep modulation projection (diffusers) + } + # Corroborating image-input signals: `img_in` (diffusers) / `first` (native), or the timestep + # modulation projection (`tproj` native). + krea2_corroborating_keys = {"img_in", "first", "tproj"} + + lora_suffixes = ( + ".lora_down.weight", + ".lora_up.weight", + ".lora_A.weight", + ".lora_B.weight", + ".dora_scale", + ".alpha", + ) + + # If any key has a LoRA suffix, this is a LoRA, not a main model. + for key in state_dict.keys(): + if isinstance(key, int): + continue + if key.endswith(lora_suffixes): + return False + + has_text_fusion = False + has_corroborator = False + for key in state_dict.keys(): + if isinstance(key, int): + continue + # Handle both direct keys and ComfyUI-style (model.diffusion_model.*) keys. + key_parts = key.split(".") + if any(part in krea2_specific_keys for part in key_parts): + has_text_fusion = True + if any(part in krea2_corroborating_keys for part in key_parts): + has_corroborator = True + # Require the distinctive text-fusion stage; the image-input key is a corroborating signal. + return has_text_fusion and has_corroborator + + class Main_SD_Checkpoint_Config_Base(Checkpoint_Config_Base, Main_Config_Base): """Model config for main checkpoint models.""" @@ -1285,6 +1370,120 @@ def _validate_looks_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: raise NotAMatchError("state dict does not look like GGUF quantized") +class Main_Diffusers_Krea2_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): + """Model config for Krea-2 diffusers models (Krea-2-Turbo).""" + + base: Literal[BaseModelType.Krea2] = Field(BaseModelType.Krea2) + variant: Krea2VariantType = Field() + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_dir(mod) + + raise_for_override_fields(cls, override_fields) + + # This check implies the base type - no further validation needed. + raise_for_class_name( + common_config_paths(mod.path), + { + "Krea2Pipeline", + }, + ) + + variant = override_fields.pop("variant", None) or cls._get_variant(mod) + + repo_variant = override_fields.pop("repo_variant", None) or cls._get_repo_variant_or_raise(mod) + + return cls( + **override_fields, + variant=variant, + repo_variant=repo_variant, + ) + + @classmethod + def _get_variant(cls, mod: ModelOnDisk) -> Krea2VariantType: + """Determine the Krea-2 variant from the pipeline-level ``is_distilled`` flag. + + Krea-2-Turbo sets ``is_distilled=true`` in model_index.json (distilled, 8 steps, CFG off); + Krea-2-Raw sets ``is_distilled=false`` (undistilled Base, more steps, CFG on). The transformer + architectures are identical, so this flag is the only reliable discriminator. + """ + # model_index.json was already validated by the class-name check in from_model_on_disk, so a + # read/parse failure here is a genuine identification error and is allowed to propagate rather + # than being silently registered as Turbo (which would give a Raw model the wrong defaults). + config = get_config_dict_or_raise(mod.path / "model_index.json") + if config.get("is_distilled", False) is False: + return Krea2VariantType.Base + return Krea2VariantType.Turbo + + +class Main_Checkpoint_Krea2_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): + """Model config for Krea-2 single-file checkpoint models (safetensors, etc).""" + + base: Literal[BaseModelType.Krea2] = Field(default=BaseModelType.Krea2) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + variant: Krea2VariantType = Field() + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_file(mod) + + raise_for_override_fields(cls, override_fields) + + if mod.path.suffix.lower() != ".safetensors": + raise NotAMatchError(f"expected a .safetensors file, got {mod.path.suffix or '(no suffix)'}") + + cls._validate_looks_like_krea2_model(mod) + + cls._validate_does_not_look_like_gguf_quantized(mod) + + variant = override_fields.pop("variant", None) or _get_krea2_variant_from_name(mod.path.name) + + return cls(**override_fields, variant=variant) + + @classmethod + def _validate_looks_like_krea2_model(cls, mod: ModelOnDisk) -> None: + if not _has_krea2_keys(mod.load_state_dict()): + raise NotAMatchError("state dict does not look like a Krea-2 model") + + @classmethod + def _validate_does_not_look_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: + if _has_ggml_tensors(mod.load_state_dict()): + raise NotAMatchError("state dict looks like GGUF quantized") + + +class Main_GGUF_Krea2_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): + """Model config for GGUF-quantized Krea-2 transformer models (single-file).""" + + base: Literal[BaseModelType.Krea2] = Field(default=BaseModelType.Krea2) + format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized) + variant: Krea2VariantType = Field() + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_file(mod) + + raise_for_override_fields(cls, override_fields) + + cls._validate_looks_like_krea2_model(mod) + + cls._validate_looks_like_gguf_quantized(mod) + + variant = override_fields.pop("variant", None) or _get_krea2_variant_from_name(mod.path.name) + + return cls(**override_fields, variant=variant) + + @classmethod + def _validate_looks_like_krea2_model(cls, mod: ModelOnDisk) -> None: + if not _has_krea2_keys(mod.load_state_dict()): + raise NotAMatchError("state dict does not look like a Krea-2 model") + + @classmethod + def _validate_looks_like_gguf_quantized(cls, mod: ModelOnDisk) -> None: + if not _has_ggml_tensors(mod.load_state_dict()): + raise NotAMatchError("state dict does not look like GGUF quantized") + + class Main_Diffusers_QwenImage_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): """Model config for Qwen Image diffusers models (both txt2img and edit).""" diff --git a/invokeai/backend/model_manager/configs/qwen3_encoder.py b/invokeai/backend/model_manager/configs/qwen3_encoder.py index b026c03db2f..2b072e9f3d8 100644 --- a/invokeai/backend/model_manager/configs/qwen3_encoder.py +++ b/invokeai/backend/model_manager/configs/qwen3_encoder.py @@ -47,17 +47,21 @@ def _has_ggml_tensors(state_dict: dict[str | int, Any]) -> bool: def _has_qwen_vl_visual_tower(state_dict: dict[str | int, Any]) -> bool: - """Check if state dict bundles a Qwen2.5-VL / Qwen2-VL vision tower. - - Qwen-VL encoders ship the visual tower (`visual.blocks.*`, `visual.patch_embed.*`) - alongside the language model, whereas a text-only Qwen3 encoder never does. A Qwen-VL - file otherwise satisfies the Qwen3 key heuristic (it has `model.layers.*` / - `model.embed_tokens.weight` too), so without this check it matches *both* the Qwen3 and - the QwenVLEncoder configs and the tiebreak can misroute it to Qwen3. We use it to keep - the two mutually exclusive. + """Check if state dict bundles a Qwen-VL vision tower (Qwen2-VL / Qwen2.5-VL / Qwen3-VL). + + VL encoders ship a visual tower alongside the language model, whereas a text-only Qwen3 encoder + never does. A VL file otherwise satisfies the Qwen3 key heuristic (it has ``model.layers.*`` / + ``model.embed_tokens.weight`` too), so without this check it matches *both* the text-only Qwen3 + config and the VL config and the tiebreak can misroute it. We use it to keep them mutually exclusive. + + The predicate deliberately mirrors ``_is_qwen3_vl_encoder_state_dict`` (qwen3_vl_encoder.py) so both + sides agree on what counts as a visual tower - crucially including the nested ``model.visual.*`` + layout that ComfyUI single-file Qwen3-VL checkpoints use. Matching only bare ``visual.blocks.*`` + missed that layout, letting a single-file Qwen3-VL 4B encoder match both configs and get misrouted to + the text-only Qwen3 type - silently breaking the single-file/GGUF Krea-2 encoder install path. """ for key in state_dict.keys(): - if isinstance(key, str) and (key.startswith("visual.blocks.") or key.startswith("visual.patch_embed.")): + if isinstance(key, str) and (key.startswith(("visual.", "model.visual.")) or ".visual." in key): return True return False diff --git a/invokeai/backend/model_manager/configs/qwen3_vl_encoder.py b/invokeai/backend/model_manager/configs/qwen3_vl_encoder.py new file mode 100644 index 00000000000..b4af3d43859 --- /dev/null +++ b/invokeai/backend/model_manager/configs/qwen3_vl_encoder.py @@ -0,0 +1,195 @@ +from pathlib import Path +from typing import Any, Literal, Self + +from pydantic import Field + +from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base, Config_Base +from invokeai.backend.model_manager.configs.identification_utils import ( + NotAMatchError, + get_config_dict_or_raise, + raise_for_class_name, + raise_for_override_fields, + raise_if_not_dir, + raise_if_not_file, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType + +_KREA2_QWEN3_VL_HIDDEN_SIZE = 2560 +_KREA2_QWEN3_VL_NUM_HIDDEN_LAYERS = 36 + + +def _validate_krea2_qwen3_vl_config(config_path: Path) -> None: + config = get_config_dict_or_raise(config_path) + text_config = config.get("text_config", config) + if not isinstance(text_config, dict): + raise NotAMatchError("Qwen3-VL text_config must be an object") + hidden_size = text_config.get("hidden_size") + num_hidden_layers = text_config.get("num_hidden_layers") + if hidden_size != _KREA2_QWEN3_VL_HIDDEN_SIZE: + raise NotAMatchError( + f"Krea-2 requires the Qwen3-VL 4B hidden size {_KREA2_QWEN3_VL_HIDDEN_SIZE}, got {hidden_size}" + ) + if num_hidden_layers != _KREA2_QWEN3_VL_NUM_HIDDEN_LAYERS: + raise NotAMatchError( + f"Krea-2 requires {_KREA2_QWEN3_VL_NUM_HIDDEN_LAYERS} Qwen3-VL layers, got {num_hidden_layers}" + ) + + +def _has_complete_pretrained_weights(weights_path: Path) -> bool: + if (weights_path / "model.safetensors").is_file() or (weights_path / "pytorch_model.bin").is_file(): + return True + + for index_name in ("model.safetensors.index.json", "pytorch_model.bin.index.json"): + index_path = weights_path / index_name + if not index_path.is_file(): + continue + index = get_config_dict_or_raise(index_path) + weight_map = index.get("weight_map") + if not isinstance(weight_map, dict) or not weight_map: + return False + filenames = list(weight_map.values()) + if not all(isinstance(filename, str) and filename for filename in filenames): + return False + root = weights_path.resolve() + referenced_files: set[Path] = set() + for filename in filenames: + filename_path = Path(filename) + if filename_path.is_absolute(): + return False + candidate = (weights_path / filename_path).resolve() + if not candidate.is_relative_to(root): + return False + referenced_files.add(candidate) + return bool(referenced_files) and all(path.is_file() for path in referenced_files) + return False + + +def _validate_krea2_qwen3_vl_checkpoint_shape(state_dict: dict[str | int, Any]) -> None: + embed_keys = ( + "model.embed_tokens.weight", + "model.language_model.embed_tokens.weight", + "language_model.embed_tokens.weight", + "embed_tokens.weight", + ) + embed = next((state_dict[key] for key in embed_keys if key in state_dict), None) + shape = getattr(embed, "shape", ()) + if len(shape) < 2 or shape[1] != _KREA2_QWEN3_VL_HIDDEN_SIZE: + hidden_size = shape[1] if len(shape) >= 2 else None + raise NotAMatchError( + f"Krea-2 requires a Qwen3-VL 4B checkpoint with hidden size " + f"{_KREA2_QWEN3_VL_HIDDEN_SIZE}, got {hidden_size}" + ) + if not any(isinstance(key, str) and ".layers.35." in key for key in state_dict): + raise NotAMatchError("Krea-2 requires a Qwen3-VL 4B checkpoint containing language-model layer 35") + + +class Qwen3VLEncoder_Qwen3VLEncoder_Config(Config_Base): + """Configuration for standalone Qwen3-VL text encoder models (diffusers-like directory format). + + Used by Krea-2, whose text conditioning comes from a Qwen3-VL model (``Qwen3VLModel``). The model + weights are expected either in a ``text_encoder`` subfolder of the model directory or directly at the + root (standalone download). This is distinct from the text-only ``Qwen3Encoder`` (Z-Image / FLUX.2 + Klein) and the Qwen2.5-VL ``QwenVLEncoder`` (Qwen Image). + """ + + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.Qwen3VLEncoder] = Field(default=ModelType.Qwen3VLEncoder) + format: Literal[ModelFormat.Qwen3VLEncoder] = Field(default=ModelFormat.Qwen3VLEncoder) + cpu_only: bool | None = Field(default=None, description="Whether this model should run on CPU only") + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_dir(mod) + + raise_for_override_fields(cls, override_fields) + + # Exclude full pipeline models - these should be matched as main models, not just encoders. + model_index_path = mod.path / "model_index.json" + transformer_path = mod.path / "transformer" + if model_index_path.exists() or transformer_path.exists(): + raise NotAMatchError( + "directory looks like a full diffusers pipeline (has model_index.json or transformer folder), " + "not a standalone Qwen3-VL encoder" + ) + + # Support both a nested text_encoder/config.json and a standalone config.json at the root. + config_path_nested = mod.path / "text_encoder" / "config.json" + config_path_direct = mod.path / "config.json" + + if config_path_nested.exists(): + expected_config_path = config_path_nested + elif config_path_direct.exists(): + expected_config_path = config_path_direct + else: + raise NotAMatchError(f"unable to load config file: {config_path_nested} does not exist") + + # Qwen3-VL uses the Qwen3VLModel / Qwen3VLForConditionalGeneration architecture. + raise_for_class_name( + expected_config_path, + { + "Qwen3VLModel", + "Qwen3VLForConditionalGeneration", + }, + ) + _validate_krea2_qwen3_vl_config(expected_config_path) + + if config_path_nested.exists(): + weights_path = mod.path / "text_encoder" + tokenizer_path = mod.path / "tokenizer" + else: + weights_path = mod.path + tokenizer_path = mod.path + + has_weights = _has_complete_pretrained_weights(weights_path) + has_tokenizer = (tokenizer_path / "tokenizer.json").exists() or ( + (tokenizer_path / "vocab.json").exists() and (tokenizer_path / "merges.txt").exists() + ) + if not has_weights: + raise NotAMatchError("standalone Qwen3-VL encoder directory does not contain model weights") + if not has_tokenizer: + raise NotAMatchError("standalone Qwen3-VL encoder directory does not contain tokenizer files") + + return cls(**override_fields) + + +def _is_qwen3_vl_encoder_state_dict(state_dict: dict[str | int, Any]) -> bool: + """True for a single-file Qwen3-VL encoder: a Qwen3 text decoder PLUS a visual tower. + + The visual tower (``visual.*`` / ``model.visual.*``) distinguishes Qwen3-VL from the text-only + ``Qwen3Encoder`` (Z-Image / FLUX.2 Klein), which has ``model.layers.*`` but no visual tower. + """ + str_keys = [k for k in state_dict if isinstance(k, str)] + has_text_decoder = any(".layers." in k and ("model." in k or k.startswith("layers.")) for k in str_keys) + has_visual_tower = any(k.startswith(("visual.", "model.visual.")) or ".visual." in k for k in str_keys) + return has_text_decoder and has_visual_tower + + +class Qwen3VLEncoder_Checkpoint_Config(Checkpoint_Config_Base, Config_Base): + """Configuration for a single-file Qwen3-VL text encoder checkpoint (e.g. ComfyUI ``qwen3vl_4b_*``). + + Distinguished from the text-only ``Qwen3Encoder`` checkpoint (Z-Image) by the presence of the + Qwen3-VL visual tower. The tokenizer is not bundled in single-file checkpoints and is pulled from + HuggingFace (``Qwen/Qwen3-VL-4B-Instruct``) by the loader. + """ + + base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) + type: Literal[ModelType.Qwen3VLEncoder] = Field(default=ModelType.Qwen3VLEncoder) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + cpu_only: bool | None = Field(default=None, description="Whether this model should run on CPU only") + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_file(mod) + + raise_for_override_fields(cls, override_fields) + + if mod.path.suffix.lower() != ".safetensors": + raise NotAMatchError(f"expected a .safetensors file, got {mod.path.suffix or '(no suffix)'}") + + state_dict = mod.load_state_dict() + if not _is_qwen3_vl_encoder_state_dict(state_dict): + raise NotAMatchError("state dict does not look like a single-file Qwen3-VL encoder") + _validate_krea2_qwen3_vl_checkpoint_shape(state_dict) + + return cls(**override_fields) diff --git a/invokeai/backend/model_manager/load/model_loaders/krea2.py b/invokeai/backend/model_manager/load/model_loaders/krea2.py new file mode 100644 index 00000000000..7757bf89b6d --- /dev/null +++ b/invokeai/backend/model_manager/load/model_loaders/krea2.py @@ -0,0 +1,576 @@ +# Copyright (c) 2024, Lincoln D. Stein and the InvokeAI Development Team +"""Class for Krea-2 model loading in InvokeAI.""" + +from pathlib import Path +from typing import Any, Optional + +import accelerate +from transformers import AutoConfig, AutoTokenizer + +from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base, Diffusers_Config_Base +from invokeai.backend.model_manager.configs.factory import AnyModelConfig +from invokeai.backend.model_manager.configs.main import Main_Checkpoint_Krea2_Config, Main_GGUF_Krea2_Config +from invokeai.backend.model_manager.configs.qwen3_vl_encoder import ( + Qwen3VLEncoder_Checkpoint_Config, + Qwen3VLEncoder_Qwen3VLEncoder_Config, +) +from invokeai.backend.model_manager.load.load_default import ModelLoader +from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry +from invokeai.backend.model_manager.load.model_loaders.generic_diffusers import GenericDiffusersLoader +from invokeai.backend.model_manager.taxonomy import ( + AnyModel, + BaseModelType, + ModelFormat, + ModelType, + SubModelType, +) +from invokeai.backend.quantization.gguf.loaders import gguf_sd_loader +from invokeai.backend.util.devices import TorchDevice + + +def _normalize_qwen3vl_rope_config(config: Any) -> Any: + """Mirror Qwen3-VL rope_parameters into rope_scaling for Transformers compatibility.""" + text_config = getattr(config, "text_config", None) + if text_config is not None: + rope_params = getattr(text_config, "rope_parameters", None) + if getattr(text_config, "rope_scaling", None) is None and rope_params is not None: + text_config.rope_scaling = rope_params + return config + + +def _strip_comfyui_prefix(sd: dict[str, Any]) -> dict[str, Any]: + """Strip ComfyUI-style ``model.diffusion_model.`` / ``diffusion_model.`` key prefixes if present.""" + prefix_to_strip = None + for prefix in ("model.diffusion_model.", "diffusion_model."): + if any(isinstance(k, str) and k.startswith(prefix) for k in sd.keys()): + prefix_to_strip = prefix + break + if not prefix_to_strip: + return sd + return { + (k[len(prefix_to_strip) :] if isinstance(k, str) and k.startswith(prefix_to_strip) else k): v + for k, v in sd.items() + } + + +def _to_plain_tensor(value: Any) -> Any: + """Dequantize a GGMLTensor to a plain tensor (needed before reshape); pass others through.""" + if hasattr(value, "get_dequantized_tensor"): + return value.get_dequantized_tensor() + return value + + +def _is_native_krea2_format(sd: dict[str, Any]) -> bool: + """Detect the native/ComfyUI Krea-2 key naming (e.g. GGUF) vs. the diffusers naming.""" + return any( + isinstance(k, str) and (k.startswith(("blocks.", "txtfusion.", "first.")) or ".mod.lin" in k) for k in sd + ) + + +def _dequantize_scaled_fp8(sd: dict[str, Any]) -> dict[str, Any]: + """Dequantize ComfyUI 'scaled fp8' weights: ``dequant = weight.float() * weight_scale``. + + Each quantized layer stores an fp8 ``.weight`` plus a (usually scalar) ``.weight_scale``. + Returns a new dict with the weights dequantized to float and the ``.weight_scale`` keys removed. + No-op if there are no scale keys. + """ + import torch + + scale_keys = [k for k in sd if isinstance(k, str) and k.endswith(".weight_scale")] + if not scale_keys: + return sd + out = dict(sd) + for scale_key in scale_keys: + weight_key = scale_key.replace(".weight_scale", ".weight") + if weight_key in out: + weight = torch.as_tensor(_to_plain_tensor(out[weight_key])).float() + scale = torch.as_tensor(_to_plain_tensor(out[scale_key])).float() + out[weight_key] = weight * scale + del out[scale_key] + return out + + +def _convert_krea2_native_to_diffusers(sd: dict[str, Any]) -> dict[str, Any]: + """Convert a native/ComfyUI-format Krea-2 state dict (e.g. GGUF) to diffusers Krea2Transformer2DModel keys. + + Top-level module renames:: + + blocks.N.* -> transformer_blocks.N.* + txtfusion.* -> text_fusion.* + first.* -> img_in.* + tmlp.0/2.* -> time_embed.linear_1/2.* + tproj.1.* -> time_mod_proj.* + txtmlp.0/1/3.* -> txt_in.norm / linear_1 / linear_2.* + last.linear/norm/modulation -> final_layer.linear / norm.weight / scale_shift_table + + Within every transformer / text-fusion block:: + + attn.wq/wk/wv/wo -> attn.to_q/to_k/to_v/to_out.0 + attn.gate -> attn.to_gate + attn.qknorm.qnorm/knorm.scale -> attn.norm_q/norm_k.weight + mlp.gate/up/down -> ff.gate/up/down + prenorm/postnorm.scale -> norm1/norm2.weight + mod.lin (6*H,) -> scale_shift_table (6, H) + + The original final-block ``last.down``/``last.up`` projections have no counterpart in the diffusers + ``Krea2FinalLayer`` (a clean AdaLN + linear) and are dropped. + """ + import torch + + new_sd: dict[str, Any] = {} + for key, value in sd.items(): + if not isinstance(key, str): + new_sd[key] = value + continue + # Drop original-only final-block projections (no diffusers equivalent). + if key in ("last.down.weight", "last.up.weight"): + continue + + k = key + # Top-level module prefixes. + if k.startswith("blocks."): + k = "transformer_blocks." + k[len("blocks.") :] + elif k.startswith("txtfusion."): + k = "text_fusion." + k[len("txtfusion.") :] + elif k.startswith("first."): + k = "img_in." + k[len("first.") :] + elif k.startswith("tmlp.0."): + k = "time_embed.linear_1." + k[len("tmlp.0.") :] + elif k.startswith("tmlp.2."): + k = "time_embed.linear_2." + k[len("tmlp.2.") :] + elif k.startswith("tproj.1."): + k = "time_mod_proj." + k[len("tproj.1.") :] + elif k == "txtmlp.0.scale": + k = "txt_in.norm.weight" + elif k.startswith("txtmlp.1."): + k = "txt_in.linear_1." + k[len("txtmlp.1.") :] + elif k.startswith("txtmlp.3."): + k = "txt_in.linear_2." + k[len("txtmlp.3.") :] + elif k == "last.linear.weight": + k = "final_layer.linear.weight" + elif k == "last.linear.bias": + k = "final_layer.linear.bias" + elif k == "last.norm.scale": + k = "final_layer.norm.weight" + elif k == "last.modulation.lin": + k = "final_layer.scale_shift_table" + # Krea2FinalLayer.scale_shift_table is (2, hidden) (scale, shift). Reshape the flat native + # table just like the per-block (6, hidden) tables below - otherwise load_state_dict(assign=True) + # installs a wrong-shaped 1-D parameter (which the meta-only completeness guard cannot catch) + # and the final layer fails at inference. + value = torch.as_tensor(_to_plain_tensor(value)).reshape(2, -1) + + # Within-block sub-module renames (apply to transformer_blocks.* and text_fusion.*). + k = k.replace(".attn.wq.weight", ".attn.to_q.weight") + k = k.replace(".attn.wk.weight", ".attn.to_k.weight") + k = k.replace(".attn.wv.weight", ".attn.to_v.weight") + k = k.replace(".attn.wo.weight", ".attn.to_out.0.weight") + k = k.replace(".attn.gate.weight", ".attn.to_gate.weight") + k = k.replace(".attn.qknorm.qnorm.scale", ".attn.norm_q.weight") + k = k.replace(".attn.qknorm.knorm.scale", ".attn.norm_k.weight") + k = k.replace(".mlp.gate.weight", ".ff.gate.weight") + k = k.replace(".mlp.up.weight", ".ff.up.weight") + k = k.replace(".mlp.down.weight", ".ff.down.weight") + k = k.replace(".prenorm.scale", ".norm1.weight") + k = k.replace(".postnorm.scale", ".norm2.weight") + + # Per-image-block modulation table: flat (6*H,) -> (6, H). + if k.endswith(".mod.lin"): + k = k[: -len(".mod.lin")] + ".scale_shift_table" + value = torch.as_tensor(_to_plain_tensor(value)).reshape(6, -1) + + new_sd[k] = value + return new_sd + + +# Default Krea2Transformer2DModel config (from the Krea-2-Turbo transformer/config.json). Used when +# loading a bare single-file checkpoint that has no accompanying config.json. +KREA2_TRANSFORMER_CONFIG = { + "attention_head_dim": 128, + "axes_dims_rope": [32, 48, 48], + "in_channels": 64, + "intermediate_size": 16384, + "norm_eps": 1e-05, + "num_attention_heads": 48, + "num_key_value_heads": 12, + "num_layers": 28, + "num_layerwise_text_blocks": 2, + "num_refiner_text_blocks": 2, + "num_text_layers": 12, + "rope_theta": 1000.0, + "text_hidden_dim": 2560, + "text_intermediate_size": 6912, + "text_num_attention_heads": 20, + "text_num_key_value_heads": 20, + "timestep_embed_dim": 256, +} + + +@ModelLoaderRegistry.register(base=BaseModelType.Krea2, type=ModelType.Main, format=ModelFormat.Diffusers) +class Krea2DiffusersModel(GenericDiffusersLoader): + """Class to load Krea-2 main models (Krea-2-Turbo) in diffusers format. + + Loads every submodel (transformer, vae, text_encoder, tokenizer, scheduler) from the diffusers + pipeline folder via the class names declared in model_index.json. The transformer resolves to + diffusers' ``Krea2Transformer2DModel`` (only available in diffusers main / >=0.39); the VAE to + ``AutoencoderKLQwenImage`` and the text encoder to ``Qwen3VLModel``. + """ + + def _load_model( + self, + config: AnyModelConfig, + submodel_type: Optional[SubModelType] = None, + ) -> AnyModel: + if isinstance(config, Checkpoint_Config_Base): + raise NotImplementedError("CheckpointConfigBase is not implemented for the Krea-2 diffusers loader.") + + if submodel_type is None: + raise Exception("A submodel type must be provided when loading main pipelines.") + + model_path = Path(config.path) + + # model_index.json declares the tokenizer as the slow `Qwen2Tokenizer`, which requires + # vocab.json/merges.txt. Krea-2 ships only a fast tokenizer.json, so load via AutoTokenizer + # (which resolves to Qwen2TokenizerFast from tokenizer.json). + # + # Krea-2's tokenizer_config.json stores `extra_special_tokens` as a list (the special tokens + # are already baked into tokenizer.json as added tokens). Newer transformers expects a dict and + # crashes on the list, so override it with an empty dict — the special tokens are still + # recognized from tokenizer.json. + if submodel_type is SubModelType.Tokenizer: + return AutoTokenizer.from_pretrained( + model_path / submodel_type.value, local_files_only=True, extra_special_tokens={} + ) + + load_class = self.get_hf_load_class(model_path, submodel_type) + repo_variant = config.repo_variant if isinstance(config, Diffusers_Config_Base) else None + variant = repo_variant.value if repo_variant else None + model_path = model_path / submodel_type.value + + # Krea-2 prefers bfloat16; use a safe dtype based on target device capabilities. + target_device = TorchDevice.choose_torch_device() + dtype = TorchDevice.choose_bfloat16_safe_dtype(target_device) + + extra_kwargs: dict[str, Any] = {} + if submodel_type is SubModelType.TextEncoder: + # Krea-2's Qwen3-VL text_encoder config stores rope settings under `rope_parameters`, but the + # installed transformers' Qwen3VL rotary embedding reads `rope_scaling` (None here) → crash. + # Patch the config so rope_scaling mirrors rope_parameters before instantiating the model. + te_config = _normalize_qwen3vl_rope_config(AutoConfig.from_pretrained(model_path, local_files_only=True)) + extra_kwargs["config"] = te_config + + try: + result: AnyModel = load_class.from_pretrained( + model_path, + torch_dtype=dtype, + variant=variant, + **extra_kwargs, + ) + except OSError as e: + if variant and "no file named" in str(e): + # try without the variant, just in case the user's preferences changed + result = load_class.from_pretrained(model_path, torch_dtype=dtype, **extra_kwargs) + else: + raise e + + result = self._apply_fp8_layerwise_casting(result, config, submodel_type) + return result + + +@ModelLoaderRegistry.register(base=BaseModelType.Krea2, type=ModelType.Main, format=ModelFormat.Checkpoint) +class Krea2CheckpointModel(ModelLoader): + """Class to load Krea-2 transformer models from single-file checkpoints (safetensors). + + Handles plain bf16/fp16 checkpoints as well as ComfyUI 'scaled fp8' checkpoints (fp8 weight + + ``.weight_scale``), and both the diffusers and native/ComfyUI key naming. Apply the fp8-storage + setting to keep the (large) transformer fp8-resident; otherwise it loads in full precision. + """ + + def _load_model( + self, + config: AnyModelConfig, + submodel_type: Optional[SubModelType] = None, + ) -> AnyModel: + if not isinstance(config, Checkpoint_Config_Base): + raise ValueError("Only CheckpointConfigBase models are supported here.") + + if submodel_type is not SubModelType.Transformer: + raise ValueError( + f"Only Transformer submodels are supported. Received: {submodel_type.value if submodel_type else 'None'}" + ) + return self._load_from_singlefile(config) + + def _load_from_singlefile(self, config: AnyModelConfig) -> AnyModel: + from diffusers import Krea2Transformer2DModel + from safetensors.torch import load_file + + if not isinstance(config, Main_Checkpoint_Krea2_Config): + raise TypeError(f"Expected Main_Checkpoint_Krea2_Config, got {type(config).__name__}.") + model_path = Path(config.path) + + sd = load_file(model_path) + sd = _strip_comfyui_prefix(sd) + # ComfyUI 'scaled fp8' checkpoints: fold the per-tensor weight_scale into the weights (→ float). + sd = _dequantize_scaled_fp8(sd) + # Native/ComfyUI key naming → diffusers Krea2Transformer2DModel keys. + if _is_native_krea2_format(sd): + sd = _convert_krea2_native_to_diffusers(sd) + + target_device = TorchDevice.choose_torch_device() + model_dtype = TorchDevice.choose_bfloat16_safe_dtype(target_device) + + with accelerate.init_empty_weights(): + model = Krea2Transformer2DModel(**KREA2_TRANSFORMER_CONFIG) + + new_sd_size = sum(ten.nelement() * model_dtype.itemsize for ten in sd.values()) + self._ram_cache.make_room(new_sd_size) + for k in sd.keys(): + sd[k] = sd[k].to(model_dtype) + + model.load_state_dict(sd, assign=True, strict=False) + _reject_incomplete_load(model, what="Krea-2 single-file checkpoint") + # Honor the fp8-storage setting (re-quantizes the dequantized weights to fp8-resident on CUDA). + model = self._apply_fp8_layerwise_casting(model, config, SubModelType.Transformer) + return model + + +@ModelLoaderRegistry.register(base=BaseModelType.Krea2, type=ModelType.Main, format=ModelFormat.GGUFQuantized) +class Krea2GGUFCheckpointModel(ModelLoader): + """Class to load GGUF-quantized Krea-2 transformer models (single-file). + + GGUF ships only the transformer; the VAE (Qwen-Image), Qwen3-VL encoder, tokenizer and scheduler + are sourced separately by the Krea-2 model-loader invocation (mix-and-match, like Z-Image/FLUX). + The GGML tensors stay quantized and are dequantized on-the-fly during inference. + """ + + def _load_model( + self, + config: AnyModelConfig, + submodel_type: Optional[SubModelType] = None, + ) -> AnyModel: + if not isinstance(config, Checkpoint_Config_Base): + raise ValueError("Only CheckpointConfigBase models are supported here.") + if submodel_type is not SubModelType.Transformer: + raise ValueError( + f"Only Transformer submodels are supported. Received: {submodel_type.value if submodel_type else 'None'}" + ) + return self._load_from_gguf(config) + + def _load_from_gguf(self, config: AnyModelConfig) -> AnyModel: + from diffusers import Krea2Transformer2DModel + + if not isinstance(config, Main_GGUF_Krea2_Config): + raise TypeError(f"Expected Main_GGUF_Krea2_Config, got {type(config).__name__}.") + + model_path = Path(config.path) + target_device = TorchDevice.choose_torch_device() + compute_dtype = TorchDevice.choose_bfloat16_safe_dtype(target_device) + + # GGMLTensor wrappers (kept on CPU; dequantized on-the-fly by the cache during inference). + sd = gguf_sd_loader(model_path, compute_dtype=compute_dtype) + sd = _strip_comfyui_prefix(sd) + # GGUF conversions use the native/ComfyUI compact key naming; remap to diffusers keys. + if _is_native_krea2_format(sd): + sd = _convert_krea2_native_to_diffusers(sd) + + with accelerate.init_empty_weights(): + model = Krea2Transformer2DModel(**KREA2_TRANSFORMER_CONFIG) + + model.load_state_dict(sd, assign=True, strict=False) + # Reject GGUF layouts that don't fully populate the diffusers Krea2Transformer2DModel (city96/ + # ComfyUI GGUFs may use key names needing conversion). Failing here beats a confusing meta-tensor + # crash mid-inference. + _reject_incomplete_load(model, what="Krea-2 GGUF checkpoint") + return model + + +@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.Qwen3VLEncoder, format=ModelFormat.Qwen3VLEncoder) +class Qwen3VLEncoderLoader(ModelLoader): + """Class to load standalone Qwen3-VL text encoder models for Krea-2 (directory format).""" + + def _load_model( + self, + config: AnyModelConfig, + submodel_type: Optional[SubModelType] = None, + ) -> AnyModel: + from transformers import Qwen3VLModel + + if not isinstance(config, Qwen3VLEncoder_Qwen3VLEncoder_Config): + raise ValueError("Only Qwen3VLEncoder_Qwen3VLEncoder_Config models are supported here.") + + model_path = Path(config.path) + + # Support both a full pipeline-style layout (text_encoder/ + tokenizer/) and a standalone + # download where the encoder files live directly at the root. + text_encoder_path = model_path / "text_encoder" + tokenizer_path = model_path / "tokenizer" + is_standalone = not text_encoder_path.exists() and (model_path / "config.json").exists() + if is_standalone: + text_encoder_path = model_path + tokenizer_path = model_path + + match submodel_type: + case SubModelType.Tokenizer: + # extra_special_tokens={} works around Krea-2's list-format tokenizer_config (see + # Krea2DiffusersModel); harmless for well-formed configs. + return AutoTokenizer.from_pretrained(tokenizer_path, local_files_only=True, extra_special_tokens={}) + case SubModelType.TextEncoder: + target_device = TorchDevice.choose_torch_device() + model_dtype = TorchDevice.choose_bfloat16_safe_dtype(target_device) + te_config = _normalize_qwen3vl_rope_config( + AutoConfig.from_pretrained(text_encoder_path, local_files_only=True) + ) + return Qwen3VLModel.from_pretrained( + text_encoder_path, + config=te_config, + torch_dtype=model_dtype, + low_cpu_mem_usage=True, + local_files_only=True, + ) + + raise ValueError( + f"Only Tokenizer and TextEncoder submodels are supported. " + f"Received: {submodel_type.value if submodel_type else 'None'}" + ) + + +def _remap_qwen3vl_singlefile_keys(sd: dict[str, Any]) -> dict[str, Any]: + """Remap ComfyUI single-file Qwen3-VL keys to the transformers ``Qwen3VLModel`` layout. + + ComfyUI/native layout uses a single ``model.`` prefix for both towers; transformers splits them: + ``model.visual.*`` -> ``visual.*`` and ``model.`` (layers/embed_tokens/norm) -> ``language_model.``. + """ + out: dict[str, Any] = {} + for k, v in sd.items(): + if not isinstance(k, str): + out[k] = v + continue + # Strip a leading "model." (some checkpoints prefix everything with it), then route by tower. + key = k[len("model.") :] if k.startswith("model.") else k + if key.startswith("visual.") or key.startswith("language_model."): + # Already the transformers layout (e.g. "model.language_model.*" / "model.visual.*"). + out[key] = v + else: + # Bare language-model keys (layers.* / embed_tokens / norm) belong under language_model. + out["language_model." + key] = v + return out + + +def _reject_incomplete_load(model: Any, *, what: str) -> None: + """Raise if a ``load_state_dict(strict=False)`` left required tensors on the meta device. + + ``strict=False`` is used to tolerate benign extra/renamed keys, but it also silently accepts a + checkpoint that omits required weights — those tensors stay on the meta device and only fail much + later during inference. Reject such loads here, naming the offending tensors, so an incomplete, + misidentified, or differently-converted checkpoint fails at load time with an actionable message. + + Both parameters *and persistent buffers* are checked: ``accelerate.init_empty_weights()`` places + buffers on the meta device too, so a native/GGUF checkpoint that omits a persistent buffer would + slip past a parameters-only guard and fail mid-inference instead of at load time. + """ + still_meta = [ + name + for name, tensor in (*model.named_parameters(), *model.named_buffers()) + if getattr(tensor, "is_meta", False) + ] + if still_meta: + raise RuntimeError( + f"{what} is incomplete: {len(still_meta)} tensor(s) were not provided by the checkpoint " + f"and remain uninitialized (meta device). First few: {still_meta[:8]}. The file is likely " + "incomplete, misidentified, or uses a key layout that needs conversion." + ) + + +@ModelLoaderRegistry.register(base=BaseModelType.Any, type=ModelType.Qwen3VLEncoder, format=ModelFormat.Checkpoint) +class Qwen3VLEncoderCheckpointLoader(ModelLoader): + """Loads a single-file Qwen3-VL encoder checkpoint (e.g. ComfyUI ``qwen3vl_4b_bf16`` / ``_fp8_scaled``). + + The checkpoint bundles the language model + visual tower but no config/tokenizer; those are pulled + from HuggingFace (``Qwen/Qwen3-VL-4B-Instruct``) with offline-cache fallback. ComfyUI 'scaled fp8' + weights are dequantized to the compute dtype on load. + """ + + DEFAULT_HF_REPO = "Qwen/Qwen3-VL-4B-Instruct" + + def _load_model( + self, + config: AnyModelConfig, + submodel_type: Optional[SubModelType] = None, + ) -> AnyModel: + if not isinstance(config, Qwen3VLEncoder_Checkpoint_Config): + raise ValueError("Only Qwen3VLEncoder_Checkpoint_Config models are supported here.") + + match submodel_type: + case SubModelType.Tokenizer: + return self._load_tokenizer() + case SubModelType.TextEncoder: + return self._load_text_encoder(config) + + raise ValueError( + f"Only Tokenizer and TextEncoder submodels are supported. " + f"Received: {submodel_type.value if submodel_type else 'None'}" + ) + + def _load_tokenizer(self) -> AnyModel: + # A partial offline cache (e.g. config present but vocab/merges missing) raises something other + # than OSError (e.g. TypeError) deep in the slow-tokenizer path, so catch broadly and re-fetch. + try: + return AutoTokenizer.from_pretrained(self.DEFAULT_HF_REPO, local_files_only=True, extra_special_tokens={}) + except Exception: + return AutoTokenizer.from_pretrained(self.DEFAULT_HF_REPO, extra_special_tokens={}) + + def _load_hf_config(self) -> Any: + try: + te_config = AutoConfig.from_pretrained(self.DEFAULT_HF_REPO, local_files_only=True) + except Exception: + te_config = AutoConfig.from_pretrained(self.DEFAULT_HF_REPO) + return _normalize_qwen3vl_rope_config(te_config) + + def _load_text_encoder(self, config: Qwen3VLEncoder_Checkpoint_Config) -> AnyModel: + import torch + from safetensors.torch import load_file + from transformers import Qwen3VLModel + + model_path = Path(config.path) + target_device = TorchDevice.choose_torch_device() + model_dtype = TorchDevice.choose_bfloat16_safe_dtype(target_device) + + sd = load_file(str(model_path)) + # Detect an fp8 source (ComfyUI 'scaled fp8' weight_scale keys, or raw float8 weights) BEFORE + # dequantizing. An fp8-on-disk encoder is kept fp8-resident with layerwise upcasting below, so + # it occupies ~half the VRAM of the dequantized bf16 model (the whole point of shipping fp8). + source_is_fp8 = any(isinstance(k, str) and k.endswith(".weight_scale") for k in sd) or any( + getattr(t, "dtype", None) in (torch.float8_e4m3fn, torch.float8_e5m2) for t in sd.values() + ) + # ComfyUI 'scaled fp8': fold weight_scale into the weights, then drop quantization metadata. + sd = _dequantize_scaled_fp8(sd) + for k in list(sd.keys()): + if isinstance(k, str) and (k.endswith(".comfy_quant") or "scale_input" in k): + del sd[k] + sd = _remap_qwen3vl_singlefile_keys(sd) + + te_config = self._load_hf_config() + with accelerate.init_empty_weights(): + model = Qwen3VLModel._from_config(te_config) + + new_sd_size = sum(ten.nelement() * model_dtype.itemsize for ten in sd.values()) + self._ram_cache.make_room(new_sd_size) + for k in sd.keys(): + sd[k] = sd[k].to(model_dtype) + + model.load_state_dict(sd, assign=True, strict=False) + _reject_incomplete_load(model, what="Qwen3-VL encoder checkpoint") + + # Keep an fp8 encoder running in fp8 (storage=float8_e4m3fn, per-layer upcast to the compute + # dtype during forward) on CUDA. `_should_use_fp8` deliberately excludes text encoders (and the + # config has no fp8_storage toggle), so apply the hook-based casting directly here. This roughly + # halves the encoder's resident VRAM (~8.9GB bf16 -> ~4.4GB), which avoids partial-load thrashing + # when it shares the GPU with a large transformer. + if source_is_fp8 and self._torch_device.type == "cuda": + self._apply_fp8_to_nn_module(model, storage_dtype=torch.float8_e4m3fn, compute_dtype=model_dtype) + self._logger.info( + f"FP8 layerwise casting enabled for Qwen3-VL encoder '{config.name}' " + f"(storage=float8_e4m3fn, compute={model_dtype})." + ) + + return model diff --git a/invokeai/backend/model_manager/load/model_loaders/lora.py b/invokeai/backend/model_manager/load/model_loaders/lora.py index 15dfa376179..1f764929226 100644 --- a/invokeai/backend/model_manager/load/model_loaders/lora.py +++ b/invokeai/backend/model_manager/load/model_loaders/lora.py @@ -57,6 +57,9 @@ is_state_dict_likely_in_flux_xlabs_format, lora_model_from_flux_xlabs_state_dict, ) +from invokeai.backend.patches.lora_conversions.krea2_lora_conversion_utils import ( + lora_model_from_krea2_state_dict, +) from invokeai.backend.patches.lora_conversions.peft_adapter_utils import normalize_peft_adapter_names from invokeai.backend.patches.lora_conversions.qwen_image_lora_conversion_utils import ( lora_model_from_qwen_image_state_dict, @@ -172,6 +175,10 @@ def _load_model( model = lora_model_from_z_image_state_dict(state_dict=state_dict, alpha=None) elif self._model_base == BaseModelType.QwenImage: model = lora_model_from_qwen_image_state_dict(state_dict=state_dict, alpha=None) + elif self._model_base == BaseModelType.Krea2: + # Krea-2 LoRAs use diffusers PEFT format targeting the Krea2 transformer (and optionally + # the Qwen3-VL text encoder). alpha=None → alpha=rank (common diffusers default). + model = lora_model_from_krea2_state_dict(state_dict=state_dict, alpha=None) elif self._model_base == BaseModelType.Anima: # Anima LoRAs use Kohya-style or diffusers PEFT format targeting Cosmos DiT blocks. model = lora_model_from_anima_state_dict(state_dict=state_dict, alpha=None) diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py index 64f099554a7..213ae1733d9 100644 --- a/invokeai/backend/model_manager/starter_models.py +++ b/invokeai/backend/model_manager/starter_models.py @@ -12,6 +12,7 @@ from invokeai.backend.model_manager.taxonomy import ( AnyVariant, BaseModelType, + Krea2VariantType, ModelFormat, ModelType, QwenImageVariantType, @@ -1085,6 +1086,68 @@ class StarterModelBundle(BaseModel): ) # endregion +# region Krea-2 +# Standalone Qwen3-VL text encoder used by Krea-2 (distinct from the Qwen2.5-VL encoder above). Pair +# with single-file / GGUF Krea-2 transformers, which ship only the transformer. The Qwen-Image VAE +# dependency reuses the `qwen_image_vae` starter defined in the Qwen Image region. +qwen3_vl_encoder_4b = StarterModel( + name="Qwen3-VL 4B Encoder (Diffusers)", + base=BaseModelType.Any, + source="Qwen/Qwen3-VL-4B-Instruct", + description="Qwen3-VL 4B text encoder (Qwen3VLModel) used by Krea-2, in HuggingFace folder layout " + "(includes tokenizer). Use with single-file / GGUF Krea-2 transformers. (~8GB)", + type=ModelType.Qwen3VLEncoder, + format=ModelFormat.Qwen3VLEncoder, +) + +krea2_turbo = StarterModel( + name="Krea-2 Turbo", + base=BaseModelType.Krea2, + source="krea/Krea-2-Turbo", + description="Krea-2 Turbo - distilled 12B parameter text-to-image model (8 steps, CFG disabled). " + "Full diffusers pipeline including the Qwen-Image VAE and Qwen3-VL text encoder. ~26GB", + type=ModelType.Main, + variant=Krea2VariantType.Turbo, +) + +krea2_raw = StarterModel( + name="Krea-2 Raw", + base=BaseModelType.Krea2, + source="krea/Krea-2-Raw", + description="Krea-2 Raw - undistilled 12B base model (28 steps, CFG enabled). Full diffusers pipeline " + "including the Qwen-Image VAE and Qwen3-VL text encoder. Primarily a base for finetuning / LoRA " + "training; Turbo is recommended for standard inference. ~26GB", + type=ModelType.Main, + variant=Krea2VariantType.Base, +) + +krea2_turbo_gguf_q4_k_m = StarterModel( + name="Krea-2 Turbo (Q4_K_M GGUF)", + base=BaseModelType.Krea2, + source="https://huggingface.co/vantagewithai/Krea-2-Turbo-GGUF/resolve/main/krea2_turbo-Q4_K_M.gguf", + description="Krea-2 Turbo transformer quantized to GGUF Q4_K_M for lower VRAM (~7GB transformer). " + "GGUF ships only the transformer, so the Qwen-Image VAE and Qwen3-VL encoder are installed as " + "dependencies.", + type=ModelType.Main, + format=ModelFormat.GGUFQuantized, + variant=Krea2VariantType.Turbo, + dependencies=[qwen_image_vae, qwen3_vl_encoder_4b], +) + +krea2_turbo_gguf_q8_0 = StarterModel( + name="Krea-2 Turbo (Q8_0 GGUF)", + base=BaseModelType.Krea2, + source="https://huggingface.co/vantagewithai/Krea-2-Turbo-GGUF/resolve/main/krea2_turbo-Q8_0.gguf", + description="Krea-2 Turbo transformer quantized to GGUF Q8_0 (near-full quality, ~13GB transformer). " + "GGUF ships only the transformer, so the Qwen-Image VAE and Qwen3-VL encoder are installed as " + "dependencies.", + type=ModelType.Main, + format=ModelFormat.GGUFQuantized, + variant=Krea2VariantType.Turbo, + dependencies=[qwen_image_vae, qwen3_vl_encoder_4b], +) +# endregion + # region External API GEMINI_3_IMAGE_ALLOWED_ASPECT_RATIOS = [ "1:1", @@ -1744,6 +1807,11 @@ def _gemini_3_resolution_presets( z_image_qwen3_encoder_quantized, z_image_controlnet_union, z_image_controlnet_tile, + krea2_turbo, + krea2_raw, + krea2_turbo_gguf_q4_k_m, + krea2_turbo_gguf_q8_0, + qwen3_vl_encoder_4b, gemini_flash_image, gemini_pro_image_preview, gemini_3_1_flash_image_preview, @@ -1861,6 +1929,15 @@ def _gemini_3_resolution_presets( anima_lllite_sketch, ] +krea2_bundle: list[StarterModel] = [ + qwen_image_vae, + qwen3_vl_encoder_4b, + krea2_turbo, + krea2_raw, + krea2_turbo_gguf_q4_k_m, + krea2_turbo_gguf_q8_0, +] + STARTER_BUNDLES: dict[str, StarterModelBundle] = { BaseModelType.StableDiffusion1: StarterModelBundle(name="Stable Diffusion 1.5", models=sd1_bundle), BaseModelType.StableDiffusionXL: StarterModelBundle(name="SDXL", models=sdxl_bundle), @@ -1869,6 +1946,7 @@ def _gemini_3_resolution_presets( BaseModelType.ZImage: StarterModelBundle(name="Z-Image Turbo", models=zimage_bundle), BaseModelType.QwenImage: StarterModelBundle(name="Qwen Image", models=qwen_image_bundle), BaseModelType.Anima: StarterModelBundle(name="Anima", models=anima_bundle), + BaseModelType.Krea2: StarterModelBundle(name="Krea-2", models=krea2_bundle), } assert len(STARTER_MODELS) == len({m.source for m in STARTER_MODELS}), "Duplicate starter models" diff --git a/invokeai/backend/model_manager/taxonomy.py b/invokeai/backend/model_manager/taxonomy.py index a2e4e58bdc4..a35ab1a786c 100644 --- a/invokeai/backend/model_manager/taxonomy.py +++ b/invokeai/backend/model_manager/taxonomy.py @@ -58,6 +58,8 @@ class BaseModelType(str, Enum): """Indicates the model is associated with Qwen Image Edit 2511 model architecture.""" Anima = "anima" """Indicates the model is associated with Anima model architecture (Cosmos Predict2 DiT + LLM Adapter).""" + Krea2 = "krea-2" + """Indicates the model is associated with the Krea 2 model architecture, including Krea-2-Turbo.""" Unknown = "unknown" """Indicates the model's base architecture is unknown.""" @@ -79,6 +81,7 @@ class ModelType(str, Enum): T5Encoder = "t5_encoder" Qwen3Encoder = "qwen3_encoder" QwenVLEncoder = "qwen_vl_encoder" + Qwen3VLEncoder = "qwen3_vl_encoder" SpandrelImageToImage = "spandrel_image_to_image" SigLIP = "siglip" FluxRedux = "flux_redux" @@ -155,6 +158,22 @@ class ZImageVariantType(str, Enum): """Z-Image Base - undistilled foundation model with full CFG and negative prompt support.""" +class Krea2VariantType(str, Enum): + """Krea 2 model variants.""" + + Turbo = "krea2_turbo" + """Krea-2-Turbo - distilled model optimized for 8 steps with CFG disabled (guidance_scale=0). + + NOTE: the value is ``krea2_turbo`` (not ``turbo``) to avoid colliding with + ``ZImageVariantType.Turbo`` in the variant-string adapter and frontend label maps.""" + + Base = "krea2_base" + """Krea-2-Raw - undistilled base model. Runs with more steps (~28) and CFG enabled (~4.5), + using resolution-aware timestep shifting (``is_distilled=false`` in model_index.json). + + NOTE: the value is ``krea2_base`` (not ``base``) for the same disambiguation reason as ``Turbo``.""" + + class QwenImageVariantType(str, Enum): """Qwen Image model variants.""" @@ -193,6 +212,7 @@ class ModelFormat(str, Enum): T5Encoder = "t5_encoder" Qwen3Encoder = "qwen3_encoder" QwenVLEncoder = "qwen_vl_encoder" + Qwen3VLEncoder = "qwen3_vl_encoder" BnbQuantizedLlmInt8b = "bnb_quantized_int8b" BnbQuantizednf4b = "bnb_quantized_nf4b" GGUFQuantized = "gguf_quantized" @@ -249,6 +269,7 @@ class FluxLoRAFormat(str, Enum): ZImageVariantType, QwenImageVariantType, Qwen3VariantType, + Krea2VariantType, ] variant_type_adapter = TypeAdapter[ ModelVariantType @@ -258,6 +279,7 @@ class FluxLoRAFormat(str, Enum): | ZImageVariantType | QwenImageVariantType | Qwen3VariantType + | Krea2VariantType ]( ModelVariantType | ClipVariantType @@ -266,4 +288,5 @@ class FluxLoRAFormat(str, Enum): | ZImageVariantType | QwenImageVariantType | Qwen3VariantType + | Krea2VariantType ) diff --git a/invokeai/backend/patches/lora_conversions/krea2_lora_constants.py b/invokeai/backend/patches/lora_conversions/krea2_lora_constants.py new file mode 100644 index 00000000000..07dfa12e36a --- /dev/null +++ b/invokeai/backend/patches/lora_conversions/krea2_lora_constants.py @@ -0,0 +1,8 @@ +# Krea-2 LoRA prefix constants. +# These prefixes namespace LoRA patch keys when applying them to Krea-2 models. + +# Prefix for Krea-2 transformer (Krea2Transformer2DModel) LoRA layers. +KREA2_LORA_TRANSFORMER_PREFIX = "lora_transformer-" + +# Prefix for Krea-2 Qwen3-VL text encoder LoRA layers. +KREA2_LORA_QWEN3VL_PREFIX = "lora_qwen3vl-" diff --git a/invokeai/backend/patches/lora_conversions/krea2_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/krea2_lora_conversion_utils.py new file mode 100644 index 00000000000..75d6f921406 --- /dev/null +++ b/invokeai/backend/patches/lora_conversions/krea2_lora_conversion_utils.py @@ -0,0 +1,135 @@ +"""Krea-2 LoRA conversion utilities. + +Krea-2 uses a single-stream MMDiT (``Krea2Transformer2DModel``) with a Qwen3-VL text encoder. +Published LoRAs (e.g. krea/Krea-2-LoRA-*) are diffusers PEFT format: keys like +``transformer..lora_A.weight`` / ``lora_B.weight``. The distinctive Krea-2 module is the +``text_fusion`` stage, which we use to disambiguate from Qwen-Image / Z-Image LoRAs (which otherwise +share the ``transformer.transformer_blocks.`` prefix). +""" + +from typing import Dict + +import torch + +from invokeai.backend.patches.layers.base_layer_patch import BaseLayerPatch +from invokeai.backend.patches.layers.utils import any_lora_layer_from_state_dict +from invokeai.backend.patches.lora_conversions.krea2_lora_constants import ( + KREA2_LORA_QWEN3VL_PREFIX, + KREA2_LORA_TRANSFORMER_PREFIX, +) +from invokeai.backend.patches.model_patch_raw import ModelPatchRaw + +# Module-name fragments unique to the Krea-2 transformer (text-fusion stage + timestep modulation proj). +KREA2_TRANSFORMER_SIGNATURE_KEYS = ("text_fusion", "time_mod_proj") + + +def is_state_dict_likely_krea2_lora(state_dict: dict[str | int, torch.Tensor]) -> bool: + """Checks if the provided state dict is likely a Krea-2 LoRA. + + Requires the distinctive Krea-2 ``text_fusion`` / ``time_mod_proj`` modules so it does not + false-match Qwen-Image or Z-Image LoRAs that also carry ``transformer.transformer_blocks.`` keys. + """ + str_keys = [k for k in state_dict.keys() if isinstance(k, str)] + has_krea2_module = any(any(sig in k for sig in KREA2_TRANSFORMER_SIGNATURE_KEYS) for k in str_keys) + has_lora_suffix = any( + k.endswith((".lora_A.weight", ".lora_B.weight", ".lora_down.weight", ".lora_up.weight")) for k in str_keys + ) + return has_krea2_module and has_lora_suffix + + +def lora_model_from_krea2_state_dict(state_dict: Dict[str, torch.Tensor], alpha: float | None = None) -> ModelPatchRaw: + """Convert a Krea-2 LoRA state dict (diffusers PEFT) to a ModelPatchRaw. + + Handles transformer layers and (if present) Qwen3-VL text encoder layers. ``alpha=None`` is treated + as ``alpha=rank`` internally (the common diffusers default). + """ + layers: dict[str, BaseLayerPatch] = {} + grouped_state_dict = _group_by_layer(state_dict) + + transformer_prefixes = ( + "base_model.model.transformer.", + "transformer.", + "diffusion_model.", + ) + text_encoder_prefixes = ( + "base_model.model.text_encoder.", + "text_encoder.", + ) + + for layer_key, layer_dict in grouped_state_dict.items(): + values = _get_lora_layer_values(layer_key, layer_dict, alpha) + + is_text_encoder = False + clean_key = layer_key + for prefix in text_encoder_prefixes: + if layer_key.startswith(prefix): + clean_key = layer_key[len(prefix) :] + is_text_encoder = True + break + if not is_text_encoder: + for prefix in transformer_prefixes: + if layer_key.startswith(prefix): + clean_key = layer_key[len(prefix) :] + break + + if is_text_encoder: + final_key = f"{KREA2_LORA_QWEN3VL_PREFIX}{clean_key}" + else: + final_key = f"{KREA2_LORA_TRANSFORMER_PREFIX}{clean_key}" + + layers[final_key] = any_lora_layer_from_state_dict(values) + + return ModelPatchRaw(layers=layers) + + +def _get_lora_layer_values( + layer_key: str, layer_dict: dict[str, torch.Tensor], alpha: float | None +) -> dict[str, torch.Tensor]: + """Convert PEFT (lora_A/lora_B) layer values to internal (lora_down/lora_up) format.""" + if "lora_A.weight" in layer_dict: + if "lora_B.weight" not in layer_dict: + raise ValueError( + f"Malformed Krea-2 LoRA: layer '{layer_key}' has lora_A.weight but no matching lora_B.weight. " + "The LoRA file is incomplete or corrupt." + ) + values = { + "lora_down.weight": layer_dict["lora_A.weight"], + "lora_up.weight": layer_dict["lora_B.weight"], + } + if "dora_scale" in layer_dict: + values["dora_scale"] = layer_dict["dora_scale"] + if "alpha" in layer_dict: + values["alpha"] = layer_dict["alpha"] + if alpha is not None: + values["alpha"] = torch.tensor(alpha) + return values + return layer_dict + + +def _group_by_layer(state_dict: Dict[str, torch.Tensor]) -> dict[str, dict[str, torch.Tensor]]: + """Groups state dict keys by layer path, splitting off the LoRA weight suffix.""" + known_suffixes = [ + ".lora_A.weight", + ".lora_B.weight", + ".lora_down.weight", + ".lora_up.weight", + ".dora_scale", + ".alpha", + ] + layer_dict: dict[str, dict[str, torch.Tensor]] = {} + for key in state_dict: + if not isinstance(key, str): + continue + layer_name = None + key_name = None + for suffix in known_suffixes: + if key.endswith(suffix): + layer_name = key[: -len(suffix)] + key_name = suffix[1:] + break + if layer_name is None: + parts = key.rsplit(".", maxsplit=2) + layer_name = parts[0] + key_name = ".".join(parts[1:]) + layer_dict.setdefault(layer_name, {})[key_name] = state_dict[key] + return layer_dict diff --git a/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py b/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py index 6a9959f1e87..e84d3a313d9 100644 --- a/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py +++ b/invokeai/backend/stable_diffusion/diffusion/conditioning_data.py @@ -105,6 +105,28 @@ def to(self, device: torch.device | None = None, dtype: torch.dtype | None = Non return self +@dataclass +class Krea2ConditioningInfo: + """Krea-2 text conditioning from the Qwen3-VL encoder. + + Krea-2 taps 12 decoder hidden-state layers and stacks them per token, so prompt_embeds keeps a + layer axis (the transformer's text-fusion stage consumes it). This is unique vs Z-Image (2D) and + Qwen-Image (3D). + """ + + prompt_embeds: torch.Tensor + """Stacked Qwen3-VL hidden states. Shape: (batch_size, seq_len, num_text_layers=12, hidden=2560).""" + + prompt_embeds_mask: torch.Tensor | None = None + """Attention mask for prompt_embeds. Shape: (batch_size, seq_len). 1/True for valid, 0/False for padding.""" + + def to(self, device: torch.device | None = None, dtype: torch.dtype | None = None): + self.prompt_embeds = self.prompt_embeds.to(device=device, dtype=dtype) + if self.prompt_embeds_mask is not None: + self.prompt_embeds_mask = self.prompt_embeds_mask.to(device=device) + return self + + @dataclass class AnimaConditioningInfo: """Anima text conditioning information from Qwen3 0.6B encoder + T5-XXL tokenizer. @@ -143,6 +165,7 @@ class ConditioningFieldData: | List[CogView4ConditioningInfo] | List[ZImageConditioningInfo] | List[QwenImageConditioningInfo] + | List[Krea2ConditioningInfo] | List[AnimaConditioningInfo] ) diff --git a/invokeai/frontend/web/openapi.json b/invokeai/frontend/web/openapi.json index e2801e9e39a..bffe9c86fe3 100644 --- a/invokeai/frontend/web/openapi.json +++ b/invokeai/frontend/web/openapi.json @@ -804,6 +804,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -828,6 +831,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -846,6 +852,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -924,6 +933,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -963,6 +975,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -1128,6 +1146,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -1152,6 +1173,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -1170,6 +1194,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -1248,6 +1275,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -1287,6 +1317,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -1452,6 +1488,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -1476,6 +1515,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -1494,6 +1536,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -1572,6 +1617,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -1611,6 +1659,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -1826,6 +1880,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -1850,6 +1907,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -1868,6 +1928,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -1946,6 +2009,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -1985,6 +2051,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -2224,6 +2296,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -2248,6 +2323,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -2266,6 +2344,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -2344,6 +2425,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -2383,6 +2467,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -3442,6 +3532,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -3466,6 +3559,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -3484,6 +3580,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -3562,6 +3661,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -3601,6 +3703,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -11740,6 +11848,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -11764,6 +11875,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -11782,6 +11896,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -11860,6 +11977,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -11899,6 +12019,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -12279,6 +12405,7 @@ "external", "qwen-image", "anima", + "krea-2", "unknown" ], "title": "BaseModelType", @@ -19447,7 +19574,11 @@ "anima_txt2img", "anima_img2img", "anima_inpaint", - "anima_outpaint" + "anima_outpaint", + "krea2_txt2img", + "krea2_img2img", + "krea2_inpaint", + "krea2_outpaint" ], "type": "string" }, @@ -29630,6 +29761,27 @@ { "$ref": "#/components/schemas/IterateInvocation" }, + { + "$ref": "#/components/schemas/Krea2ConditioningRebalanceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2DenoiseInvocation" + }, + { + "$ref": "#/components/schemas/Krea2LoRACollectionLoader" + }, + { + "$ref": "#/components/schemas/Krea2LoRALoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2ModelLoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2SeedVarianceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2TextEncoderInvocation" + }, { "$ref": "#/components/schemas/LaMaInfillInvocation" }, @@ -30191,6 +30343,15 @@ { "$ref": "#/components/schemas/IterateInvocationOutput" }, + { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + }, + { + "$ref": "#/components/schemas/Krea2LoRALoaderOutput" + }, + { + "$ref": "#/components/schemas/Krea2ModelLoaderOutput" + }, { "$ref": "#/components/schemas/LatentsCollectionOutput" }, @@ -37332,6 +37493,27 @@ { "$ref": "#/components/schemas/IterateInvocation" }, + { + "$ref": "#/components/schemas/Krea2ConditioningRebalanceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2DenoiseInvocation" + }, + { + "$ref": "#/components/schemas/Krea2LoRACollectionLoader" + }, + { + "$ref": "#/components/schemas/Krea2LoRALoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2ModelLoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2SeedVarianceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2TextEncoderInvocation" + }, { "$ref": "#/components/schemas/LaMaInfillInvocation" }, @@ -37850,6 +38032,15 @@ { "$ref": "#/components/schemas/IterateInvocationOutput" }, + { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + }, + { + "$ref": "#/components/schemas/Krea2LoRALoaderOutput" + }, + { + "$ref": "#/components/schemas/Krea2ModelLoaderOutput" + }, { "$ref": "#/components/schemas/LatentsCollectionOutput" }, @@ -38488,6 +38679,27 @@ { "$ref": "#/components/schemas/IterateInvocation" }, + { + "$ref": "#/components/schemas/Krea2ConditioningRebalanceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2DenoiseInvocation" + }, + { + "$ref": "#/components/schemas/Krea2LoRACollectionLoader" + }, + { + "$ref": "#/components/schemas/Krea2LoRALoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2ModelLoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2SeedVarianceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2TextEncoderInvocation" + }, { "$ref": "#/components/schemas/LaMaInfillInvocation" }, @@ -39316,6 +39528,27 @@ "iterate": { "$ref": "#/components/schemas/IterateInvocationOutput" }, + "krea2_conditioning_rebalance": { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + }, + "krea2_denoise": { + "$ref": "#/components/schemas/LatentsOutput" + }, + "krea2_lora_collection_loader": { + "$ref": "#/components/schemas/Krea2LoRALoaderOutput" + }, + "krea2_lora_loader": { + "$ref": "#/components/schemas/Krea2LoRALoaderOutput" + }, + "krea2_model_loader": { + "$ref": "#/components/schemas/Krea2ModelLoaderOutput" + }, + "krea2_seed_variance": { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + }, + "krea2_text_encoder": { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + }, "l2i": { "$ref": "#/components/schemas/ImageOutput" }, @@ -39818,6 +40051,13 @@ "invokeai_img_val_thresholds", "ip_adapter", "iterate", + "krea2_conditioning_rebalance", + "krea2_denoise", + "krea2_lora_collection_loader", + "krea2_lora_loader", + "krea2_model_loader", + "krea2_seed_variance", + "krea2_text_encoder", "l2i", "latents", "latents_collection", @@ -40420,6 +40660,27 @@ { "$ref": "#/components/schemas/IterateInvocation" }, + { + "$ref": "#/components/schemas/Krea2ConditioningRebalanceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2DenoiseInvocation" + }, + { + "$ref": "#/components/schemas/Krea2LoRACollectionLoader" + }, + { + "$ref": "#/components/schemas/Krea2LoRALoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2ModelLoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2SeedVarianceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2TextEncoderInvocation" + }, { "$ref": "#/components/schemas/LaMaInfillInvocation" }, @@ -41322,6 +41583,27 @@ { "$ref": "#/components/schemas/IterateInvocation" }, + { + "$ref": "#/components/schemas/Krea2ConditioningRebalanceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2DenoiseInvocation" + }, + { + "$ref": "#/components/schemas/Krea2LoRACollectionLoader" + }, + { + "$ref": "#/components/schemas/Krea2LoRALoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2ModelLoaderInvocation" + }, + { + "$ref": "#/components/schemas/Krea2SeedVarianceInvocation" + }, + { + "$ref": "#/components/schemas/Krea2TextEncoderInvocation" + }, { "$ref": "#/components/schemas/LaMaInfillInvocation" }, @@ -43562,11 +43844,971 @@ "type": "object" }, "JsonValue": {}, - "LaMaInfillInvocation": { - "category": "inpaint", + "Krea2ConditioningField": { + "description": "A Krea-2 conditioning tensor primitive value", + "properties": { + "conditioning_name": { + "description": "The name of conditioning tensor", + "title": "Conditioning Name", + "type": "string" + } + }, + "required": ["conditioning_name"], + "title": "Krea2ConditioningField", + "type": "object" + }, + "Krea2ConditioningOutput": { + "class": "output", + "description": "Base class for nodes that output a Krea-2 conditioning tensor.", + "properties": { + "conditioning": { + "$ref": "#/components/schemas/Krea2ConditioningField", + "description": "Conditioning tensor", + "field_kind": "output", + "ui_hidden": false + }, + "type": { + "const": "krea2_conditioning_output", + "default": "krea2_conditioning_output", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["output_meta", "conditioning", "type", "type"], + "title": "Krea2ConditioningOutput", + "type": "object" + }, + "Krea2ConditioningRebalanceInvocation": { + "category": "conditioning", "class": "invocation", - "classification": "stable", - "description": "Infills transparent areas of an image using the LaMa model", + "classification": "prototype", + "description": "Per-layer rebalancing of Krea-2 text conditioning (improves prompt adherence).\n\nKrea-2 conditioning stacks 12 Qwen3-VL hidden-state layers per token. Weighting those layers\nindividually (and applying an overall multiplier) lets you push the model harder toward the prompt,\ncounteracting the quality-dilution from distillation. Ported from the ComfyUI\n`ConditioningKrea2Rebalance` node. This is an optional pass between the text encoder and denoise.", + "node_pack": "invokeai", + "properties": { + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "conditioning": { + "anyOf": [ + { + "$ref": "#/components/schemas/Krea2ConditioningField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Conditioning tensor", + "field_kind": "input", + "input": "connection", + "orig_required": true, + "title": "Conditioning" + }, + "per_layer_weights": { + "default": "1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0", + "description": "Comma-separated gains for the 12 tapped encoder layers (exactly 12 values).", + "field_kind": "input", + "input": "any", + "orig_default": "1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0", + "orig_required": false, + "title": "Per Layer Weights", + "type": "string" + }, + "multiplier": { + "default": 4.0, + "description": "Overall multiplier applied to the conditioning after per-layer weighting.", + "field_kind": "input", + "input": "any", + "orig_default": 4.0, + "orig_required": false, + "title": "Multiplier", + "type": "number" + }, + "type": { + "const": "krea2_conditioning_rebalance", + "default": "krea2_conditioning_rebalance", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["conditioning", "krea2", "krea-2"], + "title": "Conditioning Rebalance - Krea-2", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + } + }, + "Krea2DenoiseInvocation": { + "category": "image", + "class": "invocation", + "classification": "prototype", + "description": "Run the denoising process with a Krea-2 model.", + "node_pack": "invokeai", + "properties": { + "board": { + "anyOf": [ + { + "$ref": "#/components/schemas/BoardField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The board to save the image to", + "field_kind": "internal", + "input": "direct", + "orig_required": false, + "ui_hidden": false + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/MetadataField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional metadata to be saved with the image", + "field_kind": "internal", + "input": "connection", + "orig_required": false, + "ui_hidden": false + }, + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "latents": { + "anyOf": [ + { + "$ref": "#/components/schemas/LatentsField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Latents tensor", + "field_kind": "input", + "input": "connection", + "orig_default": null, + "orig_required": false + }, + "denoise_mask": { + "anyOf": [ + { + "$ref": "#/components/schemas/DenoiseMaskField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved.", + "field_kind": "input", + "input": "connection", + "orig_default": null, + "orig_required": false + }, + "denoising_start": { + "default": 0.0, + "description": "When to start denoising, expressed a percentage of total steps", + "field_kind": "input", + "input": "any", + "maximum": 1, + "minimum": 0, + "orig_default": 0.0, + "orig_required": false, + "title": "Denoising Start", + "type": "number" + }, + "denoising_end": { + "default": 1.0, + "description": "When to stop denoising, expressed a percentage of total steps", + "field_kind": "input", + "input": "any", + "maximum": 1, + "minimum": 0, + "orig_default": 1.0, + "orig_required": false, + "title": "Denoising End", + "type": "number" + }, + "transformer": { + "anyOf": [ + { + "$ref": "#/components/schemas/TransformerField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Krea-2 model (Transformer) to load", + "field_kind": "input", + "input": "connection", + "orig_required": true, + "title": "Transformer" + }, + "positive_conditioning": { + "anyOf": [ + { + "$ref": "#/components/schemas/Krea2ConditioningField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Positive conditioning tensor", + "field_kind": "input", + "input": "connection", + "orig_required": true + }, + "negative_conditioning": { + "anyOf": [ + { + "$ref": "#/components/schemas/Krea2ConditioningField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Negative conditioning tensor", + "field_kind": "input", + "input": "connection", + "orig_default": null, + "orig_required": false + }, + "cfg_scale": { + "anyOf": [ + { + "type": "number" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ], + "default": 1.0, + "description": "Classifier-Free Guidance scale", + "field_kind": "input", + "input": "any", + "orig_default": 1.0, + "orig_required": false, + "title": "CFG Scale" + }, + "width": { + "default": 1024, + "description": "Width of the generated image.", + "exclusiveMinimum": 0, + "field_kind": "input", + "input": "any", + "multipleOf": 16, + "orig_default": 1024, + "orig_required": false, + "title": "Width", + "type": "integer" + }, + "height": { + "default": 1024, + "description": "Height of the generated image.", + "exclusiveMinimum": 0, + "field_kind": "input", + "input": "any", + "multipleOf": 16, + "orig_default": 1024, + "orig_required": false, + "title": "Height", + "type": "integer" + }, + "steps": { + "default": 8, + "description": "Number of steps to run", + "exclusiveMinimum": 0, + "field_kind": "input", + "input": "any", + "orig_default": 8, + "orig_required": false, + "title": "Steps", + "type": "integer" + }, + "seed": { + "default": 0, + "description": "Randomness seed for reproducibility.", + "field_kind": "input", + "input": "any", + "orig_default": 0, + "orig_required": false, + "title": "Seed", + "type": "integer" + }, + "shift": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Override the resolution-aware timestep shift (mu). Leave unset to use the model default (mu=1.15 for the distilled Turbo checkpoint).", + "field_kind": "input", + "input": "any", + "orig_default": null, + "orig_required": false, + "title": "Shift" + }, + "type": { + "const": "krea2_denoise", + "default": "krea2_denoise", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["image", "krea2", "krea-2"], + "title": "Denoise - Krea-2", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/LatentsOutput" + } + }, + "Krea2LoRACollectionLoader": { + "category": "model", + "class": "invocation", + "classification": "stable", + "description": "Applies a collection of LoRAs to a Krea-2 transformer and/or Qwen3-VL encoder.", + "node_pack": "invokeai", + "properties": { + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "loras": { + "anyOf": [ + { + "$ref": "#/components/schemas/LoRAField" + }, + { + "items": { + "$ref": "#/components/schemas/LoRAField" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "LoRA models and weights. May be a single LoRA or collection.", + "field_kind": "input", + "input": "any", + "orig_default": null, + "orig_required": false, + "title": "LoRAs" + }, + "transformer": { + "anyOf": [ + { + "$ref": "#/components/schemas/TransformerField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Transformer", + "field_kind": "input", + "input": "connection", + "orig_default": null, + "orig_required": false, + "title": "Transformer" + }, + "qwen3_vl_encoder": { + "anyOf": [ + { + "$ref": "#/components/schemas/Qwen3VLEncoderField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Qwen3-VL tokenizer and text encoder", + "field_kind": "input", + "input": "connection", + "orig_default": null, + "orig_required": false, + "title": "Qwen3-VL Encoder" + }, + "type": { + "const": "krea2_lora_collection_loader", + "default": "krea2_lora_collection_loader", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["lora", "model", "krea2", "krea-2"], + "title": "Apply LoRA Collection - Krea-2", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/Krea2LoRALoaderOutput" + } + }, + "Krea2LoRALoaderInvocation": { + "category": "model", + "class": "invocation", + "classification": "stable", + "description": "Apply a LoRA model to a Krea-2 transformer and/or Qwen3-VL text encoder.", + "node_pack": "invokeai", + "properties": { + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "lora": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelIdentifierField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "LoRA model to load", + "field_kind": "input", + "input": "any", + "orig_required": true, + "title": "LoRA", + "ui_model_base": ["krea-2"], + "ui_model_type": ["lora"] + }, + "weight": { + "default": 0.75, + "description": "The weight at which the LoRA is applied to each model", + "field_kind": "input", + "input": "any", + "orig_default": 0.75, + "orig_required": false, + "title": "Weight", + "type": "number" + }, + "transformer": { + "anyOf": [ + { + "$ref": "#/components/schemas/TransformerField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Transformer", + "field_kind": "input", + "input": "connection", + "orig_default": null, + "orig_required": false, + "title": "Krea-2 Transformer" + }, + "qwen3_vl_encoder": { + "anyOf": [ + { + "$ref": "#/components/schemas/Qwen3VLEncoderField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Qwen3-VL tokenizer and text encoder", + "field_kind": "input", + "input": "connection", + "orig_default": null, + "orig_required": false, + "title": "Qwen3-VL Encoder" + }, + "type": { + "const": "krea2_lora_loader", + "default": "krea2_lora_loader", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["lora", "model", "krea2", "krea-2"], + "title": "Apply LoRA - Krea-2", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/Krea2LoRALoaderOutput" + } + }, + "Krea2LoRALoaderOutput": { + "class": "output", + "description": "Krea-2 LoRA Loader Output", + "properties": { + "transformer": { + "anyOf": [ + { + "$ref": "#/components/schemas/TransformerField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Transformer", + "field_kind": "output", + "title": "Krea-2 Transformer", + "ui_hidden": false + }, + "qwen3_vl_encoder": { + "anyOf": [ + { + "$ref": "#/components/schemas/Qwen3VLEncoderField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Qwen3-VL tokenizer and text encoder", + "field_kind": "output", + "title": "Qwen3-VL Encoder", + "ui_hidden": false + }, + "type": { + "const": "krea2_lora_loader_output", + "default": "krea2_lora_loader_output", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["output_meta", "transformer", "qwen3_vl_encoder", "type", "type"], + "title": "Krea2LoRALoaderOutput", + "type": "object" + }, + "Krea2ModelLoaderInvocation": { + "category": "model", + "class": "invocation", + "classification": "prototype", + "description": "Loads a Krea-2 model, outputting its submodels.\n\nBy default the VAE (Qwen-Image VAE) and Qwen3-VL text encoder are extracted from the Krea-2\ndiffusers pipeline. Standalone overrides may be supplied (e.g. when the transformer is a\nsingle-file checkpoint that has no bundled VAE / encoder).", + "node_pack": "invokeai", + "properties": { + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "model": { + "$ref": "#/components/schemas/ModelIdentifierField", + "description": "Krea-2 model (Transformer) to load", + "field_kind": "input", + "input": "direct", + "orig_required": true, + "title": "Transformer", + "ui_model_base": ["krea-2"], + "ui_model_type": ["main"] + }, + "vae_model": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelIdentifierField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Standalone VAE model. Krea-2 uses the Qwen-Image VAE (16-channel). If not provided, the VAE is loaded from the Krea-2 (diffusers) model.", + "field_kind": "input", + "input": "direct", + "orig_default": null, + "orig_required": false, + "title": "VAE", + "ui_model_base": ["qwen-image", "anima"], + "ui_model_type": ["vae"] + }, + "qwen3_vl_encoder_model": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelIdentifierField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Standalone Qwen3-VL Encoder model. If not provided, the encoder is loaded from the Krea-2 (diffusers) model.", + "field_kind": "input", + "input": "direct", + "orig_default": null, + "orig_required": false, + "title": "Qwen3-VL Encoder", + "ui_model_type": ["qwen3_vl_encoder"] + }, + "type": { + "const": "krea2_model_loader", + "default": "krea2_model_loader", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["model", "type", "id"], + "tags": ["model", "krea2", "krea-2"], + "title": "Main Model - Krea-2", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/Krea2ModelLoaderOutput" + } + }, + "Krea2ModelLoaderOutput": { + "class": "output", + "description": "Krea-2 base model loader output.", + "properties": { + "transformer": { + "$ref": "#/components/schemas/TransformerField", + "description": "Transformer", + "field_kind": "output", + "title": "Transformer", + "ui_hidden": false + }, + "qwen3_vl_encoder": { + "$ref": "#/components/schemas/Qwen3VLEncoderField", + "description": "Qwen3-VL tokenizer and text encoder", + "field_kind": "output", + "title": "Qwen3-VL Encoder", + "ui_hidden": false + }, + "vae": { + "$ref": "#/components/schemas/VAEField", + "description": "VAE", + "field_kind": "output", + "title": "VAE", + "ui_hidden": false + }, + "type": { + "const": "krea2_model_loader_output", + "default": "krea2_model_loader_output", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["output_meta", "transformer", "qwen3_vl_encoder", "vae", "type", "type"], + "title": "Krea2ModelLoaderOutput", + "type": "object" + }, + "Krea2SeedVarianceInvocation": { + "category": "conditioning", + "class": "invocation", + "classification": "prototype", + "description": "Inject per-seed diversity into Krea-2 text conditioning.\n\nDistilled few-step models (like Krea-2-Turbo) suffer from low seed variance \u2014 different seeds give\nnear-identical images. This adds seeded uniform noise to a random subset of the text-embedding\nvalues, trading some prompt adherence for variety (the same idea as the Z-Image-Turbo\n`SeedVarianceEnhancer`). Optional pass between the text encoder and denoise; the defaults are\naggressive and may need tuning for Krea-2.", + "node_pack": "invokeai", + "properties": { + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "conditioning": { + "anyOf": [ + { + "$ref": "#/components/schemas/Krea2ConditioningField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Conditioning tensor", + "field_kind": "input", + "input": "connection", + "orig_required": true, + "title": "Conditioning" + }, + "strength": { + "default": 20.0, + "description": "Magnitude of the uniform noise added to the embeddings (noise in [-strength, +strength]).", + "field_kind": "input", + "input": "any", + "orig_default": 20.0, + "orig_required": false, + "title": "Strength", + "type": "number" + }, + "randomize_percent": { + "default": 50.0, + "description": "Percentage of embedding values that get perturbed (Bernoulli mask).", + "field_kind": "input", + "input": "any", + "maximum": 100.0, + "minimum": 1.0, + "orig_default": 50.0, + "orig_required": false, + "title": "Randomize Percent", + "type": "number" + }, + "variance_seed": { + "default": 0, + "description": "Seed for the variance noise (vary this to get variety).", + "field_kind": "input", + "input": "any", + "orig_default": 0, + "orig_required": false, + "title": "Variance Seed", + "type": "integer" + }, + "type": { + "const": "krea2_seed_variance", + "default": "krea2_seed_variance", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["conditioning", "krea2", "krea-2", "variance"], + "title": "Seed Variance - Krea-2", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + } + }, + "Krea2TextEncoderInvocation": { + "category": "conditioning", + "class": "invocation", + "classification": "prototype", + "description": "Encodes a text prompt for Krea-2 using the Qwen3-VL text encoder.\n\nThe encoder taps 12 decoder hidden-state layers and stacks them per token, producing a 4D\nconditioning tensor (B, seq, 12, hidden) that the Krea-2 transformer's text-fusion stage consumes.", + "node_pack": "invokeai", + "properties": { + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Text prompt describing the desired image.", + "field_kind": "input", + "input": "any", + "orig_required": true, + "title": "Prompt", + "ui_component": "textarea" + }, + "qwen3_vl_encoder": { + "anyOf": [ + { + "$ref": "#/components/schemas/Qwen3VLEncoderField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Qwen3-VL tokenizer and text encoder", + "field_kind": "input", + "input": "connection", + "orig_required": true, + "title": "Qwen3-VL Encoder" + }, + "type": { + "const": "krea2_text_encoder", + "default": "krea2_text_encoder", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["prompt", "conditioning", "krea2", "krea-2"], + "title": "Prompt - Krea-2", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/Krea2ConditioningOutput" + } + }, + "Krea2VariantType": { + "type": "string", + "enum": ["krea2_turbo", "krea2_base"], + "title": "Krea2VariantType", + "description": "Krea 2 model variants." + }, + "LaMaInfillInvocation": { + "category": "inpaint", + "class": "invocation", + "classification": "stable", + "description": "Infills transparent areas of an image using the LaMa model", "node_pack": "invokeai", "properties": { "board": { @@ -45200,19 +46442,323 @@ }, "base": { "type": "string", - "const": "flux2", + "const": "flux2", + "title": "Base", + "default": "flux2" + }, + "variant": { + "anyOf": [ + { + "$ref": "#/components/schemas/Flux2VariantType" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "type", + "trigger_phrases", + "default_settings", + "format", + "base", + "variant" + ], + "title": "LoRA_Diffusers_Flux2_Config", + "description": "Model config for FLUX.2 (Klein) LoRA models in Diffusers format." + }, + "LoRA_Diffusers_SD1_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Model description" + }, + "source": { + "type": "string", + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "type": { + "type": "string", + "const": "lora", + "title": "Type", + "default": "lora" + }, + "trigger_phrases": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "uniqueItems": true + }, + { + "type": "null" + } + ], + "title": "Trigger Phrases", + "description": "Set of trigger phrases for this model" + }, + "default_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/LoraModelDefaultSettings" + }, + { + "type": "null" + } + ], + "description": "Default settings for this model" + }, + "format": { + "type": "string", + "const": "diffusers", + "title": "Format", + "default": "diffusers" + }, + "base": { + "type": "string", + "const": "sd-1", + "title": "Base", + "default": "sd-1" + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "type", + "trigger_phrases", + "default_settings", + "format", + "base" + ], + "title": "LoRA_Diffusers_SD1_Config" + }, + "LoRA_Diffusers_SD2_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Model description" + }, + "source": { + "type": "string", + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "type": { + "type": "string", + "const": "lora", + "title": "Type", + "default": "lora" + }, + "trigger_phrases": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "uniqueItems": true + }, + { + "type": "null" + } + ], + "title": "Trigger Phrases", + "description": "Set of trigger phrases for this model" + }, + "default_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/LoraModelDefaultSettings" + }, + { + "type": "null" + } + ], + "description": "Default settings for this model" + }, + "format": { + "type": "string", + "const": "diffusers", + "title": "Format", + "default": "diffusers" + }, + "base": { + "type": "string", + "const": "sd-2", "title": "Base", - "default": "flux2" - }, - "variant": { - "anyOf": [ - { - "$ref": "#/components/schemas/Flux2VariantType" - }, - { - "type": "null" - } - ] + "default": "sd-2" } }, "type": "object", @@ -45232,13 +46778,11 @@ "trigger_phrases", "default_settings", "format", - "base", - "variant" + "base" ], - "title": "LoRA_Diffusers_Flux2_Config", - "description": "Model config for FLUX.2 (Klein) LoRA models in Diffusers format." + "title": "LoRA_Diffusers_SD2_Config" }, - "LoRA_Diffusers_SD1_Config": { + "LoRA_Diffusers_SDXL_Config": { "properties": { "key": { "type": "string", @@ -45363,9 +46907,9 @@ }, "base": { "type": "string", - "const": "sd-1", + "const": "sdxl", "title": "Base", - "default": "sd-1" + "default": "sdxl" } }, "type": "object", @@ -45387,9 +46931,9 @@ "format", "base" ], - "title": "LoRA_Diffusers_SD1_Config" + "title": "LoRA_Diffusers_SDXL_Config" }, - "LoRA_Diffusers_SD2_Config": { + "LoRA_Diffusers_ZImage_Config": { "properties": { "key": { "type": "string", @@ -45514,160 +47058,19 @@ }, "base": { "type": "string", - "const": "sd-2", + "const": "z-image", "title": "Base", - "default": "sd-2" - } - }, - "type": "object", - "required": [ - "key", - "hash", - "path", - "file_size", - "name", - "description", - "source", - "source_type", - "source_api_response", - "source_url", - "cover_image", - "type", - "trigger_phrases", - "default_settings", - "format", - "base" - ], - "title": "LoRA_Diffusers_SD2_Config" - }, - "LoRA_Diffusers_SDXL_Config": { - "properties": { - "key": { - "type": "string", - "title": "Key", - "description": "A unique key for this model." - }, - "hash": { - "type": "string", - "title": "Hash", - "description": "The hash of the model file(s)." - }, - "path": { - "type": "string", - "title": "Path", - "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." - }, - "file_size": { - "type": "integer", - "title": "File Size", - "description": "The size of the model in bytes." - }, - "name": { - "type": "string", - "title": "Name", - "description": "Name of the model." - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Description", - "description": "Model description" - }, - "source": { - "type": "string", - "title": "Source", - "description": "The original source of the model (path, URL or repo_id)." - }, - "source_type": { - "$ref": "#/components/schemas/ModelSourceType", - "description": "The type of source" - }, - "source_api_response": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Source Api Response", - "description": "The original API response from the source, as stringified JSON." - }, - "source_url": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Source Url", - "description": "Optional URL for the model (e.g. download page or model page)." - }, - "cover_image": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Cover Image", - "description": "Url for image to preview model" - }, - "type": { - "type": "string", - "const": "lora", - "title": "Type", - "default": "lora" - }, - "trigger_phrases": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array", - "uniqueItems": true - }, - { - "type": "null" - } - ], - "title": "Trigger Phrases", - "description": "Set of trigger phrases for this model" + "default": "z-image" }, - "default_settings": { + "variant": { "anyOf": [ { - "$ref": "#/components/schemas/LoraModelDefaultSettings" + "$ref": "#/components/schemas/ZImageVariantType" }, { "type": "null" } - ], - "description": "Default settings for this model" - }, - "format": { - "type": "string", - "const": "diffusers", - "title": "Format", - "default": "diffusers" - }, - "base": { - "type": "string", - "const": "sdxl", - "title": "Base", - "default": "sdxl" + ] } }, "type": "object", @@ -45687,11 +47090,13 @@ "trigger_phrases", "default_settings", "format", - "base" + "base", + "variant" ], - "title": "LoRA_Diffusers_SDXL_Config" + "title": "LoRA_Diffusers_ZImage_Config", + "description": "Model config for Z-Image LoRA models in Diffusers format." }, - "LoRA_Diffusers_ZImage_Config": { + "LoRA_LyCORIS_Anima_Config": { "properties": { "key": { "type": "string", @@ -45810,25 +47215,15 @@ }, "format": { "type": "string", - "const": "diffusers", + "const": "lycoris", "title": "Format", - "default": "diffusers" + "default": "lycoris" }, "base": { "type": "string", - "const": "z-image", + "const": "anima", "title": "Base", - "default": "z-image" - }, - "variant": { - "anyOf": [ - { - "$ref": "#/components/schemas/ZImageVariantType" - }, - { - "type": "null" - } - ] + "default": "anima" } }, "type": "object", @@ -45848,13 +47243,12 @@ "trigger_phrases", "default_settings", "format", - "base", - "variant" + "base" ], - "title": "LoRA_Diffusers_ZImage_Config", - "description": "Model config for Z-Image LoRA models in Diffusers format." + "title": "LoRA_LyCORIS_Anima_Config", + "description": "Model config for Anima LoRA models in LyCORIS format." }, - "LoRA_LyCORIS_Anima_Config": { + "LoRA_LyCORIS_FLUX_Config": { "properties": { "key": { "type": "string", @@ -45979,9 +47373,9 @@ }, "base": { "type": "string", - "const": "anima", + "const": "flux", "title": "Base", - "default": "anima" + "default": "flux" } }, "type": "object", @@ -46003,10 +47397,9 @@ "format", "base" ], - "title": "LoRA_LyCORIS_Anima_Config", - "description": "Model config for Anima LoRA models in LyCORIS format." + "title": "LoRA_LyCORIS_FLUX_Config" }, - "LoRA_LyCORIS_FLUX_Config": { + "LoRA_LyCORIS_Flux2_Config": { "properties": { "key": { "type": "string", @@ -46131,9 +47524,19 @@ }, "base": { "type": "string", - "const": "flux", + "const": "flux2", "title": "Base", - "default": "flux" + "default": "flux2" + }, + "variant": { + "anyOf": [ + { + "$ref": "#/components/schemas/Flux2VariantType" + }, + { + "type": "null" + } + ] } }, "type": "object", @@ -46153,11 +47556,13 @@ "trigger_phrases", "default_settings", "format", - "base" + "base", + "variant" ], - "title": "LoRA_LyCORIS_FLUX_Config" + "title": "LoRA_LyCORIS_Flux2_Config", + "description": "Model config for FLUX.2 (Klein) LoRA models in LyCORIS format." }, - "LoRA_LyCORIS_Flux2_Config": { + "LoRA_LyCORIS_Krea2_Config": { "properties": { "key": { "type": "string", @@ -46282,19 +47687,9 @@ }, "base": { "type": "string", - "const": "flux2", + "const": "krea-2", "title": "Base", - "default": "flux2" - }, - "variant": { - "anyOf": [ - { - "$ref": "#/components/schemas/Flux2VariantType" - }, - { - "type": "null" - } - ] + "default": "krea-2" } }, "type": "object", @@ -46314,11 +47709,10 @@ "trigger_phrases", "default_settings", "format", - "base", - "variant" + "base" ], - "title": "LoRA_LyCORIS_Flux2_Config", - "description": "Model config for FLUX.2 (Klein) LoRA models in LyCORIS format." + "title": "LoRA_LyCORIS_Krea2_Config", + "description": "Model config for Krea-2 LoRA models in LyCORIS (single-file diffusers PEFT) format." }, "LoRA_LyCORIS_QwenImage_Config": { "properties": { @@ -48133,20 +49527,523 @@ "title": "Config Path", "description": "Path to the config for this model, if any." }, - "base": { - "type": "string", - "const": "flux", - "title": "Base", - "default": "flux" - }, + "base": { + "type": "string", + "const": "flux", + "title": "Base", + "default": "flux" + }, + "format": { + "type": "string", + "const": "bnb_quantized_nf4b", + "title": "Format", + "default": "bnb_quantized_nf4b" + }, + "variant": { + "$ref": "#/components/schemas/FluxVariantType" + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "type", + "trigger_phrases", + "default_settings", + "config_path", + "base", + "format", + "variant" + ], + "title": "Main_BnBNF4_FLUX_Config", + "description": "Model config for main checkpoint models." + }, + "Main_Checkpoint_Anima_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Model description" + }, + "source": { + "type": "string", + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "type": { + "type": "string", + "const": "main", + "title": "Type", + "default": "main" + }, + "trigger_phrases": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "uniqueItems": true + }, + { + "type": "null" + } + ], + "title": "Trigger Phrases", + "description": "Set of trigger phrases for this model" + }, + "default_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/MainModelDefaultSettings" + }, + { + "type": "null" + } + ], + "description": "Default settings for this model" + }, + "config_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Config Path", + "description": "Path to the config for this model, if any." + }, + "base": { + "type": "string", + "const": "anima", + "title": "Base", + "default": "anima" + }, + "format": { + "type": "string", + "const": "checkpoint", + "title": "Format", + "default": "checkpoint" + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "type", + "trigger_phrases", + "default_settings", + "config_path", + "base", + "format" + ], + "title": "Main_Checkpoint_Anima_Config", + "description": "Model config for Anima single-file checkpoint models (safetensors).\n\nAnima is built on NVIDIA Cosmos Predict2 DiT with a custom LLM Adapter\nthat bridges Qwen3 0.6B text encoder outputs to the DiT." + }, + "Main_Checkpoint_FLUX_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Model description" + }, + "source": { + "type": "string", + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "type": { + "type": "string", + "const": "main", + "title": "Type", + "default": "main" + }, + "trigger_phrases": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "uniqueItems": true + }, + { + "type": "null" + } + ], + "title": "Trigger Phrases", + "description": "Set of trigger phrases for this model" + }, + "default_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/MainModelDefaultSettings" + }, + { + "type": "null" + } + ], + "description": "Default settings for this model" + }, + "config_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Config Path", + "description": "Path to the config for this model, if any." + }, + "format": { + "type": "string", + "const": "checkpoint", + "title": "Format", + "default": "checkpoint" + }, + "base": { + "type": "string", + "const": "flux", + "title": "Base", + "default": "flux" + }, + "variant": { + "$ref": "#/components/schemas/FluxVariantType" + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "type", + "trigger_phrases", + "default_settings", + "config_path", + "format", + "base", + "variant" + ], + "title": "Main_Checkpoint_FLUX_Config", + "description": "Model config for main checkpoint models." + }, + "Main_Checkpoint_Flux2_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Model description" + }, + "source": { + "type": "string", + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "type": { + "type": "string", + "const": "main", + "title": "Type", + "default": "main" + }, + "trigger_phrases": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "uniqueItems": true + }, + { + "type": "null" + } + ], + "title": "Trigger Phrases", + "description": "Set of trigger phrases for this model" + }, + "default_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/MainModelDefaultSettings" + }, + { + "type": "null" + } + ], + "description": "Default settings for this model" + }, + "config_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Config Path", + "description": "Path to the config for this model, if any." + }, "format": { "type": "string", - "const": "bnb_quantized_nf4b", + "const": "checkpoint", "title": "Format", - "default": "bnb_quantized_nf4b" + "default": "checkpoint" + }, + "base": { + "type": "string", + "const": "flux2", + "title": "Base", + "default": "flux2" }, "variant": { - "$ref": "#/components/schemas/FluxVariantType" + "$ref": "#/components/schemas/Flux2VariantType" } }, "type": "object", @@ -48166,14 +50063,14 @@ "trigger_phrases", "default_settings", "config_path", - "base", "format", + "base", "variant" ], - "title": "Main_BnBNF4_FLUX_Config", - "description": "Model config for main checkpoint models." + "title": "Main_Checkpoint_Flux2_Config", + "description": "Model config for FLUX.2 checkpoint models (e.g. Klein)." }, - "Main_Checkpoint_Anima_Config": { + "Main_Checkpoint_Krea2_Config": { "properties": { "key": { "type": "string", @@ -48304,15 +50201,18 @@ }, "base": { "type": "string", - "const": "anima", + "const": "krea-2", "title": "Base", - "default": "anima" + "default": "krea-2" }, "format": { "type": "string", "const": "checkpoint", "title": "Format", "default": "checkpoint" + }, + "variant": { + "$ref": "#/components/schemas/Krea2VariantType" } }, "type": "object", @@ -48333,12 +50233,13 @@ "default_settings", "config_path", "base", - "format" + "format", + "variant" ], - "title": "Main_Checkpoint_Anima_Config", - "description": "Model config for Anima single-file checkpoint models (safetensors).\n\nAnima is built on NVIDIA Cosmos Predict2 DiT with a custom LLM Adapter\nthat bridges Qwen3 0.6B text encoder outputs to the DiT." + "title": "Main_Checkpoint_Krea2_Config", + "description": "Model config for Krea-2 single-file checkpoint models (safetensors, etc)." }, - "Main_Checkpoint_FLUX_Config": { + "Main_Checkpoint_QwenImage_Config": { "properties": { "key": { "type": "string", @@ -48467,189 +50368,27 @@ "title": "Config Path", "description": "Path to the config for this model, if any." }, + "base": { + "type": "string", + "const": "qwen-image", + "title": "Base", + "default": "qwen-image" + }, "format": { "type": "string", "const": "checkpoint", "title": "Format", "default": "checkpoint" }, - "base": { - "type": "string", - "const": "flux", - "title": "Base", - "default": "flux" - }, "variant": { - "$ref": "#/components/schemas/FluxVariantType" - } - }, - "type": "object", - "required": [ - "key", - "hash", - "path", - "file_size", - "name", - "description", - "source", - "source_type", - "source_api_response", - "source_url", - "cover_image", - "type", - "trigger_phrases", - "default_settings", - "config_path", - "format", - "base", - "variant" - ], - "title": "Main_Checkpoint_FLUX_Config", - "description": "Model config for main checkpoint models." - }, - "Main_Checkpoint_Flux2_Config": { - "properties": { - "key": { - "type": "string", - "title": "Key", - "description": "A unique key for this model." - }, - "hash": { - "type": "string", - "title": "Hash", - "description": "The hash of the model file(s)." - }, - "path": { - "type": "string", - "title": "Path", - "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." - }, - "file_size": { - "type": "integer", - "title": "File Size", - "description": "The size of the model in bytes." - }, - "name": { - "type": "string", - "title": "Name", - "description": "Name of the model." - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Description", - "description": "Model description" - }, - "source": { - "type": "string", - "title": "Source", - "description": "The original source of the model (path, URL or repo_id)." - }, - "source_type": { - "$ref": "#/components/schemas/ModelSourceType", - "description": "The type of source" - }, - "source_api_response": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Source Api Response", - "description": "The original API response from the source, as stringified JSON." - }, - "source_url": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Source Url", - "description": "Optional URL for the model (e.g. download page or model page)." - }, - "cover_image": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Cover Image", - "description": "Url for image to preview model" - }, - "type": { - "type": "string", - "const": "main", - "title": "Type", - "default": "main" - }, - "trigger_phrases": { "anyOf": [ { - "items": { - "type": "string" - }, - "type": "array", - "uniqueItems": true - }, - { - "type": "null" - } - ], - "title": "Trigger Phrases", - "description": "Set of trigger phrases for this model" - }, - "default_settings": { - "anyOf": [ - { - "$ref": "#/components/schemas/MainModelDefaultSettings" - }, - { - "type": "null" - } - ], - "description": "Default settings for this model" - }, - "config_path": { - "anyOf": [ - { - "type": "string" + "$ref": "#/components/schemas/QwenImageVariantType" }, { "type": "null" } - ], - "title": "Config Path", - "description": "Path to the config for this model, if any." - }, - "format": { - "type": "string", - "const": "checkpoint", - "title": "Format", - "default": "checkpoint" - }, - "base": { - "type": "string", - "const": "flux2", - "title": "Base", - "default": "flux2" - }, - "variant": { - "$ref": "#/components/schemas/Flux2VariantType" + ] } }, "type": "object", @@ -48669,14 +50408,14 @@ "trigger_phrases", "default_settings", "config_path", - "format", "base", + "format", "variant" ], - "title": "Main_Checkpoint_Flux2_Config", - "description": "Model config for FLUX.2 checkpoint models (e.g. Klein)." + "title": "Main_Checkpoint_QwenImage_Config", + "description": "Model config for Qwen Image single-file checkpoint models (safetensors, etc).\n\nCovers both raw bf16/fp16 checkpoints and ComfyUI-style fp8_scaled checkpoints.\nThe loader dequantizes fp8 weights back to bf16 at load time; the\n`default_settings.fp8_storage` toggle can then optionally re-cast to fp8 for\nVRAM savings." }, - "Main_Checkpoint_QwenImage_Config": { + "Main_Checkpoint_SD1_Config": { "properties": { "key": { "type": "string", @@ -48805,27 +50544,23 @@ "title": "Config Path", "description": "Path to the config for this model, if any." }, - "base": { - "type": "string", - "const": "qwen-image", - "title": "Base", - "default": "qwen-image" - }, "format": { "type": "string", "const": "checkpoint", "title": "Format", "default": "checkpoint" }, + "prediction_type": { + "$ref": "#/components/schemas/SchedulerPredictionType" + }, "variant": { - "anyOf": [ - { - "$ref": "#/components/schemas/QwenImageVariantType" - }, - { - "type": "null" - } - ] + "$ref": "#/components/schemas/ModelVariantType" + }, + "base": { + "type": "string", + "const": "sd-1", + "title": "Base", + "default": "sd-1" } }, "type": "object", @@ -48845,14 +50580,14 @@ "trigger_phrases", "default_settings", "config_path", - "base", "format", - "variant" + "prediction_type", + "variant", + "base" ], - "title": "Main_Checkpoint_QwenImage_Config", - "description": "Model config for Qwen Image single-file checkpoint models (safetensors, etc).\n\nCovers both raw bf16/fp16 checkpoints and ComfyUI-style fp8_scaled checkpoints.\nThe loader dequantizes fp8 weights back to bf16 at load time; the\n`default_settings.fp8_storage` toggle can then optionally re-cast to fp8 for\nVRAM savings." + "title": "Main_Checkpoint_SD1_Config" }, - "Main_Checkpoint_SD1_Config": { + "Main_Checkpoint_SD2_Config": { "properties": { "key": { "type": "string", @@ -48995,9 +50730,9 @@ }, "base": { "type": "string", - "const": "sd-1", + "const": "sd-2", "title": "Base", - "default": "sd-1" + "default": "sd-2" } }, "type": "object", @@ -49022,9 +50757,9 @@ "variant", "base" ], - "title": "Main_Checkpoint_SD1_Config" + "title": "Main_Checkpoint_SD2_Config" }, - "Main_Checkpoint_SD2_Config": { + "Main_Checkpoint_SDXLRefiner_Config": { "properties": { "key": { "type": "string", @@ -49167,9 +50902,9 @@ }, "base": { "type": "string", - "const": "sd-2", + "const": "sdxl-refiner", "title": "Base", - "default": "sd-2" + "default": "sdxl-refiner" } }, "type": "object", @@ -49194,9 +50929,9 @@ "variant", "base" ], - "title": "Main_Checkpoint_SD2_Config" + "title": "Main_Checkpoint_SDXLRefiner_Config" }, - "Main_Checkpoint_SDXLRefiner_Config": { + "Main_Checkpoint_SDXL_Config": { "properties": { "key": { "type": "string", @@ -49339,9 +51074,9 @@ }, "base": { "type": "string", - "const": "sdxl-refiner", + "const": "sdxl", "title": "Base", - "default": "sdxl-refiner" + "default": "sdxl" } }, "type": "object", @@ -49366,9 +51101,9 @@ "variant", "base" ], - "title": "Main_Checkpoint_SDXLRefiner_Config" + "title": "Main_Checkpoint_SDXL_Config" }, - "Main_Checkpoint_SDXL_Config": { + "Main_Checkpoint_ZImage_Config": { "properties": { "key": { "type": "string", @@ -49497,23 +51232,20 @@ "title": "Config Path", "description": "Path to the config for this model, if any." }, + "base": { + "type": "string", + "const": "z-image", + "title": "Base", + "default": "z-image" + }, "format": { "type": "string", "const": "checkpoint", "title": "Format", "default": "checkpoint" }, - "prediction_type": { - "$ref": "#/components/schemas/SchedulerPredictionType" - }, "variant": { - "$ref": "#/components/schemas/ModelVariantType" - }, - "base": { - "type": "string", - "const": "sdxl", - "title": "Base", - "default": "sdxl" + "$ref": "#/components/schemas/ZImageVariantType" } }, "type": "object", @@ -49533,14 +51265,14 @@ "trigger_phrases", "default_settings", "config_path", + "base", "format", - "prediction_type", - "variant", - "base" + "variant" ], - "title": "Main_Checkpoint_SDXL_Config" + "title": "Main_Checkpoint_ZImage_Config", + "description": "Model config for Z-Image single-file checkpoint models (safetensors, etc)." }, - "Main_Checkpoint_ZImage_Config": { + "Main_Diffusers_CogView4_Config": { "properties": { "key": { "type": "string", @@ -49657,7 +51389,73 @@ ], "description": "Default settings for this model" }, - "config_path": { + "format": { + "type": "string", + "const": "diffusers", + "title": "Format", + "default": "diffusers" + }, + "repo_variant": { + "$ref": "#/components/schemas/ModelRepoVariant", + "default": "" + }, + "base": { + "type": "string", + "const": "cogview4", + "title": "Base", + "default": "cogview4" + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "type", + "trigger_phrases", + "default_settings", + "format", + "repo_variant", + "base" + ], + "title": "Main_Diffusers_CogView4_Config" + }, + "Main_Diffusers_FLUX_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { "anyOf": [ { "type": "string" @@ -49666,23 +51464,105 @@ "type": "null" } ], - "title": "Config Path", - "description": "Path to the config for this model, if any." + "title": "Description", + "description": "Model description" }, - "base": { + "source": { "type": "string", - "const": "z-image", - "title": "Base", - "default": "z-image" + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "type": { + "type": "string", + "const": "main", + "title": "Type", + "default": "main" + }, + "trigger_phrases": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array", + "uniqueItems": true + }, + { + "type": "null" + } + ], + "title": "Trigger Phrases", + "description": "Set of trigger phrases for this model" + }, + "default_settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/MainModelDefaultSettings" + }, + { + "type": "null" + } + ], + "description": "Default settings for this model" }, "format": { "type": "string", - "const": "checkpoint", + "const": "diffusers", "title": "Format", - "default": "checkpoint" + "default": "diffusers" + }, + "repo_variant": { + "$ref": "#/components/schemas/ModelRepoVariant", + "default": "" + }, + "base": { + "type": "string", + "const": "flux", + "title": "Base", + "default": "flux" }, "variant": { - "$ref": "#/components/schemas/ZImageVariantType" + "$ref": "#/components/schemas/FluxVariantType" } }, "type": "object", @@ -49701,15 +51581,15 @@ "type", "trigger_phrases", "default_settings", - "config_path", - "base", "format", + "repo_variant", + "base", "variant" ], - "title": "Main_Checkpoint_ZImage_Config", - "description": "Model config for Z-Image single-file checkpoint models (safetensors, etc)." + "title": "Main_Diffusers_FLUX_Config", + "description": "Model config for FLUX.1 models in diffusers format." }, - "Main_Diffusers_CogView4_Config": { + "Main_Diffusers_Flux2_Config": { "properties": { "key": { "type": "string", @@ -49838,9 +51718,12 @@ }, "base": { "type": "string", - "const": "cogview4", + "const": "flux2", "title": "Base", - "default": "cogview4" + "default": "flux2" + }, + "variant": { + "$ref": "#/components/schemas/Flux2VariantType" } }, "type": "object", @@ -49861,11 +51744,13 @@ "default_settings", "format", "repo_variant", - "base" + "base", + "variant" ], - "title": "Main_Diffusers_CogView4_Config" + "title": "Main_Diffusers_Flux2_Config", + "description": "Model config for FLUX.2 models in diffusers format (e.g. FLUX.2 Klein)." }, - "Main_Diffusers_FLUX_Config": { + "Main_Diffusers_Krea2_Config": { "properties": { "key": { "type": "string", @@ -49994,12 +51879,12 @@ }, "base": { "type": "string", - "const": "flux", + "const": "krea-2", "title": "Base", - "default": "flux" + "default": "krea-2" }, "variant": { - "$ref": "#/components/schemas/FluxVariantType" + "$ref": "#/components/schemas/Krea2VariantType" } }, "type": "object", @@ -50023,10 +51908,10 @@ "base", "variant" ], - "title": "Main_Diffusers_FLUX_Config", - "description": "Model config for FLUX.1 models in diffusers format." + "title": "Main_Diffusers_Krea2_Config", + "description": "Model config for Krea-2 diffusers models (Krea-2-Turbo)." }, - "Main_Diffusers_Flux2_Config": { + "Main_Diffusers_QwenImage_Config": { "properties": { "key": { "type": "string", @@ -50155,12 +52040,19 @@ }, "base": { "type": "string", - "const": "flux2", + "const": "qwen-image", "title": "Base", - "default": "flux2" + "default": "qwen-image" }, "variant": { - "$ref": "#/components/schemas/Flux2VariantType" + "anyOf": [ + { + "$ref": "#/components/schemas/QwenImageVariantType" + }, + { + "type": "null" + } + ] } }, "type": "object", @@ -50184,10 +52076,10 @@ "base", "variant" ], - "title": "Main_Diffusers_Flux2_Config", - "description": "Model config for FLUX.2 models in diffusers format (e.g. FLUX.2 Klein)." + "title": "Main_Diffusers_QwenImage_Config", + "description": "Model config for Qwen Image diffusers models (both txt2img and edit)." }, - "Main_Diffusers_QwenImage_Config": { + "Main_Diffusers_SD1_Config": { "properties": { "key": { "type": "string", @@ -50314,21 +52206,17 @@ "$ref": "#/components/schemas/ModelRepoVariant", "default": "" }, + "prediction_type": { + "$ref": "#/components/schemas/SchedulerPredictionType" + }, + "variant": { + "$ref": "#/components/schemas/ModelVariantType" + }, "base": { "type": "string", - "const": "qwen-image", + "const": "sd-1", "title": "Base", - "default": "qwen-image" - }, - "variant": { - "anyOf": [ - { - "$ref": "#/components/schemas/QwenImageVariantType" - }, - { - "type": "null" - } - ] + "default": "sd-1" } }, "type": "object", @@ -50349,13 +52237,13 @@ "default_settings", "format", "repo_variant", - "base", - "variant" + "prediction_type", + "variant", + "base" ], - "title": "Main_Diffusers_QwenImage_Config", - "description": "Model config for Qwen Image diffusers models (both txt2img and edit)." + "title": "Main_Diffusers_SD1_Config" }, - "Main_Diffusers_SD1_Config": { + "Main_Diffusers_SD2_Config": { "properties": { "key": { "type": "string", @@ -50490,9 +52378,9 @@ }, "base": { "type": "string", - "const": "sd-1", + "const": "sd-2", "title": "Base", - "default": "sd-1" + "default": "sd-2" } }, "type": "object", @@ -50517,9 +52405,9 @@ "variant", "base" ], - "title": "Main_Diffusers_SD1_Config" + "title": "Main_Diffusers_SD2_Config" }, - "Main_Diffusers_SD2_Config": { + "Main_Diffusers_SD3_Config": { "properties": { "key": { "type": "string", @@ -50646,17 +52534,29 @@ "$ref": "#/components/schemas/ModelRepoVariant", "default": "" }, - "prediction_type": { - "$ref": "#/components/schemas/SchedulerPredictionType" - }, - "variant": { - "$ref": "#/components/schemas/ModelVariantType" - }, "base": { "type": "string", - "const": "sd-2", + "const": "sd-3", "title": "Base", - "default": "sd-2" + "default": "sd-3" + }, + "submodels": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/components/schemas/SubmodelDefinition" + }, + "propertyNames": { + "$ref": "#/components/schemas/SubModelType" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Submodels", + "description": "Loadable submodels in this model" } }, "type": "object", @@ -50677,13 +52577,12 @@ "default_settings", "format", "repo_variant", - "prediction_type", - "variant", - "base" + "base", + "submodels" ], - "title": "Main_Diffusers_SD2_Config" + "title": "Main_Diffusers_SD3_Config" }, - "Main_Diffusers_SD3_Config": { + "Main_Diffusers_SDXLRefiner_Config": { "properties": { "key": { "type": "string", @@ -50810,29 +52709,17 @@ "$ref": "#/components/schemas/ModelRepoVariant", "default": "" }, + "prediction_type": { + "$ref": "#/components/schemas/SchedulerPredictionType" + }, + "variant": { + "$ref": "#/components/schemas/ModelVariantType" + }, "base": { "type": "string", - "const": "sd-3", + "const": "sdxl-refiner", "title": "Base", - "default": "sd-3" - }, - "submodels": { - "anyOf": [ - { - "additionalProperties": { - "$ref": "#/components/schemas/SubmodelDefinition" - }, - "propertyNames": { - "$ref": "#/components/schemas/SubModelType" - }, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Submodels", - "description": "Loadable submodels in this model" + "default": "sdxl-refiner" } }, "type": "object", @@ -50853,12 +52740,13 @@ "default_settings", "format", "repo_variant", - "base", - "submodels" + "prediction_type", + "variant", + "base" ], - "title": "Main_Diffusers_SD3_Config" + "title": "Main_Diffusers_SDXLRefiner_Config" }, - "Main_Diffusers_SDXLRefiner_Config": { + "Main_Diffusers_SDXL_Config": { "properties": { "key": { "type": "string", @@ -50993,9 +52881,9 @@ }, "base": { "type": "string", - "const": "sdxl-refiner", + "const": "sdxl", "title": "Base", - "default": "sdxl-refiner" + "default": "sdxl" } }, "type": "object", @@ -51020,9 +52908,9 @@ "variant", "base" ], - "title": "Main_Diffusers_SDXLRefiner_Config" + "title": "Main_Diffusers_SDXL_Config" }, - "Main_Diffusers_SDXL_Config": { + "Main_Diffusers_ZImage_Config": { "properties": { "key": { "type": "string", @@ -51149,17 +53037,14 @@ "$ref": "#/components/schemas/ModelRepoVariant", "default": "" }, - "prediction_type": { - "$ref": "#/components/schemas/SchedulerPredictionType" - }, - "variant": { - "$ref": "#/components/schemas/ModelVariantType" - }, "base": { "type": "string", - "const": "sdxl", + "const": "z-image", "title": "Base", - "default": "sdxl" + "default": "z-image" + }, + "variant": { + "$ref": "#/components/schemas/ZImageVariantType" } }, "type": "object", @@ -51180,13 +53065,13 @@ "default_settings", "format", "repo_variant", - "prediction_type", - "variant", - "base" + "base", + "variant" ], - "title": "Main_Diffusers_SDXL_Config" + "title": "Main_Diffusers_ZImage_Config", + "description": "Model config for Z-Image diffusers models (Z-Image-Turbo, Z-Image-Base)." }, - "Main_Diffusers_ZImage_Config": { + "Main_GGUF_FLUX_Config": { "properties": { "key": { "type": "string", @@ -51303,24 +53188,32 @@ ], "description": "Default settings for this model" }, - "format": { - "type": "string", - "const": "diffusers", - "title": "Format", - "default": "diffusers" - }, - "repo_variant": { - "$ref": "#/components/schemas/ModelRepoVariant", - "default": "" + "config_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Config Path", + "description": "Path to the config for this model, if any." }, "base": { "type": "string", - "const": "z-image", + "const": "flux", "title": "Base", - "default": "z-image" + "default": "flux" + }, + "format": { + "type": "string", + "const": "gguf_quantized", + "title": "Format", + "default": "gguf_quantized" }, "variant": { - "$ref": "#/components/schemas/ZImageVariantType" + "$ref": "#/components/schemas/FluxVariantType" } }, "type": "object", @@ -51339,15 +53232,15 @@ "type", "trigger_phrases", "default_settings", - "format", - "repo_variant", + "config_path", "base", + "format", "variant" ], - "title": "Main_Diffusers_ZImage_Config", - "description": "Model config for Z-Image diffusers models (Z-Image-Turbo, Z-Image-Base)." + "title": "Main_GGUF_FLUX_Config", + "description": "Model config for main checkpoint models." }, - "Main_GGUF_FLUX_Config": { + "Main_GGUF_Flux2_Config": { "properties": { "key": { "type": "string", @@ -51478,9 +53371,9 @@ }, "base": { "type": "string", - "const": "flux", + "const": "flux2", "title": "Base", - "default": "flux" + "default": "flux2" }, "format": { "type": "string", @@ -51489,7 +53382,7 @@ "default": "gguf_quantized" }, "variant": { - "$ref": "#/components/schemas/FluxVariantType" + "$ref": "#/components/schemas/Flux2VariantType" } }, "type": "object", @@ -51513,10 +53406,10 @@ "format", "variant" ], - "title": "Main_GGUF_FLUX_Config", - "description": "Model config for main checkpoint models." + "title": "Main_GGUF_Flux2_Config", + "description": "Model config for GGUF-quantized FLUX.2 checkpoint models (e.g. Klein)." }, - "Main_GGUF_Flux2_Config": { + "Main_GGUF_Krea2_Config": { "properties": { "key": { "type": "string", @@ -51647,9 +53540,9 @@ }, "base": { "type": "string", - "const": "flux2", + "const": "krea-2", "title": "Base", - "default": "flux2" + "default": "krea-2" }, "format": { "type": "string", @@ -51658,7 +53551,7 @@ "default": "gguf_quantized" }, "variant": { - "$ref": "#/components/schemas/Flux2VariantType" + "$ref": "#/components/schemas/Krea2VariantType" } }, "type": "object", @@ -51682,8 +53575,8 @@ "format", "variant" ], - "title": "Main_GGUF_Flux2_Config", - "description": "Model config for GGUF-quantized FLUX.2 checkpoint models (e.g. Klein)." + "title": "Main_GGUF_Krea2_Config", + "description": "Model config for GGUF-quantized Krea-2 transformer models (single-file)." }, "Main_GGUF_QwenImage_Config": { "properties": { @@ -55541,6 +57434,7 @@ "t5_encoder", "qwen3_encoder", "qwen_vl_encoder", + "qwen3_vl_encoder", "bnb_quantized_int8b", "bnb_quantized_nf4b", "gguf_quantized", @@ -55814,6 +57708,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -55838,6 +57735,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -55856,6 +57756,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -55934,6 +57837,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -55973,6 +57879,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -56389,6 +58301,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -56413,6 +58328,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -56431,6 +58349,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -56509,6 +58430,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -56548,6 +58472,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -56849,6 +58779,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -56873,6 +58806,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -56891,6 +58827,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -56969,6 +58908,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -57008,6 +58950,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -57159,6 +59107,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -57183,6 +59134,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -57201,6 +59155,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -57279,6 +59236,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -57318,6 +59278,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -57727,6 +59693,9 @@ { "$ref": "#/components/schemas/Qwen3VariantType" }, + { + "$ref": "#/components/schemas/Krea2VariantType" + }, { "type": "null" } @@ -57866,6 +59835,7 @@ "t5_encoder", "qwen3_encoder", "qwen_vl_encoder", + "qwen3_vl_encoder", "spandrel_image_to_image", "siglip", "flux_redux", @@ -57918,6 +59888,9 @@ { "$ref": "#/components/schemas/Main_Diffusers_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Diffusers_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_SD1_Config" }, @@ -57942,6 +59915,9 @@ { "$ref": "#/components/schemas/Main_Checkpoint_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_Checkpoint_Krea2_Config" + }, { "$ref": "#/components/schemas/Main_Checkpoint_Anima_Config" }, @@ -57960,6 +59936,9 @@ { "$ref": "#/components/schemas/Main_GGUF_ZImage_Config" }, + { + "$ref": "#/components/schemas/Main_GGUF_Krea2_Config" + }, { "$ref": "#/components/schemas/VAE_Checkpoint_SD1_Config" }, @@ -58038,6 +60017,9 @@ { "$ref": "#/components/schemas/LoRA_LyCORIS_ZImage_Config" }, + { + "$ref": "#/components/schemas/LoRA_LyCORIS_Krea2_Config" + }, { "$ref": "#/components/schemas/LoRA_LyCORIS_QwenImage_Config" }, @@ -58077,6 +60059,12 @@ { "$ref": "#/components/schemas/T5Encoder_BnBLLMint8_Config" }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Checkpoint_Config" + }, + { + "$ref": "#/components/schemas/Qwen3VLEncoder_Qwen3VLEncoder_Config" + }, { "$ref": "#/components/schemas/Qwen3Encoder_Qwen3Encoder_Config" }, @@ -60781,6 +62769,315 @@ "title": "Qwen3Encoder_Qwen3Encoder_Config", "description": "Configuration for Qwen3 Encoder models in a diffusers-like format.\n\nThe model weights are expected to be in a folder called text_encoder inside the model directory,\ncompatible with Qwen2VLForConditionalGeneration or similar architectures used by Z-Image." }, + "Qwen3VLEncoderField": { + "description": "Field for the Qwen3-VL text encoder used by Krea-2 models.", + "properties": { + "tokenizer": { + "$ref": "#/components/schemas/ModelIdentifierField", + "description": "Info to load tokenizer submodel" + }, + "text_encoder": { + "$ref": "#/components/schemas/ModelIdentifierField", + "description": "Info to load text_encoder submodel" + }, + "loras": { + "description": "LoRAs to apply on model loading", + "items": { + "$ref": "#/components/schemas/LoRAField" + }, + "title": "Loras", + "type": "array" + } + }, + "required": ["tokenizer", "text_encoder"], + "title": "Qwen3VLEncoderField", + "type": "object" + }, + "Qwen3VLEncoder_Checkpoint_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Model description" + }, + "source": { + "type": "string", + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "config_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Config Path", + "description": "Path to the config for this model, if any." + }, + "base": { + "type": "string", + "const": "any", + "title": "Base", + "default": "any" + }, + "type": { + "type": "string", + "const": "qwen3_vl_encoder", + "title": "Type", + "default": "qwen3_vl_encoder" + }, + "format": { + "type": "string", + "const": "checkpoint", + "title": "Format", + "default": "checkpoint" + }, + "cpu_only": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cpu Only", + "description": "Whether this model should run on CPU only" + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "config_path", + "base", + "type", + "format", + "cpu_only" + ], + "title": "Qwen3VLEncoder_Checkpoint_Config", + "description": "Configuration for a single-file Qwen3-VL text encoder checkpoint (e.g. ComfyUI ``qwen3vl_4b_*``).\n\nDistinguished from the text-only ``Qwen3Encoder`` checkpoint (Z-Image) by the presence of the\nQwen3-VL visual tower. The tokenizer is not bundled in single-file checkpoints and is pulled from\nHuggingFace (``Qwen/Qwen3-VL-4B-Instruct``) by the loader." + }, + "Qwen3VLEncoder_Qwen3VLEncoder_Config": { + "properties": { + "key": { + "type": "string", + "title": "Key", + "description": "A unique key for this model." + }, + "hash": { + "type": "string", + "title": "Hash", + "description": "The hash of the model file(s)." + }, + "path": { + "type": "string", + "title": "Path", + "description": "Path to the model on the filesystem. Relative paths are relative to the Invoke root directory." + }, + "file_size": { + "type": "integer", + "title": "File Size", + "description": "The size of the model in bytes." + }, + "name": { + "type": "string", + "title": "Name", + "description": "Name of the model." + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Model description" + }, + "source": { + "type": "string", + "title": "Source", + "description": "The original source of the model (path, URL or repo_id)." + }, + "source_type": { + "$ref": "#/components/schemas/ModelSourceType", + "description": "The type of source" + }, + "source_api_response": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Api Response", + "description": "The original API response from the source, as stringified JSON." + }, + "source_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Url", + "description": "Optional URL for the model (e.g. download page or model page)." + }, + "cover_image": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Cover Image", + "description": "Url for image to preview model" + }, + "base": { + "type": "string", + "const": "any", + "title": "Base", + "default": "any" + }, + "type": { + "type": "string", + "const": "qwen3_vl_encoder", + "title": "Type", + "default": "qwen3_vl_encoder" + }, + "format": { + "type": "string", + "const": "qwen3_vl_encoder", + "title": "Format", + "default": "qwen3_vl_encoder" + }, + "cpu_only": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Cpu Only", + "description": "Whether this model should run on CPU only" + } + }, + "type": "object", + "required": [ + "key", + "hash", + "path", + "file_size", + "name", + "description", + "source", + "source_type", + "source_api_response", + "source_url", + "cover_image", + "base", + "type", + "format", + "cpu_only" + ], + "title": "Qwen3VLEncoder_Qwen3VLEncoder_Config", + "description": "Configuration for standalone Qwen3-VL text encoder models (diffusers-like directory format).\n\nUsed by Krea-2, whose text conditioning comes from a Qwen3-VL model (``Qwen3VLModel``). The model\nweights are expected either in a ``text_encoder`` subfolder of the model directory or directly at the\nroot (standalone download). This is distinct from the text-only ``Qwen3Encoder`` (Z-Image / FLUX.2\nKlein) and the Qwen2.5-VL ``QwenVLEncoder`` (Qwen Image)." + }, "Qwen3VariantType": { "type": "string", "enum": ["qwen3_4b", "qwen3_8b", "qwen3_06b"], @@ -67343,6 +69640,9 @@ { "$ref": "#/components/schemas/Qwen3VariantType" }, + { + "$ref": "#/components/schemas/Krea2VariantType" + }, { "type": "null" } @@ -67503,6 +69803,9 @@ { "$ref": "#/components/schemas/Qwen3VariantType" }, + { + "$ref": "#/components/schemas/Krea2VariantType" + }, { "type": "null" } @@ -68435,6 +70738,9 @@ { "$ref": "#/components/schemas/Qwen3VariantType" }, + { + "$ref": "#/components/schemas/Krea2VariantType" + }, { "type": "null" } diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 050af54fcf1..95aa80a5d10 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1036,6 +1036,10 @@ "imageDetails": "Image Details", "imageDimensions": "Image Dimensions", "imageSize": "Image Size", + "krea2Qwen3VlEncoder": "Qwen3-VL Encoder", + "krea2RebalanceEnabled": "Conditioning Rebalance Enabled", + "krea2RebalanceMultiplier": "Rebalance Multiplier", + "krea2RebalanceWeights": "Rebalance Per-Layer Weights", "metadata": "Metadata", "model": "Model", "negativePrompt": "Negative Prompt", @@ -1357,6 +1361,7 @@ "t5Encoder": "T5 Encoder", "qwen3Encoder": "Qwen3 Encoder", "qwenVLEncoder": "Qwen2.5-VL Encoder", + "qwen3VLEncoder": "Qwen3-VL Encoder", "animaVae": "VAE", "animaVaePlaceholder": "Select Anima-compatible VAE", "animaQwen3Encoder": "Qwen3 0.6B Encoder", @@ -1371,6 +1376,10 @@ "zImageQwen3EncoderPlaceholder": "From Qwen3 source model", "zImageQwen3Source": "Qwen3 & VAE Source Model", "zImageQwen3SourcePlaceholder": "Required if VAE/Encoder empty", + "krea2Vae": "VAE (optional)", + "krea2VaePlaceholder": "From Krea-2 diffusers model", + "krea2Qwen3VlEncoder": "Qwen3-VL Encoder (optional)", + "krea2Qwen3VlEncoderPlaceholder": "From Krea-2 diffusers model", "flux2KleinVae": "VAE (optional)", "flux2KleinVaePlaceholder": "From diffusers model", "flux2KleinVaeNoModelPlaceholder": "No diffusers model available", @@ -1696,6 +1705,8 @@ "noQwenImageComponentSourceSelected": "GGUF Qwen Image models require a Diffusers Component Source for VAE/encoder", "noZImageVaeSourceSelected": "No VAE source: Select VAE (FLUX) or Qwen3 Source model", "noZImageQwen3EncoderSourceSelected": "No Qwen3 Encoder source: Select Qwen3 Encoder or Qwen3 Source model", + "noKrea2VaeModelSelected": "Non-diffusers Krea-2: select a VAE in Advanced settings", + "noKrea2Qwen3VlEncoderModelSelected": "Non-diffusers Krea-2: select a Qwen3-VL Encoder in Advanced settings", "noAnimaVaeModelSelected": "No Anima VAE model selected", "noAnimaQwen3EncoderModelSelected": "No Anima Qwen3 Encoder model selected", "fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), bbox width is {{width}}", @@ -1742,6 +1753,10 @@ "seedVarianceEnabled": "Seed Variance Enhancer", "seedVarianceStrength": "Variance Strength", "seedVarianceRandomizePercent": "Randomize Percent", + "krea2RebalanceEnabled": "Conditioning Rebalance", + "krea2RebalanceMultiplier": "Rebalance Multiplier", + "krea2RebalanceWeights": "Per-Layer Weights (12)", + "krea2RebalanceWeightsPlaceholder": "1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0", "imageActions": "Image Actions", "sendToCanvas": "Send To Canvas", "sendToUpscale": "Send To Upscale", @@ -2090,6 +2105,48 @@ "Lower values create more selective noise patterns, while 100% affects all values equally." ] }, + "krea2ConditioningRebalance": { + "heading": "Conditioning Rebalance", + "paragraphs": [ + "Krea-2 stacks 12 text-encoder layers per token. This reweights those layers and applies an overall multiplier to push the model harder toward your prompt, countering the quality dilution of the distilled Turbo model.", + "Enable this to improve prompt adherence when the model ignores parts of your prompt." + ] + }, + "krea2RebalanceMultiplier": { + "heading": "Rebalance Multiplier", + "paragraphs": [ + "Overall gain applied to the conditioning after per-layer weighting. Higher values push harder toward the prompt.", + "Default is 4.0. Very high values may over-saturate or distort the output." + ] + }, + "krea2RebalanceWeights": { + "heading": "Per-Layer Weights", + "paragraphs": [ + "Comma-separated gains for the 12 tapped encoder layers (exactly 12 values). Later layers carry more semantic detail.", + "The default emphasizes specific layers (e.g. 2.5, 5, 4) found to improve prompt adherence." + ] + }, + "krea2SeedVarianceEnhancer": { + "heading": "Seed Variance Enhancer", + "paragraphs": [ + "Distilled few-step models like Krea-2-Turbo can produce nearly identical images across different seeds. This adds seed-based noise to the text embeddings to increase variation while staying reproducible.", + "Enable this to get more diverse results when exploring seeds, at some cost to prompt adherence." + ] + }, + "krea2SeedVarianceStrength": { + "heading": "Variance Strength", + "paragraphs": [ + "Magnitude of the uniform noise added to the embeddings.", + "Higher values give more variety but can drift further from the prompt. Default is 20." + ] + }, + "krea2SeedVarianceRandomizePercent": { + "heading": "Randomize Percent", + "paragraphs": [ + "Percentage of embedding values that receive noise (1-100%).", + "Lower values create more selective perturbations, while 100% affects all values." + ] + }, "compositingMaskBlur": { "heading": "Mask Blur", "paragraphs": ["The blur radius of the mask."] diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/krea2ComponentSync.test.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/krea2ComponentSync.test.ts new file mode 100644 index 00000000000..a759d57ce78 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/krea2ComponentSync.test.ts @@ -0,0 +1,98 @@ +import { describe, expect, it } from 'vitest'; + +import { getKrea2ComponentUpdates } from './krea2ComponentSync'; + +const vae = { key: 'vae', hash: 'h-vae', name: 'VAE', base: 'qwen-image', type: 'vae' } as const; +const animaVae = { key: 'anima-vae', hash: 'h-anima', name: 'Anima VAE', base: 'anima', type: 'vae' } as const; +const encoder = { + key: 'encoder', + hash: 'h-encoder', + name: 'Encoder', + base: 'any', + type: 'qwen3_vl_encoder', +} as const; + +describe('getKrea2ComponentUpdates', () => { + it('clears stale standalone components for a Diffusers model', () => { + expect( + getKrea2ComponentUpdates({ + format: 'diffusers', + selectedVae: vae, + selectedEncoder: encoder, + availableQwenImageVaes: [vae], + availableAnimaVaes: [animaVae], + availableEncoders: [encoder], + }) + ).toEqual({ vae: null, encoder: null }); + }); + + it('selects installed standalone components for a non-Diffusers model', () => { + expect( + getKrea2ComponentUpdates({ + format: 'gguf_quantized', + selectedVae: null, + selectedEncoder: null, + availableQwenImageVaes: [vae], + availableAnimaVaes: [animaVae], + availableEncoders: [encoder], + }) + ).toEqual({ vae, encoder }); + }); + + it('falls back to an Anima VAE and preserves explicit standalone selections', () => { + expect( + getKrea2ComponentUpdates({ + format: 'checkpoint', + selectedVae: animaVae, + selectedEncoder: encoder, + availableQwenImageVaes: [], + availableAnimaVaes: [animaVae], + availableEncoders: [encoder], + }) + ).toEqual({}); + }); + + it('replaces stale standalone selections with installed compatible components', () => { + const staleVae = { ...vae, key: 'deleted-vae' }; + const staleEncoder = { ...encoder, key: 'deleted-encoder' }; + + expect( + getKrea2ComponentUpdates({ + format: 'checkpoint', + selectedVae: staleVae, + selectedEncoder: staleEncoder, + availableQwenImageVaes: [vae], + availableAnimaVaes: [], + availableEncoders: [encoder], + }) + ).toEqual({ vae, encoder }); + }); + + it('clears stale standalone selections when no compatible replacement is installed', () => { + expect( + getKrea2ComponentUpdates({ + format: 'checkpoint', + selectedVae: { ...vae, key: 'deleted-vae' }, + selectedEncoder: { ...encoder, key: 'deleted-encoder' }, + availableQwenImageVaes: [], + availableAnimaVaes: [], + availableEncoders: [], + }) + ).toEqual({ vae: null, encoder: null }); + }); + + it('replaces an installed but incompatible VAE selection', () => { + const incompatibleVae = { ...vae, key: 'sdxl-vae', base: 'sdxl' as const }; + + expect( + getKrea2ComponentUpdates({ + format: 'checkpoint', + selectedVae: incompatibleVae, + selectedEncoder: encoder, + availableQwenImageVaes: [vae], + availableAnimaVaes: [], + availableEncoders: [encoder], + }) + ).toEqual({ vae }); + }); +}); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/krea2ComponentSync.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/krea2ComponentSync.ts new file mode 100644 index 00000000000..6ee342c7490 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/krea2ComponentSync.ts @@ -0,0 +1,42 @@ +import type { ModelIdentifierField } from 'features/nodes/types/common'; + +type Krea2ComponentSyncArg = { + format: string; + selectedVae: ModelIdentifierField | null; + selectedEncoder: ModelIdentifierField | null; + availableQwenImageVaes: ModelIdentifierField[]; + availableAnimaVaes: ModelIdentifierField[]; + availableEncoders: ModelIdentifierField[]; +}; + +type Krea2ComponentUpdates = { + vae?: ModelIdentifierField | null; + encoder?: ModelIdentifierField | null; +}; + +export const getKrea2ComponentUpdates = (arg: Krea2ComponentSyncArg): Krea2ComponentUpdates => { + const { format, selectedVae, selectedEncoder, availableQwenImageVaes, availableAnimaVaes, availableEncoders } = arg; + + if (format === 'diffusers') { + return { + ...(selectedVae ? { vae: null } : {}), + ...(selectedEncoder ? { encoder: null } : {}), + }; + } + + const defaultVae = availableQwenImageVaes[0] ?? availableAnimaVaes[0]; + const defaultEncoder = availableEncoders[0]; + const availableVaes = [...availableQwenImageVaes, ...availableAnimaVaes]; + const hasSelectedVae = selectedVae !== null && selectedVae !== undefined; + const hasSelectedEncoder = selectedEncoder !== null && selectedEncoder !== undefined; + const selectedVaeIsAvailable = hasSelectedVae && availableVaes.some((vae) => vae.key === selectedVae.key); + const selectedEncoderIsAvailable = + hasSelectedEncoder && availableEncoders.some((encoder) => encoder.key === selectedEncoder.key); + + return { + ...(!selectedVaeIsAvailable && (hasSelectedVae || defaultVae) ? { vae: defaultVae ?? null } : {}), + ...(!selectedEncoderIsAvailable && (hasSelectedEncoder || defaultEncoder) + ? { encoder: defaultEncoder ?? null } + : {}), + }; +}; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.test.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.test.ts index 2dab056cf69..9adf54102e1 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.test.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.test.ts @@ -37,6 +37,33 @@ const mockFluxMainModel = { type: 'main' as const, }; +const mockKrea2MainModel = { + key: 'krea2-main-key', + hash: 'krea2-main-hash', + name: 'Krea-2 Turbo', + base: 'krea-2' as const, + type: 'main' as const, +}; + +// Krea-2 borrows the Qwen-Image VAE and uses a standalone Qwen3-VL encoder for single-file / GGUF transformers. +const mockKrea2Vae = { + key: 'krea2-vae-key', + hash: 'krea2-vae-hash', + name: 'Qwen Image VAE', + base: 'qwen-image' as const, + type: 'vae' as const, + format: 'checkpoint' as const, +}; + +const mockKrea2Qwen3VlEncoder = { + key: 'krea2-q3vl-key', + hash: 'krea2-q3vl-hash', + name: 'Qwen3-VL 4B Encoder', + base: 'any' as const, + type: 'qwen3_vl_encoder' as const, + format: 'qwen3_vl_encoder' as const, +}; + // Track dispatched actions const dispatched: Array<{ type: string; payload: unknown }> = []; const mockDispatch = vi.fn((action: { type: string; payload: unknown }) => { @@ -69,9 +96,15 @@ const mockSelectAnimaQwen3EncoderModels = vi.fn((_state: unknown) => [mockAnimaQ const mockSelectAnimaVAEModels = vi.fn((_state: unknown) => [mockAnimaVAE]); +// Krea-2 standalone-component selectors (used only by the Krea-2 auto-select branch). +const mockSelectQwenImageVAEModels = vi.fn((_state: unknown) => [mockKrea2Vae]); +const mockSelectQwen3VLEncoderModels = vi.fn((_state: unknown) => [mockKrea2Qwen3VlEncoder]); + vi.mock('services/api/hooks/modelsByType', () => ({ selectAnimaQwen3EncoderModels: (state: unknown) => mockSelectAnimaQwen3EncoderModels(state), selectAnimaVAEModels: (state: unknown) => mockSelectAnimaVAEModels(state), + selectQwenImageVAEModels: (state: unknown) => mockSelectQwenImageVAEModels(state), + selectQwen3VLEncoderModels: (state: unknown) => mockSelectQwen3VLEncoderModels(state), selectQwen3EncoderModels: vi.fn(() => []), selectZImageDiffusersModels: vi.fn(() => []), selectFluxVAEModels: vi.fn(() => []), @@ -79,10 +112,20 @@ vi.mock('services/api/hooks/modelsByType', () => ({ selectRegionalRefImageModels: vi.fn(() => []), })); -// Mock model configs adapter +// Mock model configs adapter. Routed through overridable fns so the Krea-2 tests can toggle the resolved +// model's format (diffusers vs. single-file/GGUF), which drives clear-vs-auto-select. +const mockSelectModelConfigsQuery = vi.fn((_state: unknown) => ({ data: undefined }) as { data: unknown }); +const mockSelectModelById = vi.fn((_data: unknown, _key: string) => undefined as unknown); +const mockSelectIndividualModelConfig = vi.fn((_state: unknown) => ({ data: undefined }) as { data: unknown }); + vi.mock('services/api/endpoints/models', () => ({ - modelConfigsAdapterSelectors: { selectById: vi.fn() }, - selectModelConfigsQuery: vi.fn(() => ({ data: undefined })), + modelConfigsAdapterSelectors: { selectById: (data: unknown, key: string) => mockSelectModelById(data, key) }, + modelsApi: { + endpoints: { + getModelConfig: { select: (_key: string) => (state: unknown) => mockSelectIndividualModelConfig(state) }, + }, + }, + selectModelConfigsQuery: (state: unknown) => mockSelectModelConfigsQuery(state), })); vi.mock('services/api/types', () => ({ @@ -144,8 +187,15 @@ let capturedEffect: ((action: unknown, api: unknown) => void) | null = null; const paramsSliceActual = (await vi.importActual('features/controlLayers/store/paramsSlice')) as { animaQwen3EncoderModelSelected: { type: string }; animaVaeModelSelected: { type: string }; + krea2VaeModelSelected: { type: string }; + krea2Qwen3VlEncoderModelSelected: { type: string }; }; -const { animaQwen3EncoderModelSelected, animaVaeModelSelected } = paramsSliceActual; +const { + animaQwen3EncoderModelSelected, + animaVaeModelSelected, + krea2VaeModelSelected, + krea2Qwen3VlEncoderModelSelected, +} = paramsSliceActual; // Import after mocks are set up const { addModelSelectedListener } = await import('./modelSelected'); @@ -310,3 +360,205 @@ describe('zModelIdentifierField schema validation', () => { expect(zModelIdentifierField.safeParse(complete).success).toBe(true); }); }); + +describe('modelSelected listener - Krea-2 defaulting', () => { + beforeEach(() => { + dispatched.length = 0; + mockDispatch.mockClear(); + // Standalone components installed by default; resolved model defaults to a non-diffusers (single-file / + // GGUF) transformer, which is what triggers the auto-select branch. + mockSelectQwenImageVAEModels.mockReturnValue([mockKrea2Vae]); + mockSelectAnimaVAEModels.mockReturnValue([mockAnimaVAE]); + mockSelectQwen3VLEncoderModels.mockReturnValue([mockKrea2Qwen3VlEncoder]); + mockSelectModelConfigsQuery.mockReturnValue({ data: {} }); + mockSelectModelById.mockReturnValue({ format: 'checkpoint' }); + mockSelectIndividualModelConfig.mockReturnValue({ data: undefined }); + }); + + it('auto-selects a standalone VAE and Qwen3-VL encoder when switching to a single-file/GGUF Krea-2 model', () => { + const state = buildMockState({ model: mockFluxMainModel }); + const action = modelSelected(zParameterModel.parse(mockKrea2MainModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + const vaeDispatch = dispatched.find((a) => a.type === krea2VaeModelSelected.type); + const encoderDispatch = dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type); + + expect(vaeDispatch).toBeDefined(); + expect(encoderDispatch).toBeDefined(); + // The reducer parses payloads with zModelIdentifierField, so the dispatched values must be complete. + expect(zModelIdentifierField.safeParse(vaeDispatch!.payload).success).toBe(true); + expect(zModelIdentifierField.safeParse(encoderDispatch!.payload).success).toBe(true); + expect(vaeDispatch!.payload).toMatchObject({ key: mockKrea2Vae.key, type: 'vae' }); + expect(encoderDispatch!.payload).toMatchObject({ key: mockKrea2Qwen3VlEncoder.key, type: 'qwen3_vl_encoder' }); + }); + + it('falls back to an Anima-tagged VAE when no Qwen-Image VAE is installed', () => { + mockSelectQwenImageVAEModels.mockReturnValue([]); + mockSelectAnimaVAEModels.mockReturnValue([mockAnimaVAE]); + + const state = buildMockState({ model: mockFluxMainModel }); + const action = modelSelected(zParameterModel.parse(mockKrea2MainModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + const vaeDispatch = dispatched.find((a) => a.type === krea2VaeModelSelected.type); + expect(vaeDispatch).toBeDefined(); + expect(vaeDispatch!.payload).toMatchObject({ key: mockAnimaVAE.key }); + }); + + it('does not auto-select standalone components when none are installed', () => { + // No Qwen-Image VAEs, no Anima VAEs (the fallback), no Qwen3-VL encoders. + mockSelectQwenImageVAEModels.mockReturnValue([]); + mockSelectAnimaVAEModels.mockReturnValue([]); + mockSelectQwen3VLEncoderModels.mockReturnValue([]); + + const state = buildMockState({ model: mockFluxMainModel }); + const action = modelSelected(zParameterModel.parse(mockKrea2MainModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + expect(dispatched.find((a) => a.type === krea2VaeModelSelected.type)).toBeUndefined(); + expect(dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type)).toBeUndefined(); + }); + + it('defers standalone component changes while the selected model format is unknown', () => { + mockSelectModelConfigsQuery.mockReturnValue({ data: undefined }); + mockSelectModelById.mockReturnValue(undefined); + const state = buildMockState({ model: mockFluxMainModel }); + const action = modelSelected(zParameterModel.parse(mockKrea2MainModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + expect(dispatched.find((a) => a.type === krea2VaeModelSelected.type)).toBeUndefined(); + expect(dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type)).toBeUndefined(); + }); + + it('uses the individual model-config cache when the list cache is unavailable', () => { + mockSelectModelConfigsQuery.mockReturnValue({ data: undefined }); + mockSelectModelById.mockReturnValue(undefined); + mockSelectIndividualModelConfig.mockReturnValue({ data: { format: 'diffusers' } }); + const state = buildMockState({ + model: mockKrea2MainModel, + krea2VaeModel: { key: 'stale-vae', hash: 'h', name: 'Stale VAE', base: 'qwen-image', type: 'vae' }, + krea2Qwen3VlEncoderModel: { + key: 'stale-enc', + hash: 'h', + name: 'Stale Enc', + base: 'any', + type: 'qwen3_vl_encoder', + }, + }); + const nextKreaModel = { ...mockKrea2MainModel, key: 'next-krea-key' }; + const action = modelSelected(zParameterModel.parse(nextKreaModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + expect(dispatched.find((a) => a.type === krea2VaeModelSelected.type)?.payload).toBeNull(); + expect(dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type)?.payload).toBeNull(); + }); + + it('does not overwrite standalone components the user already selected', () => { + const existingVae = { + key: 'existing-vae', + hash: 'h', + name: 'Existing VAE', + base: 'qwen-image', + type: 'vae', + format: 'checkpoint', + } as const; + const existingEncoder = { + key: 'existing-enc', + hash: 'h', + name: 'Existing Enc', + base: 'any', + type: 'qwen3_vl_encoder', + format: 'qwen3_vl_encoder', + } as const; + mockSelectQwenImageVAEModels.mockReturnValue([existingVae]); + mockSelectQwen3VLEncoderModels.mockReturnValue([existingEncoder]); + const state = buildMockState({ + model: mockFluxMainModel, + krea2VaeModel: existingVae, + krea2Qwen3VlEncoderModel: existingEncoder, + }); + const action = modelSelected(zParameterModel.parse(mockKrea2MainModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + // Already set + non-diffusers -> nothing dispatched for the standalone slots. + expect(dispatched.find((a) => a.type === krea2VaeModelSelected.type)).toBeUndefined(); + expect(dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type)).toBeUndefined(); + }); + + it('clears stale standalone components when no replacement is installed', () => { + mockSelectQwenImageVAEModels.mockReturnValue([]); + mockSelectAnimaVAEModels.mockReturnValue([]); + mockSelectQwen3VLEncoderModels.mockReturnValue([]); + const state = buildMockState({ + model: mockFluxMainModel, + krea2VaeModel: { key: 'deleted-vae', hash: 'h', name: 'Deleted VAE', base: 'qwen-image', type: 'vae' }, + krea2Qwen3VlEncoderModel: { + key: 'deleted-enc', + hash: 'h', + name: 'Deleted Enc', + base: 'any', + type: 'qwen3_vl_encoder', + }, + }); + + capturedEffect!(modelSelected(zParameterModel.parse(mockKrea2MainModel)), { + getState: () => state, + dispatch: mockDispatch, + }); + + expect(dispatched.find((a) => a.type === krea2VaeModelSelected.type)?.payload).toBeNull(); + expect(dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type)?.payload).toBeNull(); + }); + + it('clears stale standalone overrides when switching to a Diffusers Krea-2 model', () => { + // A Diffusers pipeline bundles its own VAE + encoder, so any standalone overrides must be cleared. + mockSelectModelConfigsQuery.mockReturnValue({ data: {} }); + mockSelectModelById.mockReturnValue({ format: 'diffusers' }); + + const state = buildMockState({ + model: mockFluxMainModel, + krea2VaeModel: { key: 'stale-vae', hash: 'h', name: 'Stale VAE', base: 'qwen-image', type: 'vae' }, + krea2Qwen3VlEncoderModel: { + key: 'stale-enc', + hash: 'h', + name: 'Stale Enc', + base: 'any', + type: 'qwen3_vl_encoder', + }, + }); + const action = modelSelected(zParameterModel.parse(mockKrea2MainModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + const vaeDispatch = dispatched.find((a) => a.type === krea2VaeModelSelected.type); + const encoderDispatch = dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type); + expect(vaeDispatch).toBeDefined(); + expect(vaeDispatch!.payload).toBeNull(); + expect(encoderDispatch).toBeDefined(); + expect(encoderDispatch!.payload).toBeNull(); + }); + + it('clears Krea-2 standalone components when switching away from Krea-2', () => { + const state = buildMockState({ + model: mockKrea2MainModel, + krea2VaeModel: { key: 'vae', hash: 'h', name: 'VAE', base: 'qwen-image', type: 'vae' }, + krea2Qwen3VlEncoderModel: { key: 'enc', hash: 'h', name: 'Enc', base: 'any', type: 'qwen3_vl_encoder' }, + }); + const action = modelSelected(zParameterModel.parse(mockFluxMainModel)); + + capturedEffect!(action, { getState: () => state, dispatch: mockDispatch }); + + const vaeDispatch = dispatched.find((a) => a.type === krea2VaeModelSelected.type); + const encoderDispatch = dispatched.find((a) => a.type === krea2Qwen3VlEncoderModelSelected.type); + expect(vaeDispatch).toBeDefined(); + expect(vaeDispatch!.payload).toBeNull(); + expect(encoderDispatch).toBeDefined(); + expect(encoderDispatch!.payload).toBeNull(); + }); +}); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts index 9e67e013946..a58f5d5d3af 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts @@ -9,6 +9,8 @@ import { aspectRatioIdChanged, kleinQwen3EncoderModelSelected, kleinVaeModelSelected, + krea2Qwen3VlEncoderModelSelected, + krea2VaeModelSelected, modelChanged, qwenImageComponentSourceSelected, qwenImageQwenVLEncoderModelSelected, @@ -50,13 +52,14 @@ import { modelSelected } from 'features/parameters/store/actions'; import { zParameterModel } from 'features/parameters/types/parameterSchemas'; import { toast } from 'features/toast/toast'; import { t } from 'i18next'; -import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models'; +import { modelConfigsAdapterSelectors, modelsApi, selectModelConfigsQuery } from 'services/api/endpoints/models'; import { selectAnimaQwen3EncoderModels, selectAnimaVAEModels, selectFluxVAEModels, selectGlobalRefImageModels, selectQwen3EncoderModels, + selectQwen3VLEncoderModels, selectQwenImageDiffusersModels, selectQwenImageVAEModels, selectQwenVLEncoderModels, @@ -66,6 +69,8 @@ import { import type { FLUXKontextModelConfig, FLUXReduxModelConfig, IPAdapterModelConfig } from 'services/api/types'; import { isExternalApiModelConfig, isFluxKontextModelConfig, isFluxReduxModelConfig } from 'services/api/types'; +import { getKrea2ComponentUpdates } from './krea2ComponentSync'; + const log = logger('models'); export const addModelSelectedListener = (startAppListening: AppStartListening) => { @@ -302,6 +307,71 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) = } } + // handle incompatible Krea-2 standalone components + const { krea2VaeModel, krea2Qwen3VlEncoderModel } = state.params; + if (newBase !== 'krea-2') { + // Switching away from Krea-2 - clear the standalone VAE / Qwen3-VL encoder selections so they + // don't survive onto another model family (or get reused as stale overrides later). + if (krea2VaeModel) { + dispatch(krea2VaeModelSelected(null)); + modelsUpdatedDisabledOrCleared += 1; + } + if (krea2Qwen3VlEncoderModel) { + dispatch(krea2Qwen3VlEncoderModelSelected(null)); + modelsUpdatedDisabledOrCleared += 1; + } + } else { + // Switching to Krea-2. A Diffusers pipeline bundles its own VAE + encoder, so clear any + // standalone overrides (buildKrea2Graph would otherwise pass stale selections). A single-file / + // GGUF transformer ships only the transformer, so auto-select standalone components if the user + // hasn't already picked them - this unblocks the readiness check after installing the starter pack. + const modelConfigsResult = selectModelConfigsQuery(state); + const newModelConfig = + (modelConfigsResult.data + ? modelConfigsAdapterSelectors.selectById(modelConfigsResult.data, newModel.key) + : undefined) ?? modelsApi.endpoints.getModelConfig.select(newModel.key)(state).data; + if (!newModelConfig) { + // The model list may not be populated yet during startup or metadata recall. Defer component + // changes until the selected model's format is known instead of treating unknown as single-file. + } else if (newModelConfig.format === 'diffusers') { + const updates = getKrea2ComponentUpdates({ + format: newModelConfig.format, + selectedVae: krea2VaeModel, + selectedEncoder: krea2Qwen3VlEncoderModel, + availableQwenImageVaes: selectQwenImageVAEModels(state), + availableAnimaVaes: selectAnimaVAEModels(state), + availableEncoders: selectQwen3VLEncoderModels(state), + }); + if ('vae' in updates) { + dispatch(krea2VaeModelSelected(updates.vae ? zModelIdentifierField.parse(updates.vae) : null)); + modelsUpdatedDisabledOrCleared += 1; + } + if ('encoder' in updates) { + dispatch( + krea2Qwen3VlEncoderModelSelected(updates.encoder ? zModelIdentifierField.parse(updates.encoder) : null) + ); + modelsUpdatedDisabledOrCleared += 1; + } + } else { + const updates = getKrea2ComponentUpdates({ + format: newModelConfig.format, + selectedVae: krea2VaeModel, + selectedEncoder: krea2Qwen3VlEncoderModel, + availableQwenImageVaes: selectQwenImageVAEModels(state), + availableAnimaVaes: selectAnimaVAEModels(state), + availableEncoders: selectQwen3VLEncoderModels(state), + }); + if ('vae' in updates) { + dispatch(krea2VaeModelSelected(updates.vae ? zModelIdentifierField.parse(updates.vae) : null)); + } + if ('encoder' in updates) { + dispatch( + krea2Qwen3VlEncoderModelSelected(updates.encoder ? zModelIdentifierField.parse(updates.encoder) : null) + ); + } + } + } + if (newModel.base !== 'external' && SUPPORTS_REF_IMAGES_BASE_MODELS.includes(newModel.base)) { // Handle incompatible reference image models - switch to first compatible model, with some smart logic // to choose the best available model based on the new main model. @@ -519,6 +589,38 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) = } } + // Handle Krea-2 model changes within the same base (e.g. switching GGUF <-> Diffusers). A Diffusers + // pipeline bundles its VAE + encoder, so stale standalone overrides must be cleared; a single-file / + // GGUF transformer needs standalone components auto-selected so the readiness check passes. + if (newBase === 'krea-2' && state.params.model?.base === 'krea-2' && newModel.key !== state.params.model?.key) { + const { krea2VaeModel, krea2Qwen3VlEncoderModel } = state.params; + const modelConfigsResult = selectModelConfigsQuery(state); + const newModelConfig = + (modelConfigsResult.data + ? modelConfigsAdapterSelectors.selectById(modelConfigsResult.data, newModel.key) + : undefined) ?? modelsApi.endpoints.getModelConfig.select(newModel.key)(state).data; + if (!newModelConfig) { + // Defer until the model format is known. + } else { + const updates = getKrea2ComponentUpdates({ + format: newModelConfig.format, + selectedVae: krea2VaeModel, + selectedEncoder: krea2Qwen3VlEncoderModel, + availableQwenImageVaes: selectQwenImageVAEModels(state), + availableAnimaVaes: selectAnimaVAEModels(state), + availableEncoders: selectQwen3VLEncoderModels(state), + }); + if ('vae' in updates) { + dispatch(krea2VaeModelSelected(updates.vae ? zModelIdentifierField.parse(updates.vae) : null)); + } + if ('encoder' in updates) { + dispatch( + krea2Qwen3VlEncoderModelSelected(updates.encoder ? zModelIdentifierField.parse(updates.encoder) : null) + ); + } + } + } + // Handle Z-Image scheduler when switching to Z-Image Base (zbase) model // LCM is not supported for undistilled models, so reset to euler if (newBase === 'z-image' && state.params.zImageScheduler === 'lcm') { diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.krea2.test.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.krea2.test.ts new file mode 100644 index 00000000000..e2d89af4866 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.krea2.test.ts @@ -0,0 +1,85 @@ +import type { RootState } from 'app/store/store'; +import { krea2Qwen3VlEncoderModelSelected, krea2VaeModelSelected } from 'features/controlLayers/store/paramsSlice'; +import type { AnyModelConfig } from 'services/api/types'; +import { describe, expect, it, vi } from 'vitest'; + +import { handleKrea2Components } from './modelsLoaded'; + +const mainModel = { + key: 'krea-main', + hash: 'main-hash', + name: 'Krea Main', + base: 'krea-2', + type: 'main', + format: 'gguf_quantized', +} as const; +const vae = { + key: 'vae', + hash: 'vae-hash', + name: 'Qwen Image VAE', + base: 'qwen-image', + type: 'vae', + format: 'checkpoint', +} as const; +const encoder = { + key: 'encoder', + hash: 'encoder-hash', + name: 'Qwen3-VL Encoder', + base: 'any', + type: 'qwen3_vl_encoder', + format: 'qwen3_vl_encoder', +} as const; + +const makeState = (overrides: Record = {}) => + ({ + params: { + model: mainModel, + krea2VaeModel: null, + krea2Qwen3VlEncoderModel: null, + ...overrides, + }, + }) as unknown as RootState; + +describe('handleKrea2Components', () => { + it('selects standalone components when a deferred non-Diffusers model arrives in the fulfilled list', () => { + const dispatch = vi.fn(); + + handleKrea2Components( + [mainModel, vae, encoder] as unknown as AnyModelConfig[], + makeState(), + dispatch, + null as never + ); + + expect(dispatch).toHaveBeenCalledWith(krea2VaeModelSelected(expect.objectContaining({ key: vae.key }))); + expect(dispatch).toHaveBeenCalledWith( + krea2Qwen3VlEncoderModelSelected(expect.objectContaining({ key: encoder.key })) + ); + }); + + it('clears stale standalone components when a deferred Diffusers model arrives in the fulfilled list', () => { + const dispatch = vi.fn(); + const diffusersMain = { ...mainModel, format: 'diffusers' } as const; + const state = makeState({ model: diffusersMain, krea2VaeModel: vae, krea2Qwen3VlEncoderModel: encoder }); + + handleKrea2Components([diffusersMain, vae, encoder] as unknown as AnyModelConfig[], state, dispatch, null as never); + + expect(dispatch).toHaveBeenCalledWith(krea2VaeModelSelected(null)); + expect(dispatch).toHaveBeenCalledWith(krea2Qwen3VlEncoderModelSelected(null)); + }); + + it('replaces deleted standalone components when the model list refreshes', () => { + const dispatch = vi.fn(); + const state = makeState({ + krea2VaeModel: { ...vae, key: 'deleted-vae' }, + krea2Qwen3VlEncoderModel: { ...encoder, key: 'deleted-encoder' }, + }); + + handleKrea2Components([mainModel, vae, encoder] as unknown as AnyModelConfig[], state, dispatch, null as never); + + expect(dispatch).toHaveBeenCalledWith(krea2VaeModelSelected(expect.objectContaining({ key: vae.key }))); + expect(dispatch).toHaveBeenCalledWith( + krea2Qwen3VlEncoderModelSelected(expect.objectContaining({ key: encoder.key })) + ); + }); +}); 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 8cbbc72343b..130a0972963 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 @@ -5,6 +5,8 @@ import { loraDeleted } from 'features/controlLayers/store/lorasSlice'; import { clipEmbedModelSelected, fluxVAESelected, + krea2Qwen3VlEncoderModelSelected, + krea2VaeModelSelected, modelChanged, refinerModelChanged, t5EncoderModelSelected, @@ -19,6 +21,7 @@ import { isRegionalGuidanceFLUXReduxConfig, isRegionalGuidanceIPAdapterConfig, } from 'features/controlLayers/store/types'; +import { zModelIdentifierField } from 'features/nodes/types/common'; import { modelSelected } from 'features/parameters/store/actions'; import { postProcessingModelChanged, @@ -35,6 +38,7 @@ import type { Logger } from 'roarr'; import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models'; import type { AnyModelConfig } from 'services/api/types'; import { + isAnimaVAEModelConfig, isCLIPEmbedModelConfigOrSubmodel, isControlLayerModelConfig, isControlNetModelConfig, @@ -44,12 +48,16 @@ import { isLoRAModelConfig, isNonFluxVAEModelConfig, isNonRefinerMainModelConfig, + isQwen3VLEncoderModelConfig, + isQwenImageVAEModelConfig, isRefinerMainModelModelConfig, isSpandrelImageToImageModelConfig, isT5EncoderModelConfigOrSubmodel, } from 'services/api/types'; import type { JsonObject } from 'type-fest'; +import { getKrea2ComponentUpdates } from './krea2ComponentSync'; + const log = logger('models'); /** @@ -75,6 +83,7 @@ export const addModelsLoadedListener = (startAppListening: AppStartListening) => const models = modelConfigsAdapterSelectors.selectAll(action.payload); handleMainModels(models, state, dispatch, log); + handleKrea2Components(models, state, dispatch, log); handleRefinerModels(models, state, dispatch, log); handleVAEModels(models, state, dispatch, log); handleLoRAModels(models, state, dispatch, log); @@ -91,6 +100,31 @@ export const addModelsLoadedListener = (startAppListening: AppStartListening) => }); }; +export const handleKrea2Components: ModelHandler = (models, state, dispatch) => { + if (state.params.model?.base !== 'krea-2') { + return; + } + const selectedModel = models.find((model) => model.key === state.params.model?.key); + if (!selectedModel || !isNonRefinerMainModelConfig(selectedModel)) { + return; + } + + const updates = getKrea2ComponentUpdates({ + format: selectedModel.format, + selectedVae: state.params.krea2VaeModel, + selectedEncoder: state.params.krea2Qwen3VlEncoderModel, + availableQwenImageVaes: models.filter((model) => isQwenImageVAEModelConfig(model)), + availableAnimaVaes: models.filter((model) => isAnimaVAEModelConfig(model)), + availableEncoders: models.filter(isQwen3VLEncoderModelConfig), + }); + if ('vae' in updates) { + dispatch(krea2VaeModelSelected(updates.vae ? zModelIdentifierField.parse(updates.vae) : null)); + } + if ('encoder' in updates) { + dispatch(krea2Qwen3VlEncoderModelSelected(updates.encoder ? zModelIdentifierField.parse(updates.encoder) : null)); + } +}; + type ModelHandler = ( models: AnyModelConfig[], state: RootState, diff --git a/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts b/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts index e9d855648ad..2a55c964381 100644 --- a/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts +++ b/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts @@ -13,6 +13,12 @@ export type Feature = | 'seedVarianceEnhancer' | 'seedVarianceStrength' | 'seedVarianceRandomizePercent' + | 'krea2ConditioningRebalance' + | 'krea2RebalanceMultiplier' + | 'krea2RebalanceWeights' + | 'krea2SeedVarianceEnhancer' + | 'krea2SeedVarianceStrength' + | 'krea2SeedVarianceRandomizePercent' | 'compositingMaskBlur' | 'compositingBlurMethod' | 'compositingCoherencePass' diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts index d210d2fd2ac..8abb24cad12 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts @@ -157,7 +157,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(v2State) as ReturnType; - expect(result._version).toBe(3); + expect(result._version).toBe(4); expect(result.qwenImageVaeModel).toBeNull(); expect(result.qwenImageQwenVLEncoderModel).toBeNull(); // Existing params should be preserved @@ -168,6 +168,42 @@ describe('paramsSliceConfig persisted state migration', () => { expect(result.dimensions.height).toBe(768); }); + it('backfills Krea-2 fields when migrating from v3 and preserves existing params', () => { + expect(migrate).toBeDefined(); + + const initial = getInitialParamsState(); + const v3State: Record = { + ...initial, + _version: 3, + positivePrompt: 'preserve this prompt', + seed: 1234, + dimensions: { ...initial.dimensions, width: 640, height: 896 }, + }; + delete v3State.krea2VaeModel; + delete v3State.krea2Qwen3VlEncoderModel; + delete v3State.krea2SeedVarianceEnabled; + delete v3State.krea2SeedVarianceStrength; + delete v3State.krea2SeedVarianceRandomizePercent; + delete v3State.krea2RebalanceEnabled; + delete v3State.krea2RebalanceMultiplier; + delete v3State.krea2RebalanceWeights; + + const result = migrate?.(v3State) as ReturnType; + + expect(result._version).toBe(4); + expect(result.krea2VaeModel).toBeNull(); + expect(result.krea2Qwen3VlEncoderModel).toBeNull(); + expect(result.krea2SeedVarianceEnabled).toBe(false); + expect(result.krea2SeedVarianceStrength).toBe(20); + expect(result.krea2SeedVarianceRandomizePercent).toBe(50); + expect(result.krea2RebalanceEnabled).toBe(false); + expect(result.krea2RebalanceMultiplier).toBe(4); + expect(result.krea2RebalanceWeights).toBe('1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0'); + expect(result.positivePrompt).toBe('preserve this prompt'); + expect(result.seed).toBe(1234); + expect(result.dimensions).toMatchObject({ width: 640, height: 896 }); + }); + it('migrates old positive prompt history entries to prompt pairs', () => { expect(migrate).toBeDefined(); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts index b7b1a6ef1c0..3c998e80199 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts @@ -107,6 +107,24 @@ const slice = createSlice({ setZImageSeedVarianceRandomizePercent: (state, action: PayloadAction) => { state.zImageSeedVarianceRandomizePercent = action.payload; }, + setKrea2SeedVarianceEnabled: (state, action: PayloadAction) => { + state.krea2SeedVarianceEnabled = action.payload; + }, + setKrea2SeedVarianceStrength: (state, action: PayloadAction) => { + state.krea2SeedVarianceStrength = action.payload; + }, + setKrea2SeedVarianceRandomizePercent: (state, action: PayloadAction) => { + state.krea2SeedVarianceRandomizePercent = action.payload; + }, + setKrea2RebalanceEnabled: (state, action: PayloadAction) => { + state.krea2RebalanceEnabled = action.payload; + }, + setKrea2RebalanceMultiplier: (state, action: PayloadAction) => { + state.krea2RebalanceMultiplier = action.payload; + }, + setKrea2RebalanceWeights: (state, action: PayloadAction) => { + state.krea2RebalanceWeights = action.payload; + }, setUpscaleScheduler: (state, action: PayloadAction) => { state.upscaleScheduler = action.payload; }, @@ -223,6 +241,23 @@ const slice = createSlice({ } state.zImageQwen3SourceModel = result.data; }, + krea2VaeModelSelected: (state, action: PayloadAction) => { + const result = zParamsState.shape.krea2VaeModel.safeParse(action.payload); + if (!result.success) { + return; + } + state.krea2VaeModel = result.data; + }, + krea2Qwen3VlEncoderModelSelected: ( + state, + action: PayloadAction<{ key: string; name: string; base: string } | null> + ) => { + const result = zParamsState.shape.krea2Qwen3VlEncoderModel.safeParse(action.payload); + if (!result.success) { + return; + } + state.krea2Qwen3VlEncoderModel = result.data; + }, animaVaeModelSelected: (state, action: PayloadAction) => { const result = zParamsState.shape.animaVaeModel.safeParse(action.payload); if (!result.success) { @@ -626,6 +661,8 @@ const resetState = (state: ParamsState): ParamsState => { newState.zImageVaeModel = oldState.zImageVaeModel; newState.zImageQwen3EncoderModel = oldState.zImageQwen3EncoderModel; newState.zImageQwen3SourceModel = oldState.zImageQwen3SourceModel; + newState.krea2VaeModel = oldState.krea2VaeModel; + newState.krea2Qwen3VlEncoderModel = oldState.krea2Qwen3VlEncoderModel; newState.animaVaeModel = oldState.animaVaeModel; newState.animaQwen3EncoderModel = oldState.animaQwen3EncoderModel; newState.animaLLLiteModel = oldState.animaLLLiteModel; @@ -663,6 +700,12 @@ export const { setZImageSeedVarianceEnabled, setZImageSeedVarianceStrength, setZImageSeedVarianceRandomizePercent, + setKrea2SeedVarianceEnabled, + setKrea2SeedVarianceStrength, + setKrea2SeedVarianceRandomizePercent, + setKrea2RebalanceEnabled, + setKrea2RebalanceMultiplier, + setKrea2RebalanceWeights, setUpscaleScheduler, setUpscaleCfgScale, setSeed, @@ -681,6 +724,8 @@ export const { zImageVaeModelSelected, zImageQwen3EncoderModelSelected, zImageQwen3SourceModelSelected, + krea2VaeModelSelected, + krea2Qwen3VlEncoderModelSelected, kleinVaeModelSelected, kleinQwen3EncoderModelSelected, qwenImageComponentSourceSelected, @@ -761,6 +806,19 @@ export const paramsSliceConfig: SliceConfig = { state.qwenImageQwenVLEncoderModel = null; } + if (state._version === 3) { + // v3 -> v4, add Krea-2 standalone component and conditioning enhancer fields + state._version = 4; + state.krea2VaeModel = null; + state.krea2Qwen3VlEncoderModel = null; + state.krea2SeedVarianceEnabled = false; + state.krea2SeedVarianceStrength = 20; + state.krea2SeedVarianceRandomizePercent = 50; + state.krea2RebalanceEnabled = false; + state.krea2RebalanceMultiplier = 4; + state.krea2RebalanceWeights = '1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0'; + } + return zParamsState.parse(state); }, }, @@ -779,6 +837,7 @@ export const selectIsAnima = createParamsSelector((params) => params.model?.base export const selectIsFlux2 = createParamsSelector((params) => params.model?.base === 'flux2'); export const selectIsExternal = createParamsSelector((params) => params.model?.base === 'external'); export const selectIsQwenImage = createParamsSelector((params) => params.model?.base === 'qwen-image'); +export const selectIsKrea2 = createParamsSelector((params) => params.model?.base === 'krea-2'); export const selectIsFluxKontext = createParamsSelector((params) => { if (params.model?.base === 'flux' && params.model?.name.toLowerCase().includes('kontext')) { return true; @@ -799,6 +858,8 @@ export const selectCLIPGEmbedModel = createParamsSelector((params) => params.cli export const selectZImageVaeModel = createParamsSelector((params) => params.zImageVaeModel); export const selectZImageQwen3EncoderModel = createParamsSelector((params) => params.zImageQwen3EncoderModel); export const selectZImageQwen3SourceModel = createParamsSelector((params) => params.zImageQwen3SourceModel); +export const selectKrea2VaeModel = createParamsSelector((params) => params.krea2VaeModel); +export const selectKrea2Qwen3VlEncoderModel = createParamsSelector((params) => params.krea2Qwen3VlEncoderModel); export const selectAnimaVaeModel = createParamsSelector((params) => params.animaVaeModel); export const selectAnimaQwen3EncoderModel = createParamsSelector((params) => params.animaQwen3EncoderModel); export const selectAnimaScheduler = createParamsSelector((params) => params.animaScheduler); @@ -930,6 +991,14 @@ export const selectZImageSeedVarianceStrength = createParamsSelector((params) => export const selectZImageSeedVarianceRandomizePercent = createParamsSelector( (params) => params.zImageSeedVarianceRandomizePercent ); +export const selectKrea2SeedVarianceEnabled = createParamsSelector((params) => params.krea2SeedVarianceEnabled); +export const selectKrea2SeedVarianceStrength = createParamsSelector((params) => params.krea2SeedVarianceStrength); +export const selectKrea2SeedVarianceRandomizePercent = createParamsSelector( + (params) => params.krea2SeedVarianceRandomizePercent +); +export const selectKrea2RebalanceEnabled = createParamsSelector((params) => params.krea2RebalanceEnabled); +export const selectKrea2RebalanceMultiplier = createParamsSelector((params) => params.krea2RebalanceMultiplier); +export const selectKrea2RebalanceWeights = createParamsSelector((params) => params.krea2RebalanceWeights); export const selectSeamlessXAxis = createParamsSelector((params) => params.seamlessXAxis); export const selectSeamlessYAxis = createParamsSelector((params) => params.seamlessYAxis); export const selectSeed = createParamsSelector((params) => params.seed); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index 43ea53d13f3..7fa1f9a3c67 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -790,7 +790,7 @@ export const zInfillMethod = z.enum(['patchmatch', 'lama', 'cv2', 'color', 'tile export type InfillMethod = z.infer; export const zParamsState = z.object({ - _version: z.literal(3), + _version: z.literal(4), maskBlur: z.number(), maskBlurMethod: zParameterMaskBlurMethod, canvasCoherenceMode: zParameterCanvasCoherenceMode, @@ -865,6 +865,17 @@ export const zParamsState = z.object({ zImageSeedVarianceEnabled: z.boolean(), zImageSeedVarianceStrength: z.number().min(0).max(2), zImageSeedVarianceRandomizePercent: z.number().min(1).max(100), + // Krea-2 standalone submodels (optional; used when the transformer is a single-file checkpoint/GGUF + // that has no bundled VAE / Qwen3-VL encoder. When null, they are extracted from the diffusers model.) + krea2VaeModel: zParameterVAEModel.nullable(), + krea2Qwen3VlEncoderModel: zModelIdentifierField.nullable(), + // Krea-2 conditioning enhancers (optional; both default off so stock behaviour is unchanged) + krea2SeedVarianceEnabled: z.boolean(), + krea2SeedVarianceStrength: z.number().min(0).max(100), + krea2SeedVarianceRandomizePercent: z.number().min(1).max(100), + krea2RebalanceEnabled: z.boolean(), + krea2RebalanceMultiplier: z.number().min(0).max(20), + krea2RebalanceWeights: z.string(), imageSize: z.string().nullable().default(null), // OpenAI-specific external options openaiQuality: z.enum(['auto', 'high', 'medium', 'low']).default('auto'), @@ -880,7 +891,7 @@ export const zParamsState = z.object({ }); export type ParamsState = z.infer; export const getInitialParamsState = (): ParamsState => ({ - _version: 3, + _version: 4, maskBlur: 16, maskBlurMethod: 'box', canvasCoherenceMode: 'Gaussian Blur', @@ -950,6 +961,14 @@ export const getInitialParamsState = (): ParamsState => ({ zImageSeedVarianceEnabled: false, zImageSeedVarianceStrength: 0.1, zImageSeedVarianceRandomizePercent: 50, + krea2VaeModel: null, + krea2Qwen3VlEncoderModel: null, + krea2SeedVarianceEnabled: false, + krea2SeedVarianceStrength: 20, + krea2SeedVarianceRandomizePercent: 50, + krea2RebalanceEnabled: false, + krea2RebalanceMultiplier: 4, + krea2RebalanceWeights: '1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0', imageSize: null, openaiQuality: 'auto', openaiBackground: 'auto', diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx index a4f1427aef9..853bf2ceaad 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx @@ -9,4 +9,22 @@ describe('IMAGE_METADATA_ACTION_HANDLERS', () => { expect(IMAGE_METADATA_ACTION_HANDLERS).toContain(ImageMetadataHandlers.QwenImageQuantization); expect(IMAGE_METADATA_ACTION_HANDLERS).toContain(ImageMetadataHandlers.QwenImageShift); }); + + it('includes every Krea-2 metadata handler in the recall parameters UI', () => { + // Krea-2 records standalone components (single-file / GGUF) and the conditioning-enhancer settings. + // All must be wired into the recall UI, otherwise they are saved to metadata but cannot be recalled. + const krea2Handlers = [ + ImageMetadataHandlers.Krea2VAEModel, + ImageMetadataHandlers.Krea2Qwen3VlEncoderModel, + ImageMetadataHandlers.Krea2SeedVarianceEnabled, + ImageMetadataHandlers.Krea2SeedVarianceStrength, + ImageMetadataHandlers.Krea2SeedVarianceRandomizePercent, + ImageMetadataHandlers.Krea2RebalanceEnabled, + ImageMetadataHandlers.Krea2RebalanceMultiplier, + ImageMetadataHandlers.Krea2RebalanceWeights, + ]; + for (const handler of krea2Handlers) { + expect(IMAGE_METADATA_ACTION_HANDLERS).toContain(handler); + } + }); }); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx index f20ef705be3..85d66d2ba2f 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -65,6 +65,14 @@ export const IMAGE_METADATA_ACTION_HANDLERS: ImageMetadataActionHandler[] = [ ImageMetadataHandlers.RefImages, ImageMetadataHandlers.KleinVAEModel, ImageMetadataHandlers.KleinQwen3EncoderModel, + ImageMetadataHandlers.Krea2VAEModel, + ImageMetadataHandlers.Krea2Qwen3VlEncoderModel, + ImageMetadataHandlers.Krea2SeedVarianceEnabled, + ImageMetadataHandlers.Krea2SeedVarianceStrength, + ImageMetadataHandlers.Krea2SeedVarianceRandomizePercent, + ImageMetadataHandlers.Krea2RebalanceEnabled, + ImageMetadataHandlers.Krea2RebalanceMultiplier, + ImageMetadataHandlers.Krea2RebalanceWeights, ImageMetadataHandlers.LoRAs, ]; diff --git a/invokeai/frontend/web/src/features/metadata/parsing.test.tsx b/invokeai/frontend/web/src/features/metadata/parsing.test.tsx index bb295303273..528cb523535 100644 --- a/invokeai/frontend/web/src/features/metadata/parsing.test.tsx +++ b/invokeai/frontend/web/src/features/metadata/parsing.test.tsx @@ -22,7 +22,7 @@ vi.mock('features/controlLayers/store/paramsSlice', async (importOriginal) => { return { ...mod, selectBase: () => currentBase }; }); -const fakeModel = (type: 'vae' | 'qwen3_encoder', base: string) => ({ +const fakeModel = (type: 'vae' | 'qwen3_encoder' | 'qwen3_vl_encoder', base: string) => ({ key: `${type}-key`, hash: 'hash', name: `Some ${type}`, @@ -105,10 +105,11 @@ describe('ImageMetadataHandlers — Klein recall gating', () => { }); describe('VAEModel (generic)', () => { - // The generic VAEModel handler must NOT also fire for FLUX.2 / Z-Image - // images, otherwise the metadata viewer renders duplicate VAE rows next - // to the dedicated KleinVAEModel / ZImageVAEModel handlers. - it.each(['flux2', 'z-image'])('rejects parsing when current base is %s', async (base) => { + // The generic VAEModel handler must NOT also fire for FLUX.2 / Z-Image / + // Krea-2 images, otherwise the metadata viewer renders duplicate VAE rows + // next to the dedicated KleinVAEModel / ZImageVAEModel / Krea2VAEModel + // handlers (and recalls into the wrong, shared VAE slot). + it.each(['flux2', 'z-image', 'krea-2'])('rejects parsing when current base is %s', async (base) => { currentBase = base; nextResolved = fakeModel('vae', base); const store = makeStore(); @@ -172,3 +173,152 @@ describe('ImageMetadataHandlers — Klein recall gating', () => { }); }); }); + +describe('ImageMetadataHandlers — Krea-2 recall gating', () => { + // Krea-2 borrows the Qwen-Image VAE and a standalone Qwen3-VL encoder for single-file / GGUF + // transformers, recalled into dedicated (krea2VaeModel / krea2Qwen3VlEncoderModel) slots — but only when + // the current main model is actually Krea-2. + describe('Krea2VAEModel', () => { + it.each(['qwen-image', 'anima'] as const)( + 'parses a supported %s VAE when the current and metadata main models are Krea-2', + async (vaeBase) => { + currentBase = 'krea-2'; + nextResolved = fakeModel('vae', vaeBase); + const store = makeStore(); + + const parsed = await ImageMetadataHandlers.Krea2VAEModel.parse( + { model: fakeModel('main', 'krea-2'), vae: nextResolved }, + store + ); + + expect(parsed.key).toBe('vae-key'); + expect(parsed.type).toBe('vae'); + expect(parsed.base).toBe(vaeBase); + } + ); + + it('rejects parsing when the current main model is not Krea-2', async () => { + currentBase = 'sdxl'; + nextResolved = fakeModel('vae', 'krea-2'); + const store = makeStore(); + + await expect(ImageMetadataHandlers.Krea2VAEModel.parse({ vae: nextResolved }, store)).rejects.toThrow(); + }); + + it('rejects VAE metadata from a non-Krea-2 image even when Krea-2 is currently selected', async () => { + currentBase = 'krea-2'; + nextResolved = fakeModel('vae', 'sdxl'); + const store = makeStore(); + + await expect( + ImageMetadataHandlers.Krea2VAEModel.parse( + { model: fakeModel('qwen3_vl_encoder', 'sdxl'), vae: nextResolved }, + store + ) + ).rejects.toThrow(); + }); + + it.each(['sdxl', 'flux'] as const)('rejects an incompatible %s VAE from Krea-2 image metadata', async (vaeBase) => { + currentBase = 'krea-2'; + nextResolved = fakeModel('vae', vaeBase); + const store = makeStore(); + + await expect( + ImageMetadataHandlers.Krea2VAEModel.parse({ model: fakeModel('main', 'krea-2'), vae: nextResolved }, store) + ).rejects.toThrow(); + }); + }); + + describe('Krea2Qwen3VlEncoderModel', () => { + it('parses metadata.qwen3_vl_encoder when the current main model is Krea-2', async () => { + currentBase = 'krea-2'; + nextResolved = fakeModel('qwen3_vl_encoder', 'krea-2'); + const store = makeStore(); + + const parsed = await ImageMetadataHandlers.Krea2Qwen3VlEncoderModel.parse( + { model: fakeModel('main', 'krea-2'), qwen3_vl_encoder: nextResolved }, + store + ); + + expect(parsed.key).toBe('qwen3_vl_encoder-key'); + expect(parsed.type).toBe('qwen3_vl_encoder'); + }); + + it('rejects parsing when the current main model is not Krea-2', async () => { + currentBase = 'flux'; + nextResolved = fakeModel('qwen3_vl_encoder', 'krea-2'); + const store = makeStore(); + + await expect( + ImageMetadataHandlers.Krea2Qwen3VlEncoderModel.parse({ qwen3_vl_encoder: nextResolved }, store) + ).rejects.toThrow(); + }); + + it('rejects encoder metadata from a non-Krea-2 image even when Krea-2 is currently selected', async () => { + currentBase = 'krea-2'; + nextResolved = fakeModel('qwen3_vl_encoder', 'any'); + const store = makeStore(); + + await expect( + ImageMetadataHandlers.Krea2Qwen3VlEncoderModel.parse( + { model: fakeModel('qwen3_vl_encoder', 'flux'), qwen3_vl_encoder: nextResolved }, + store + ) + ).rejects.toThrow(); + }); + }); + + // The conditioning-enhancer settings are Krea-2-only scalars. Their parse is gated on the current base so + // recalling an unrelated (older / non-Krea-2) image does NOT clobber the user's hidden enhancer state. + // The base check throws synchronously, which the parse runner turns into a rejected promise. + describe('conditioning-enhancer gating', () => { + const enhancerCases = [ + { handler: 'Krea2SeedVarianceEnabled', field: 'krea2_seed_variance_enabled', value: true }, + { handler: 'Krea2SeedVarianceStrength', field: 'krea2_seed_variance_strength', value: 20 }, + { handler: 'Krea2SeedVarianceRandomizePercent', field: 'krea2_seed_variance_randomize_percent', value: 50 }, + { handler: 'Krea2RebalanceEnabled', field: 'krea2_rebalance_enabled', value: true }, + { handler: 'Krea2RebalanceMultiplier', field: 'krea2_rebalance_multiplier', value: 4 }, + { handler: 'Krea2RebalanceWeights', field: 'krea2_rebalance_weights', value: '1,1,1,1,1,1,1,2.5,5,1.1,4,1' }, + ] as const; + + // The six handlers have different value types (boolean/number/string), so index into a loosely-typed + // view to keep the union of parse signatures callable. + const getHandler = (name: (typeof enhancerCases)[number]['handler']) => + ImageMetadataHandlers[name] as unknown as { + parse: (metadata: Record, store: AppStore) => Promise; + }; + + it.each(enhancerCases)('$handler parses when the current base is Krea-2', async ({ handler, field, value }) => { + currentBase = 'krea-2'; + const store = makeStore(); + + const parsed = await getHandler(handler).parse({ model: { base: 'krea-2' }, [field]: value }, store); + + expect(parsed).toBe(value); + }); + + it.each(enhancerCases)( + '$handler rejects (does not clobber) when the current base is not Krea-2', + async ({ handler, field, value }) => { + currentBase = 'sdxl'; + const store = makeStore(); + + await expect( + Promise.resolve().then(() => getHandler(handler).parse({ model: { base: 'krea-2' }, [field]: value }, store)) + ).rejects.toThrow(); + } + ); + + it.each(enhancerCases)( + '$handler rejects metadata from another model base even when Krea-2 is selected', + async ({ handler, field, value }) => { + currentBase = 'krea-2'; + const store = makeStore(); + + await expect( + Promise.resolve().then(() => getHandler(handler).parse({ model: { base: 'sdxl' }, [field]: value }, store)) + ).rejects.toThrow(); + } + ); + }); +}); diff --git a/invokeai/frontend/web/src/features/metadata/parsing.tsx b/invokeai/frontend/web/src/features/metadata/parsing.tsx index bc2623045e1..129e1fc995a 100644 --- a/invokeai/frontend/web/src/features/metadata/parsing.tsx +++ b/invokeai/frontend/web/src/features/metadata/parsing.tsx @@ -16,6 +16,8 @@ import { imageSizeChanged, kleinQwen3EncoderModelSelected, kleinVaeModelSelected, + krea2Qwen3VlEncoderModelSelected, + krea2VaeModelSelected, negativePromptChanged, openaiBackgroundChanged, openaiInputFidelityChanged, @@ -40,6 +42,12 @@ import { setFluxScheduler, setGuidance, setImg2imgStrength, + setKrea2RebalanceEnabled, + setKrea2RebalanceMultiplier, + setKrea2RebalanceWeights, + setKrea2SeedVarianceEnabled, + setKrea2SeedVarianceRandomizePercent, + setKrea2SeedVarianceStrength, setRefinerCFGScale, setRefinerNegativeAestheticScore, setRefinerPositiveAestheticScore, @@ -151,6 +159,12 @@ const getProperty = (obj: unknown, path: string): unknown => { return get(obj, path) as unknown; }; +const assertMetadataModelBase = (metadata: unknown, expectedBase: string, handlerType: string): void => { + const rawModel = getProperty(metadata, 'model'); + const modelBase = (rawModel as { base?: unknown } | undefined)?.base; + assert(modelBase === expectedBase, `${handlerType} handler only works with ${expectedBase} metadata`); +}; + type UnparsedData = { isParsed: false; isSuccess: false; @@ -1059,9 +1073,12 @@ const VAEModel: SingleMetadataHandler = { const parsed = await parseModelIdentifier(raw, store, 'vae'); assert(parsed.type === 'vae'); assert(isCompatibleWithMainModel(parsed, store)); - // Z-Image and FLUX.2 Klein have dedicated VAE handlers; avoid rendering a duplicate row. + // Z-Image, FLUX.2 Klein and Krea-2 have dedicated VAE handlers; avoid rendering a duplicate row. const base = selectBase(store.getState()); - assert(base !== 'z-image' && base !== 'flux2', 'VAEModel handler does not apply to Z-Image or FLUX.2 Klein'); + assert( + base !== 'z-image' && base !== 'flux2' && base !== 'krea-2', + 'VAEModel handler does not apply to Z-Image, FLUX.2 Klein or Krea-2' + ); return Promise.resolve(parsed); }, recall: (value, store) => { @@ -1151,6 +1168,179 @@ const ZImageQwen3SourceModel: SingleMetadataHandler = { }; //#endregion ZImageQwen3SourceModel +//#region Krea2VAEModel +const Krea2VAEModel: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2VAEModel', + parse: async (metadata, store) => { + assertMetadataModelBase(metadata, 'krea-2', 'Krea2VAEModel'); + const raw = getProperty(metadata, 'vae'); + const parsed = await parseModelIdentifier(raw, store, 'vae'); + assert(parsed.type === 'vae'); + assert(parsed.base === 'qwen-image' || parsed.base === 'anima', 'Krea2VAEModel requires a Qwen Image or Anima VAE'); + // Only recall if the current main model is Krea-2 (its VAE dropdown differs from other bases). + const base = selectBase(store.getState()); + assert(base === 'krea-2', 'Krea2VAEModel handler only works with Krea-2 models'); + return Promise.resolve(parsed); + }, + recall: (value, store) => { + store.dispatch(krea2VaeModelSelected(value)); + }, + i18nKey: 'metadata.vae', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => ( + + ), +}; +//#endregion Krea2VAEModel + +//#region Krea2Qwen3VlEncoderModel +const Krea2Qwen3VlEncoderModel: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2Qwen3VlEncoderModel', + parse: async (metadata, store) => { + assertMetadataModelBase(metadata, 'krea-2', 'Krea2Qwen3VlEncoderModel'); + const raw = getProperty(metadata, 'qwen3_vl_encoder'); + const parsed = await parseModelIdentifier(raw, store, 'qwen3_vl_encoder'); + assert(parsed.type === 'qwen3_vl_encoder'); + const base = selectBase(store.getState()); + assert(base === 'krea-2', 'Krea2Qwen3VlEncoderModel handler only works with Krea-2 models'); + return Promise.resolve(parsed); + }, + recall: (value, store) => { + store.dispatch(krea2Qwen3VlEncoderModelSelected(value)); + }, + i18nKey: 'metadata.krea2Qwen3VlEncoder', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => ( + + ), +}; +//#endregion Krea2Qwen3VlEncoderModel + +//#region Krea2SeedVarianceEnabled +const Krea2SeedVarianceEnabled: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2SeedVarianceEnabled', + parse: (metadata, store) => { + // Only applies to Krea-2 models, and only when the field is actually present — otherwise recalling + // an unrelated/older image would silently clear the user's current enhancer state. (A synchronous + // throw here is turned into a rejected promise by the parse runner, skipping the handler.) + assert(selectBase(store.getState()) === 'krea-2', 'Krea2SeedVarianceEnabled handler only applies to Krea-2 models'); + assertMetadataModelBase(metadata, 'krea-2', 'Krea2SeedVarianceEnabled'); + const raw = getProperty(metadata, 'krea2_seed_variance_enabled'); + return Promise.resolve(z.boolean().parse(raw)); + }, + recall: (value, store) => { + store.dispatch(setKrea2SeedVarianceEnabled(value)); + }, + i18nKey: 'metadata.seedVarianceEnabled', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => , +}; +//#endregion Krea2SeedVarianceEnabled + +//#region Krea2SeedVarianceStrength +const Krea2SeedVarianceStrength: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2SeedVarianceStrength', + parse: (metadata, store) => { + assert( + selectBase(store.getState()) === 'krea-2', + 'Krea2SeedVarianceStrength handler only applies to Krea-2 models' + ); + assertMetadataModelBase(metadata, 'krea-2', 'Krea2SeedVarianceStrength'); + const raw = getProperty(metadata, 'krea2_seed_variance_strength'); + return Promise.resolve(z.number().min(0).max(100).parse(raw)); + }, + recall: (value, store) => { + store.dispatch(setKrea2SeedVarianceStrength(value)); + }, + i18nKey: 'metadata.seedVarianceStrength', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => , +}; +//#endregion Krea2SeedVarianceStrength + +//#region Krea2SeedVarianceRandomizePercent +const Krea2SeedVarianceRandomizePercent: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2SeedVarianceRandomizePercent', + parse: (metadata, store) => { + assert( + selectBase(store.getState()) === 'krea-2', + 'Krea2SeedVarianceRandomizePercent handler only applies to Krea-2 models' + ); + assertMetadataModelBase(metadata, 'krea-2', 'Krea2SeedVarianceRandomizePercent'); + const raw = getProperty(metadata, 'krea2_seed_variance_randomize_percent'); + return Promise.resolve(z.number().min(1).max(100).parse(raw)); + }, + recall: (value, store) => { + store.dispatch(setKrea2SeedVarianceRandomizePercent(value)); + }, + i18nKey: 'metadata.seedVarianceRandomizePercent', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => , +}; +//#endregion Krea2SeedVarianceRandomizePercent + +//#region Krea2RebalanceEnabled +const Krea2RebalanceEnabled: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2RebalanceEnabled', + parse: (metadata, store) => { + assert(selectBase(store.getState()) === 'krea-2', 'Krea2RebalanceEnabled handler only applies to Krea-2 models'); + assertMetadataModelBase(metadata, 'krea-2', 'Krea2RebalanceEnabled'); + const raw = getProperty(metadata, 'krea2_rebalance_enabled'); + return Promise.resolve(z.boolean().parse(raw)); + }, + recall: (value, store) => { + store.dispatch(setKrea2RebalanceEnabled(value)); + }, + i18nKey: 'metadata.krea2RebalanceEnabled', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => , +}; +//#endregion Krea2RebalanceEnabled + +//#region Krea2RebalanceMultiplier +const Krea2RebalanceMultiplier: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2RebalanceMultiplier', + parse: (metadata, store) => { + assert(selectBase(store.getState()) === 'krea-2', 'Krea2RebalanceMultiplier handler only applies to Krea-2 models'); + assertMetadataModelBase(metadata, 'krea-2', 'Krea2RebalanceMultiplier'); + const raw = getProperty(metadata, 'krea2_rebalance_multiplier'); + return Promise.resolve(z.number().min(0).max(20).parse(raw)); + }, + recall: (value, store) => { + store.dispatch(setKrea2RebalanceMultiplier(value)); + }, + i18nKey: 'metadata.krea2RebalanceMultiplier', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => , +}; +//#endregion Krea2RebalanceMultiplier + +//#region Krea2RebalanceWeights +const Krea2RebalanceWeights: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'Krea2RebalanceWeights', + parse: (metadata, store) => { + assert(selectBase(store.getState()) === 'krea-2', 'Krea2RebalanceWeights handler only applies to Krea-2 models'); + assertMetadataModelBase(metadata, 'krea-2', 'Krea2RebalanceWeights'); + const raw = getProperty(metadata, 'krea2_rebalance_weights'); + return Promise.resolve(z.string().parse(raw)); + }, + recall: (value, store) => { + store.dispatch(setKrea2RebalanceWeights(value)); + }, + i18nKey: 'metadata.krea2RebalanceWeights', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => , +}; +//#endregion Krea2RebalanceWeights + //#region AnimaVAEModel const AnimaVAEModel: SingleMetadataHandler = { [SingleMetadataKey]: true, @@ -1649,6 +1839,14 @@ export const ImageMetadataHandlers = { ZImageSeedVarianceEnabled, ZImageSeedVarianceStrength, ZImageSeedVarianceRandomizePercent, + Krea2VAEModel, + Krea2Qwen3VlEncoderModel, + Krea2SeedVarianceEnabled, + Krea2SeedVarianceStrength, + Krea2SeedVarianceRandomizePercent, + Krea2RebalanceEnabled, + Krea2RebalanceMultiplier, + Krea2RebalanceWeights, QwenImageComponentSource, QwenImageVaeModel, QwenImageQwenVLEncoderModel, diff --git a/invokeai/frontend/web/src/features/modelManagerV2/models.ts b/invokeai/frontend/web/src/features/modelManagerV2/models.ts index cf295c9af6a..c2be1d0857c 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/models.ts +++ b/invokeai/frontend/web/src/features/modelManagerV2/models.ts @@ -13,6 +13,7 @@ import { isLoRAModelConfig, isNonRefinerMainModelConfig, isQwen3EncoderModelConfig, + isQwen3VLEncoderModelConfig, isQwenVLEncoderModelConfig, isRefinerMainModelModelConfig, isSigLipModelConfig, @@ -85,6 +86,11 @@ const MODEL_CATEGORIES: Record = { i18nKey: 'modelManager.qwenVLEncoder', filter: isQwenVLEncoderModelConfig, }, + qwen3_vl_encoder: { + category: 'qwen3_vl_encoder', + i18nKey: 'modelManager.qwen3VLEncoder', + filter: isQwen3VLEncoderModelConfig, + }, control_lora: { category: 'control_lora', i18nKey: 'modelManager.controlLora', @@ -163,6 +169,7 @@ export const MODEL_BASE_TO_COLOR: Record = { cogview4: 'red', 'qwen-image': 'orange', 'z-image': 'cyan', + 'krea-2': 'pink', external: 'orange', anima: 'invokePurple', unknown: 'red', @@ -187,6 +194,7 @@ export const MODEL_TYPE_TO_LONG_NAME: Record = { t5_encoder: 'T5 Encoder', qwen3_encoder: 'Qwen3 Encoder', qwen_vl_encoder: 'Qwen2.5-VL Encoder', + qwen3_vl_encoder: 'Qwen3-VL Encoder', clip_embed: 'CLIP Embed', siglip: 'SigLIP', flux_redux: 'FLUX Redux', @@ -210,6 +218,7 @@ export const MODEL_BASE_TO_LONG_NAME: Record = { cogview4: 'CogView4', 'qwen-image': 'Qwen Image', 'z-image': 'Z-Image', + 'krea-2': 'Krea-2', external: 'External', anima: 'Anima', unknown: 'Unknown', @@ -230,6 +239,7 @@ export const MODEL_BASE_TO_SHORT_NAME: Record = { cogview4: 'CogView4', 'qwen-image': 'QwenImg', 'z-image': 'Z-Image', + 'krea-2': 'Krea-2', external: 'External', anima: 'Anima', unknown: 'Unknown', @@ -248,6 +258,8 @@ export const MODEL_VARIANT_TO_LONG_NAME: Record = { klein_9b_base: 'FLUX.2 Klein 9B Base', turbo: 'Z-Image Turbo', zbase: 'Z-Image Base', + krea2_turbo: 'Krea-2 Turbo', + krea2_base: 'Krea-2 Raw', large: 'CLIP L', gigantic: 'CLIP G', generate: 'Qwen Image', @@ -271,6 +283,7 @@ export const MODEL_FORMAT_TO_LONG_NAME: Record = { t5_encoder: 'T5 Encoder', qwen3_encoder: 'Qwen3 Encoder', qwen_vl_encoder: 'Qwen2.5-VL Encoder', + qwen3_vl_encoder: 'Qwen3-VL Encoder', bnb_quantized_int8b: 'BNB Quantized (int8b)', bnb_quantized_nf4b: 'BNB Quantized (nf4b)', gguf_quantized: 'GGUF Quantized', @@ -290,4 +303,5 @@ export const SUPPORTS_NEGATIVE_PROMPT_BASE_MODELS: BaseModelType[] = [ 'sd-3', 'z-image', 'anima', + 'krea-2', ]; diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx index 71d2efe0e45..56bcc4c3afb 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx @@ -16,6 +16,7 @@ const FORMAT_NAME_MAP: Record = { t5_encoder: 't5_encoder', qwen3_encoder: 'qwen3_encoder', qwen_vl_encoder: 'qwen_vl_encoder', + qwen3_vl_encoder: 'qwen3_vl_encoder', bnb_quantized_int8b: 'bnb_quantized_int8b', bnb_quantized_nf4b: 'quantized', gguf_quantized: 'gguf', @@ -37,6 +38,7 @@ const FORMAT_COLOR_MAP: Record = { t5_encoder: 'base', qwen3_encoder: 'base', qwen_vl_encoder: 'base', + qwen3_vl_encoder: 'base', bnb_quantized_int8b: 'base', bnb_quantized_nf4b: 'base', gguf_quantized: 'base', diff --git a/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts b/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts index 04e0fea2cc9..373f95f6f59 100644 --- a/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts +++ b/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts @@ -14,6 +14,7 @@ import type { zClipVariantType, zFlux2VariantType, zFluxVariantType, + zKrea2VariantType, zModelFormat, zModelVariantType, zQwen3VariantType, @@ -52,6 +53,7 @@ describe('Common types', () => { test('FluxVariantType', () => assert, S['FluxVariantType']>>()); test('Flux2VariantType', () => assert, S['Flux2VariantType']>>()); test('ZImageVariantType', () => assert, S['ZImageVariantType']>>()); + test('Krea2VariantType', () => assert, S['Krea2VariantType']>>()); test('Qwen3VariantType', () => assert, S['Qwen3VariantType']>>()); test('ModelFormat', () => assert, S['ModelFormat']>>()); diff --git a/invokeai/frontend/web/src/features/nodes/types/common.ts b/invokeai/frontend/web/src/features/nodes/types/common.ts index fb2a1ce946a..d69d3fc6071 100644 --- a/invokeai/frontend/web/src/features/nodes/types/common.ts +++ b/invokeai/frontend/web/src/features/nodes/types/common.ts @@ -98,6 +98,7 @@ export const zBaseModelType = z.enum([ 'cogview4', 'qwen-image', 'z-image', + 'krea-2', 'external', 'anima', 'unknown', @@ -113,6 +114,7 @@ export const zMainModelBase = z.enum([ 'cogview4', 'qwen-image', 'z-image', + 'krea-2', 'anima', ]); type MainModelBase = z.infer; @@ -134,6 +136,7 @@ export const zModelType = z.enum([ 't5_encoder', 'qwen3_encoder', 'qwen_vl_encoder', + 'qwen3_vl_encoder', 'clip_embed', 'siglip', 'flux_redux', @@ -162,6 +165,7 @@ export const zModelVariantType = z.enum(['normal', 'inpaint', 'depth']); export const zFluxVariantType = z.enum(['dev', 'dev_fill', 'schnell']); export const zFlux2VariantType = z.enum(['klein_4b', 'klein_4b_base', 'klein_9b', 'klein_9b_base']); export const zZImageVariantType = z.enum(['turbo', 'zbase']); +export const zKrea2VariantType = z.enum(['krea2_turbo', 'krea2_base']); const zQwenImageVariantType = z.enum(['generate', 'edit']); export const zQwen3VariantType = z.enum(['qwen3_4b', 'qwen3_8b', 'qwen3_06b']); export const zAnyModelVariant = z.union([ @@ -170,6 +174,7 @@ export const zAnyModelVariant = z.union([ zFluxVariantType, zFlux2VariantType, zZImageVariantType, + zKrea2VariantType, zQwenImageVariantType, zQwen3VariantType, ]); @@ -187,6 +192,7 @@ export const zModelFormat = z.enum([ 't5_encoder', 'qwen3_encoder', 'qwen_vl_encoder', + 'qwen3_vl_encoder', 'bnb_quantized_int8b', 'bnb_quantized_nf4b', 'gguf_quantized', diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts index f17ff970f27..bf7137013ac 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts @@ -71,6 +71,7 @@ export const addImageToImage = async ({ denoise.type === 'flux2_denoise' || denoise.type === 'sd3_denoise' || denoise.type === 'z_image_denoise' || + denoise.type === 'krea2_denoise' || denoise.type === 'anima_denoise' ) { denoise.width = scaledSize.width; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts index 51ac7c8d6b3..18cd2912894 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts @@ -72,6 +72,7 @@ export const addInpaint = async ({ denoise.type === 'flux2_denoise' || denoise.type === 'sd3_denoise' || denoise.type === 'z_image_denoise' || + denoise.type === 'krea2_denoise' || denoise.type === 'anima_denoise' ) { denoise.width = scaledSize.width; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.test.ts new file mode 100644 index 00000000000..0250fbf969d --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.test.ts @@ -0,0 +1,126 @@ +import type { Invocation } from 'services/api/types'; +import { describe, expect, it, vi } from 'vitest'; + +let nextId = 0; +vi.mock('features/controlLayers/konva/util', () => ({ + getPrefixedId: (prefix: string) => `${prefix}:${nextId++}`, +})); + +import { addKrea2LoRAs } from './addKrea2LoRAs'; +import { Graph } from './Graph'; + +const model = { key: 'krea2-model', hash: 'h', name: 'Krea-2', base: 'krea-2', type: 'main' }; + +const buildBaseGraph = (withNeg: boolean) => { + nextId = 0; + const g = new Graph('test'); + const modelLoader = g.addNode({ + type: 'krea2_model_loader', + id: 'model_loader', + model, + } as Invocation<'krea2_model_loader'>); + const denoise = g.addNode({ type: 'krea2_denoise', id: 'denoise' } as Invocation<'krea2_denoise'>); + const posCond = g.addNode({ type: 'krea2_text_encoder', id: 'pos_prompt' } as Invocation<'krea2_text_encoder'>); + const negCond = withNeg + ? g.addNode({ type: 'krea2_text_encoder', id: 'neg_prompt' } as Invocation<'krea2_text_encoder'>) + : null; + + // The direct model -> denoise / text-encoder edges that addKrea2LoRAs must reroute. + g.addEdge(modelLoader, 'transformer', denoise, 'transformer'); + g.addEdge(modelLoader, 'qwen3_vl_encoder', posCond, 'qwen3_vl_encoder'); + if (negCond) { + g.addEdge(modelLoader, 'qwen3_vl_encoder', negCond, 'qwen3_vl_encoder'); + } + + return { g, modelLoader, denoise, posCond, negCond }; +}; + +const stateWith = (loras: unknown[]) => ({ loras: { loras } }) as never; + +const enabledKrea2Lora = { + id: 'lora-1', + isEnabled: true, + weight: 0.8, + model: { key: 'lora-key', hash: 'lora-hash', name: 'My Krea LoRA', base: 'krea-2', type: 'lora' }, +}; + +describe('addKrea2LoRAs', () => { + it('reroutes the transformer and both text encoders through the collection loader', () => { + const { g, modelLoader, denoise, posCond, negCond } = buildBaseGraph(true); + + addKrea2LoRAs(stateWith([enabledKrea2Lora]), g, denoise, modelLoader, posCond, negCond); + + const graph = g.getGraph(); + const nodeTypes = Object.values(graph.nodes).map((n) => n.type); + expect(nodeTypes).toContain('krea2_lora_collection_loader'); + expect(nodeTypes).toContain('collect'); + expect(nodeTypes).toContain('lora_selector'); + + const collectionLoaderId = Object.values(graph.nodes).find((n) => n.type === 'krea2_lora_collection_loader')!.id; + + // The single edge into denoise.transformer now originates from the collection loader (old direct edge gone). + const transformerEdges = graph.edges.filter( + (e) => e.destination.node_id === denoise.id && e.destination.field === 'transformer' + ); + expect(transformerEdges).toHaveLength(1); + expect(transformerEdges[0]!.source.node_id).toBe(collectionLoaderId); + + // Both the positive and negative encoders are rerouted to the collection loader. + for (const cond of [posCond, negCond!]) { + const encoderEdges = graph.edges.filter( + (e) => e.destination.node_id === cond.id && e.destination.field === 'qwen3_vl_encoder' + ); + expect(encoderEdges).toHaveLength(1); + expect(encoderEdges[0]!.source.node_id).toBe(collectionLoaderId); + } + + // No stale direct edge remains from the model loader to denoise or the encoders. + const staleDirectEdges = graph.edges.filter( + (e) => + e.source.node_id === modelLoader.id && [denoise.id, posCond.id, negCond!.id].includes(e.destination.node_id) + ); + expect(staleDirectEdges).toHaveLength(0); + + // The lora selector feeds the collector, which feeds the collection loader. + const selectorId = Object.values(graph.nodes).find((n) => n.type === 'lora_selector')!.id; + const collectorId = Object.values(graph.nodes).find((n) => n.type === 'collect')!.id; + expect(graph.edges.some((e) => e.source.node_id === selectorId && e.destination.node_id === collectorId)).toBe( + true + ); + expect( + graph.edges.some((e) => e.source.node_id === collectorId && e.destination.node_id === collectionLoaderId) + ).toBe(true); + }); + + it('does nothing (keeps direct edges) when no Krea-2 LoRAs are enabled', () => { + const { g, modelLoader, denoise, posCond, negCond } = buildBaseGraph(true); + + addKrea2LoRAs(stateWith([]), g, denoise, modelLoader, posCond, negCond); + + const graph = g.getGraph(); + expect(Object.values(graph.nodes).map((n) => n.type)).not.toContain('krea2_lora_collection_loader'); + const transformerEdges = graph.edges.filter( + (e) => e.destination.node_id === denoise.id && e.destination.field === 'transformer' + ); + expect(transformerEdges).toHaveLength(1); + expect(transformerEdges[0]!.source.node_id).toBe(modelLoader.id); + }); + + it('ignores disabled and non-Krea-2 LoRAs', () => { + const { g, modelLoader, denoise, posCond, negCond } = buildBaseGraph(false); + + addKrea2LoRAs( + stateWith([ + { ...enabledKrea2Lora, isEnabled: false }, + { ...enabledKrea2Lora, id: 'flux', model: { ...enabledKrea2Lora.model, base: 'flux' } }, + ]), + g, + denoise, + modelLoader, + posCond, + negCond + ); + + expect(Object.values(g.getGraph().nodes).map((n) => n.type)).not.toContain('krea2_lora_collection_loader'); + }); +}); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.ts new file mode 100644 index 00000000000..4fe06628282 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.ts @@ -0,0 +1,68 @@ +import type { RootState } from 'app/store/store'; +import { getPrefixedId } from 'features/controlLayers/konva/util'; +import { zModelIdentifierField } from 'features/nodes/types/common'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation, S } from 'services/api/types'; + +export const addKrea2LoRAs = ( + state: RootState, + g: Graph, + denoise: Invocation<'krea2_denoise'>, + modelLoader: Invocation<'krea2_model_loader'>, + posCond: Invocation<'krea2_text_encoder'>, + negCond: Invocation<'krea2_text_encoder'> | null +): void => { + const enabledLoRAs = state.loras.loras.filter((l) => l.isEnabled && l.model.base === 'krea-2'); + const loraCount = enabledLoRAs.length; + + if (loraCount === 0) { + return; + } + + const loraMetadata: S['LoRAMetadataField'][] = []; + + // Collect LoRAs into a collection node, then apply them all via the collection loader, which reroutes + // the transformer and Qwen3-VL encoder through itself. + const loraCollector = g.addNode({ + id: getPrefixedId('lora_collector'), + type: 'collect', + }); + const loraCollectionLoader = g.addNode({ + type: 'krea2_lora_collection_loader', + id: getPrefixedId('krea2_lora_collection_loader'), + }); + + g.addEdge(loraCollector, 'collection', loraCollectionLoader, 'loras'); + g.addEdge(modelLoader, 'transformer', loraCollectionLoader, 'transformer'); + g.addEdge(modelLoader, 'qwen3_vl_encoder', loraCollectionLoader, 'qwen3_vl_encoder'); + // Reroute model connections through the LoRA collection loader. + g.deleteEdgesTo(denoise, ['transformer']); + g.deleteEdgesTo(posCond, ['qwen3_vl_encoder']); + g.addEdge(loraCollectionLoader, 'transformer', denoise, 'transformer'); + g.addEdge(loraCollectionLoader, 'qwen3_vl_encoder', posCond, 'qwen3_vl_encoder'); + if (negCond !== null) { + g.deleteEdgesTo(negCond, ['qwen3_vl_encoder']); + g.addEdge(loraCollectionLoader, 'qwen3_vl_encoder', negCond, 'qwen3_vl_encoder'); + } + + for (const lora of enabledLoRAs) { + const { weight } = lora; + const parsedModel = zModelIdentifierField.parse(lora.model); + + const loraSelector = g.addNode({ + type: 'lora_selector', + id: getPrefixedId('lora_selector'), + lora: parsedModel, + weight, + }); + + loraMetadata.push({ + model: parsedModel, + weight, + }); + + g.addEdge(loraSelector, 'lora', loraCollector, 'item'); + } + + g.upsertMetadata({ loras: loraMetadata }); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts index fa362fb095e..675e336b5c4 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts @@ -65,6 +65,7 @@ export const addOutpaint = async ({ denoise.type === 'flux2_denoise' || denoise.type === 'sd3_denoise' || denoise.type === 'z_image_denoise' || + denoise.type === 'krea2_denoise' || denoise.type === 'anima_denoise' ) { denoise.width = scaledSize.width; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts index 06ece522da5..654b3167296 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts @@ -44,6 +44,7 @@ export const addTextToImage = ({ denoise.type === 'flux2_denoise' || denoise.type === 'sd3_denoise' || denoise.type === 'z_image_denoise' || + denoise.type === 'krea2_denoise' || denoise.type === 'anima_denoise' ) { denoise.width = scaledSize.width; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.test.ts new file mode 100644 index 00000000000..6dd52be25f0 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.test.ts @@ -0,0 +1,303 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +vi.mock('app/logging/logger', () => ({ + logger: () => ({ + debug: vi.fn(), + }), +})); + +let nextId = 0; +vi.mock('features/controlLayers/konva/util', () => ({ + getPrefixedId: (prefix: string) => `${prefix}:${nextId++}`, +})); + +const baseModel = { + key: 'krea2-model', + hash: 'krea2-hash', + name: 'Krea-2 Turbo', + base: 'krea-2', + type: 'main', + format: 'diffusers', + variant: 'krea2_turbo', +}; + +let model: Record = { ...baseModel }; + +const defaultParams = { + cfgScale: 1 as number | number[], + steps: 8, + krea2VaeModel: null as unknown, + krea2Qwen3VlEncoderModel: null as unknown, + krea2RebalanceEnabled: false, + krea2RebalanceMultiplier: 4, + krea2RebalanceWeights: '1,1,1,1,1,1,1,2.5,5,1.1,4,1', + krea2SeedVarianceEnabled: false, + krea2SeedVarianceStrength: 20, + krea2SeedVarianceRandomizePercent: 50, +}; + +let params = { ...defaultParams }; + +vi.mock('features/controlLayers/store/paramsSlice', () => ({ + selectMainModelConfig: vi.fn(() => model), + selectParamsSlice: vi.fn(() => params), +})); + +vi.mock('features/controlLayers/store/selectors', () => ({ + selectCanvasMetadata: vi.fn(() => ({})), +})); + +vi.mock('features/metadata/util/modelFetchingHelpers', () => ({ + fetchModelConfigWithTypeGuard: vi.fn(() => Promise.resolve(model)), +})); + +vi.mock('features/nodes/util/graph/generation/addImageToImage', () => ({ + addImageToImage: vi.fn(({ l2i }) => Promise.resolve(l2i)), +})); + +vi.mock('features/nodes/util/graph/generation/addInpaint', () => ({ + addInpaint: vi.fn(({ l2i }) => Promise.resolve(l2i)), +})); + +vi.mock('features/nodes/util/graph/generation/addOutpaint', () => ({ + addOutpaint: vi.fn(({ l2i }) => Promise.resolve(l2i)), +})); + +vi.mock('features/nodes/util/graph/generation/addKrea2LoRAs', () => ({ + addKrea2LoRAs: vi.fn(), +})); + +vi.mock('features/nodes/util/graph/generation/addNSFWChecker', () => ({ + addNSFWChecker: vi.fn((_g, node) => node), +})); + +vi.mock('features/nodes/util/graph/generation/addWatermarker', () => ({ + addWatermarker: vi.fn((_g, node) => node), +})); + +vi.mock('features/nodes/util/graph/generation/addTextToImage', () => ({ + addTextToImage: vi.fn(({ l2i }) => l2i), +})); + +vi.mock('features/nodes/util/graph/graphBuilderUtils', () => ({ + selectCanvasOutputFields: vi.fn(() => ({})), + selectPresetModifiedPrompts: vi.fn(() => ({ + positive: 'a prompt', + negative: 'a negative prompt', + })), +})); + +vi.mock('features/ui/store/uiSelectors', () => ({ + selectActiveTab: vi.fn(() => 'generation'), +})); + +vi.mock('services/api/types', async () => { + const actual = await vi.importActual('services/api/types'); + return { + ...actual, + isNonRefinerMainModelConfig: vi.fn(() => true), + }; +}); + +import { addImageToImage } from './addImageToImage'; +import { addInpaint } from './addInpaint'; +import { addOutpaint } from './addOutpaint'; +import { buildKrea2Graph } from './buildKrea2Graph'; + +type BuiltGraph = Awaited>['g']; + +const buildTxt2Img = () => + buildKrea2Graph({ + generationMode: 'txt2img', + manager: null, + state: { + system: { shouldUseNSFWChecker: false, shouldUseWatermarker: false }, + } as never, + }); + +const buildCanvasMode = (generationMode: 'img2img' | 'inpaint' | 'outpaint') => + buildKrea2Graph({ + generationMode, + manager: { id: 'manager' } as never, + state: { + system: { shouldUseNSFWChecker: false, shouldUseWatermarker: false }, + } as never, + }); + +const nodeTypesOf = (g: BuiltGraph): string[] => Object.values(g.getGraph().nodes).map((n) => n.type); +const posConditioningEdge = (g: BuiltGraph) => + g.getGraph().edges.find((e) => e.destination.field === 'positive_conditioning'); + +describe('buildKrea2Graph', () => { + afterEach(() => { + nextId = 0; + params = { ...defaultParams }; + model = { ...baseModel }; + }); + + it('builds the core txt2img node chain', async () => { + const { g } = await buildTxt2Img(); + const types = nodeTypesOf(g); + expect(types).toContain('krea2_model_loader'); + expect(types).toContain('krea2_text_encoder'); + expect(types).toContain('krea2_denoise'); + // Krea-2 decodes with the Qwen-Image VAE node. + expect(types).toContain('qwen_image_l2i'); + }); + + it.each([ + ['img2img', addImageToImage], + ['inpaint', addInpaint], + ['outpaint', addOutpaint], + ] as const)('builds the %s graph through its canvas integration', async (mode, integration) => { + const { g } = await buildCanvasMode(mode); + + expect(integration).toHaveBeenCalledOnce(); + expect(nodeTypesOf(g)).toContain('qwen_image_i2l'); + expect((g.getMetadataNode() as unknown as Record).generation_mode).toBe(`krea2_${mode}`); + }); + + describe('CFG gating (negative conditioning)', () => { + // Krea-2 only adds a negative prompt + negative_conditioning edge when CFG is enabled (cfg_scale > 1). + // The distilled Turbo checkpoint runs with CFG off (cfg_scale 1.0), so recording/encoding a negative + // prompt would be wasted work. + it('omits the negative prompt + edge when cfg_scale <= 1 (distilled Turbo default)', async () => { + params = { ...defaultParams, cfgScale: 1 }; + const { g } = await buildTxt2Img(); + const graph = g.getGraph(); + const hasNegPromptNode = Object.keys(graph.nodes).some((id) => id.startsWith('neg_prompt:')); + const hasNegEdge = graph.edges.some((e) => e.destination.field === 'negative_conditioning'); + expect(hasNegPromptNode).toBe(false); + expect(hasNegEdge).toBe(false); + }); + + it('includes the negative prompt + edge when cfg_scale > 1 (Raw / CFG on)', async () => { + params = { ...defaultParams, cfgScale: 4.5 }; + const { g } = await buildTxt2Img(); + const graph = g.getGraph(); + const hasNegPromptNode = Object.keys(graph.nodes).some((id) => id.startsWith('neg_prompt:')); + const hasNegEdge = graph.edges.some((e) => e.destination.field === 'negative_conditioning'); + expect(hasNegPromptNode).toBe(true); + expect(hasNegEdge).toBe(true); + }); + }); + + describe('conditioning enhancers', () => { + it('inserts no enhancer nodes by default; positive conditioning flows straight to denoise', async () => { + const { g } = await buildTxt2Img(); + const types = nodeTypesOf(g); + expect(types).not.toContain('krea2_conditioning_rebalance'); + expect(types).not.toContain('krea2_seed_variance'); + // The edge into denoise.positive_conditioning comes directly from the text encoder. + const edge = posConditioningEdge(g); + expect(edge).toBeDefined(); + expect(edge!.source.node_id.startsWith('pos_prompt:')).toBe(true); + }); + + it('inserts the rebalance node and reroutes positive conditioning through it when enabled', async () => { + params = { ...defaultParams, krea2RebalanceEnabled: true }; + const { g } = await buildTxt2Img(); + const types = nodeTypesOf(g); + expect(types).toContain('krea2_conditioning_rebalance'); + expect(types).not.toContain('krea2_seed_variance'); + const edge = posConditioningEdge(g); + expect(edge!.source.node_id.startsWith('krea2_rebalance:')).toBe(true); + }); + + it('inserts the seed-variance node when enabled with strength > 0', async () => { + params = { ...defaultParams, krea2SeedVarianceEnabled: true, krea2SeedVarianceStrength: 20 }; + const { g } = await buildTxt2Img(); + expect(nodeTypesOf(g)).toContain('krea2_seed_variance'); + const edge = posConditioningEdge(g); + expect(edge!.source.node_id.startsWith('krea2_seed_variance:')).toBe(true); + }); + + it('does not insert the seed-variance node when strength is 0 (a no-op)', async () => { + params = { ...defaultParams, krea2SeedVarianceEnabled: true, krea2SeedVarianceStrength: 0 }; + const { g } = await buildTxt2Img(); + expect(nodeTypesOf(g)).not.toContain('krea2_seed_variance'); + }); + + it('chains rebalance -> seed-variance -> denoise when both are enabled', async () => { + params = { + ...defaultParams, + krea2RebalanceEnabled: true, + krea2SeedVarianceEnabled: true, + krea2SeedVarianceStrength: 20, + }; + const { g } = await buildTxt2Img(); + const graph = g.getGraph(); + const types = nodeTypesOf(g); + expect(types).toContain('krea2_conditioning_rebalance'); + expect(types).toContain('krea2_seed_variance'); + // rebalance -> seed_variance + const rebalanceToSeed = graph.edges.find( + (e) => + e.source.node_id.startsWith('krea2_rebalance:') && e.destination.node_id.startsWith('krea2_seed_variance:') + ); + expect(rebalanceToSeed).toBeDefined(); + // seed_variance -> denoise.positive_conditioning + const edge = posConditioningEdge(g); + expect(edge!.source.node_id.startsWith('krea2_seed_variance:')).toBe(true); + }); + }); + + describe('standalone components for non-diffusers transformers', () => { + // A single-file / GGUF transformer has no bundled VAE or encoder, so both standalone submodels are + // required. A Diffusers pipeline bundles them, so it needs neither. + it('throws when a single-file/GGUF transformer has no VAE selected', async () => { + model = { ...baseModel, format: 'gguf_quantized' }; + params = { ...defaultParams, krea2VaeModel: null, krea2Qwen3VlEncoderModel: null }; + await expect(buildTxt2Img()).rejects.toThrow(/require a VAE/); + }); + + it('throws when a single-file/GGUF transformer has no Qwen3-VL encoder selected', async () => { + model = { ...baseModel, format: 'gguf_quantized' }; + params = { + ...defaultParams, + krea2VaeModel: { key: 'vae', hash: 'h', name: 'VAE', base: 'qwen-image', type: 'vae' }, + krea2Qwen3VlEncoderModel: null, + }; + await expect(buildTxt2Img()).rejects.toThrow(/require a Qwen3-VL encoder/); + }); + + it('passes the standalone submodels to the model loader when provided', async () => { + model = { ...baseModel, format: 'gguf_quantized' }; + params = { + ...defaultParams, + krea2VaeModel: { key: 'vae', hash: 'h', name: 'VAE', base: 'qwen-image', type: 'vae' }, + krea2Qwen3VlEncoderModel: { key: 'enc', hash: 'h', name: 'Enc', base: 'any', type: 'qwen3_vl_encoder' }, + }; + const { g } = await buildTxt2Img(); + const loader = Object.values(g.getGraph().nodes).find((n) => n.type === 'krea2_model_loader') as + | { vae_model?: { key: string }; qwen3_vl_encoder_model?: { key: string } } + | undefined; + expect(loader?.vae_model).toMatchObject({ key: 'vae' }); + expect(loader?.qwen3_vl_encoder_model).toMatchObject({ key: 'enc' }); + }); + }); + + describe('metadata', () => { + it('records the conditioning-enhancer settings and generation mode', async () => { + params = { + ...defaultParams, + krea2RebalanceEnabled: true, + krea2RebalanceMultiplier: 4, + krea2SeedVarianceEnabled: false, + }; + const { g } = await buildTxt2Img(); + const metadata = g.getMetadataNode() as unknown as Record; + expect(metadata.krea2_rebalance_enabled).toBe(true); + expect(metadata.krea2_rebalance_multiplier).toBe(4); + expect(metadata.krea2_seed_variance_enabled).toBe(false); + expect(metadata.generation_mode).toBe('krea2_txt2img'); + }); + + it('does not record a negative prompt for the CFG-off (Turbo) default', async () => { + params = { ...defaultParams, cfgScale: 1 }; + const { g } = await buildTxt2Img(); + const metadata = g.getMetadataNode() as unknown as Record; + expect(metadata.negative_prompt).toBeUndefined(); + }); + }); +}); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.ts new file mode 100644 index 00000000000..7f208ea9d93 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.ts @@ -0,0 +1,252 @@ +import { logger } from 'app/logging/logger'; +import { getPrefixedId } from 'features/controlLayers/konva/util'; +import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; +import { selectCanvasMetadata } from 'features/controlLayers/store/selectors'; +import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; +import { addImageToImage } from 'features/nodes/util/graph/generation/addImageToImage'; +import { addInpaint } from 'features/nodes/util/graph/generation/addInpaint'; +import { addKrea2LoRAs } from 'features/nodes/util/graph/generation/addKrea2LoRAs'; +import { addNSFWChecker } from 'features/nodes/util/graph/generation/addNSFWChecker'; +import { addOutpaint } from 'features/nodes/util/graph/generation/addOutpaint'; +import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage'; +import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker'; +import { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { selectCanvasOutputFields, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { GraphBuilderArg, GraphBuilderReturn, ImageOutputNodes } from 'features/nodes/util/graph/types'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; +import type { Invocation } from 'services/api/types'; +import { isNonRefinerMainModelConfig } from 'services/api/types'; +import type { Equals } from 'tsafe'; +import { assert } from 'tsafe'; + +const log = logger('system'); + +export const buildKrea2Graph = async (arg: GraphBuilderArg): Promise => { + const { generationMode, state, manager } = arg; + + log.debug({ generationMode, manager: manager?.id }, 'Building Krea-2 graph'); + + const model = selectMainModelConfig(state); + assert(model, 'No model selected'); + assert(model.base === 'krea-2', 'Selected model is not a Krea-2 model'); + + const params = selectParamsSlice(state); + // Krea-2-Turbo uses the standard CFG convention; cfg_scale defaults to 1.0 (no CFG) for the distilled model. + const { + cfgScale: cfg_scale, + steps, + krea2VaeModel, + krea2Qwen3VlEncoderModel, + krea2RebalanceEnabled, + krea2RebalanceMultiplier, + krea2RebalanceWeights, + krea2SeedVarianceEnabled, + krea2SeedVarianceStrength, + krea2SeedVarianceRandomizePercent, + } = params; + + // Krea-2 has no source field: a non-diffusers transformer (single-file checkpoint / GGUF) has no + // bundled VAE or encoder, so both standalone submodels must be selected. (Also enforced in readiness.) + if (model.format !== 'diffusers') { + assert(krea2VaeModel, 'Krea-2 non-diffusers models require a VAE to be selected'); + assert(krea2Qwen3VlEncoderModel, 'Krea-2 non-diffusers models require a Qwen3-VL encoder to be selected'); + } + + const prompts = selectPresetModifiedPrompts(state); + + const g = new Graph(getPrefixedId('krea2_graph')); + + const modelLoader = g.addNode({ + type: 'krea2_model_loader', + id: getPrefixedId('krea2_model_loader'), + model, + // Optional standalone submodels (used when the transformer is a single-file checkpoint/GGUF). When + // unset, the loader extracts the VAE / Qwen3-VL encoder from the diffusers model. + vae_model: krea2VaeModel ?? undefined, + qwen3_vl_encoder_model: krea2Qwen3VlEncoderModel ?? undefined, + }); + + const positivePrompt = g.addNode({ + id: getPrefixedId('positive_prompt'), + type: 'string', + }); + const posCond = g.addNode({ + type: 'krea2_text_encoder', + id: getPrefixedId('pos_prompt'), + }); + + // Krea-2 supports negative conditioning only when CFG is enabled (cfg_scale > 1). + let negCond: Invocation<'krea2_text_encoder'> | null = null; + if (cfg_scale > 1) { + negCond = g.addNode({ + type: 'krea2_text_encoder', + id: getPrefixedId('neg_prompt'), + prompt: prompts.negative, + }); + } + + const seed = g.addNode({ + id: getPrefixedId('seed'), + type: 'integer', + }); + const denoise = g.addNode({ + type: 'krea2_denoise', + id: getPrefixedId('denoise_latents'), + cfg_scale, + steps, + }); + // Krea-2 decodes with the Qwen-Image VAE, so reuse the Qwen-Image latents-to-image node. + const l2i = g.addNode({ + type: 'qwen_image_l2i', + id: getPrefixedId('l2i'), + }); + + g.addEdge(modelLoader, 'transformer', denoise, 'transformer'); + g.addEdge(modelLoader, 'qwen3_vl_encoder', posCond, 'qwen3_vl_encoder'); + g.addEdge(modelLoader, 'vae', l2i, 'vae'); + + g.addEdge(positivePrompt, 'value', posCond, 'prompt'); + + // Optional conditioning enhancers between the text encoder and denoise. Both default OFF (params), so + // by default the conditioning flows straight through and stock Krea-2 behaviour is unchanged. Order: + // rebalance (scale the signal toward the prompt) first, then seed variance (perturb for variety). + if (krea2RebalanceEnabled) { + const rebalance = g.addNode({ + type: 'krea2_conditioning_rebalance', + id: getPrefixedId('krea2_rebalance'), + multiplier: krea2RebalanceMultiplier, + per_layer_weights: krea2RebalanceWeights, + }); + g.addEdge(posCond, 'conditioning', rebalance, 'conditioning'); + + if (krea2SeedVarianceEnabled && krea2SeedVarianceStrength > 0) { + const seedVariance = g.addNode({ + type: 'krea2_seed_variance', + id: getPrefixedId('krea2_seed_variance'), + strength: krea2SeedVarianceStrength, + randomize_percent: krea2SeedVarianceRandomizePercent, + }); + g.addEdge(rebalance, 'conditioning', seedVariance, 'conditioning'); + g.addEdge(seed, 'value', seedVariance, 'variance_seed'); + g.addEdge(seedVariance, 'conditioning', denoise, 'positive_conditioning'); + } else { + g.addEdge(rebalance, 'conditioning', denoise, 'positive_conditioning'); + } + } else if (krea2SeedVarianceEnabled && krea2SeedVarianceStrength > 0) { + const seedVariance = g.addNode({ + type: 'krea2_seed_variance', + id: getPrefixedId('krea2_seed_variance'), + strength: krea2SeedVarianceStrength, + randomize_percent: krea2SeedVarianceRandomizePercent, + }); + g.addEdge(posCond, 'conditioning', seedVariance, 'conditioning'); + g.addEdge(seed, 'value', seedVariance, 'variance_seed'); + g.addEdge(seedVariance, 'conditioning', denoise, 'positive_conditioning'); + } else { + g.addEdge(posCond, 'conditioning', denoise, 'positive_conditioning'); + } + + if (negCond !== null) { + g.addEdge(modelLoader, 'qwen3_vl_encoder', negCond, 'qwen3_vl_encoder'); + g.addEdge(negCond, 'conditioning', denoise, 'negative_conditioning'); + } + + g.addEdge(seed, 'value', denoise, 'seed'); + g.addEdge(denoise, 'latents', l2i, 'latents'); + + // Apply any enabled Krea-2 LoRAs (reroutes transformer + Qwen3-VL encoder through the collection loader). + addKrea2LoRAs(state, g, denoise, modelLoader, posCond, negCond); + + const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig); + assert(modelConfig.base === 'krea-2'); + + g.upsertMetadata({ + cfg_scale, + model: Graph.getModelMetadataField(modelConfig), + steps, + // Standalone submodels (used for single-file / GGUF transformers) - recorded so they recall. + vae: krea2VaeModel ?? undefined, + qwen3_vl_encoder: krea2Qwen3VlEncoderModel ?? undefined, + // Conditioning enhancer settings (default off) - recorded so they recall. + krea2_seed_variance_enabled: krea2SeedVarianceEnabled, + krea2_seed_variance_strength: krea2SeedVarianceStrength, + krea2_seed_variance_randomize_percent: krea2SeedVarianceRandomizePercent, + krea2_rebalance_enabled: krea2RebalanceEnabled, + krea2_rebalance_multiplier: krea2RebalanceMultiplier, + krea2_rebalance_weights: krea2RebalanceWeights, + }); + // Only record a negative prompt when CFG is enabled (cfg_scale > 1). Krea-2-Turbo runs with CFG + // disabled by default, in which case there is no negative conditioning - recording it would surface a + // spurious (often empty) negative prompt on metadata recall. + if (cfg_scale > 1) { + g.upsertMetadata({ negative_prompt: prompts.negative }); + } + g.addEdgeToMetadata(seed, 'value', 'seed'); + g.addEdgeToMetadata(positivePrompt, 'value', 'positive_prompt'); + + let canvasOutput: Invocation = l2i; + + if (generationMode === 'txt2img') { + canvasOutput = addTextToImage({ g, state, denoise, l2i }); + g.upsertMetadata({ generation_mode: 'krea2_txt2img' }); + } else if (generationMode === 'img2img') { + assert(manager !== null); + const i2l = g.addNode({ type: 'qwen_image_i2l', id: getPrefixedId('qwen_image_i2l') }); + canvasOutput = await addImageToImage({ g, state, manager, denoise, l2i, i2l, vaeSource: modelLoader }); + g.upsertMetadata({ generation_mode: 'krea2_img2img' }); + } else if (generationMode === 'inpaint') { + assert(manager !== null); + const i2l = g.addNode({ type: 'qwen_image_i2l', id: getPrefixedId('qwen_image_i2l') }); + canvasOutput = await addInpaint({ + g, + state, + manager, + l2i, + i2l, + denoise, + vaeSource: modelLoader, + modelLoader, + seed, + }); + g.upsertMetadata({ generation_mode: 'krea2_inpaint' }); + } else if (generationMode === 'outpaint') { + assert(manager !== null); + const i2l = g.addNode({ type: 'qwen_image_i2l', id: getPrefixedId('qwen_image_i2l') }); + canvasOutput = await addOutpaint({ + g, + state, + manager, + l2i, + i2l, + denoise, + vaeSource: modelLoader, + modelLoader, + seed, + }); + g.upsertMetadata({ generation_mode: 'krea2_outpaint' }); + } else { + assert>(false); + } + + if (state.system.shouldUseNSFWChecker) { + canvasOutput = addNSFWChecker(g, canvasOutput); + } + + if (state.system.shouldUseWatermarker) { + canvasOutput = addWatermarker(g, canvasOutput); + } + + g.updateNode(canvasOutput, selectCanvasOutputFields(state)); + + if (selectActiveTab(state) === 'canvas') { + g.upsertMetadata(selectCanvasMetadata(state)); + } + + g.setMetadataReceivingNode(canvasOutput); + + return { + g, + seed, + positivePrompt, + }; +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts index 28aa74db5ec..74413ef5a88 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts @@ -209,16 +209,17 @@ export const getInfill = ( export const CANVAS_OUTPUT_PREFIX = 'canvas_output'; -export const isMainModelWithoutUnet = (modelLoader: Invocation) => { - return ( - modelLoader.type === 'flux_model_loader' || - modelLoader.type === 'flux2_klein_model_loader' || - modelLoader.type === 'sd3_model_loader' || - modelLoader.type === 'cogview4_model_loader' || - modelLoader.type === 'qwen_image_model_loader' || - modelLoader.type === 'z_image_model_loader' || - modelLoader.type === 'anima_model_loader' - ); +// Only the classic SD/SDXL loaders expose a `unet` output; every other main-model loader (FLUX, +// FLUX.2, SD3, CogView4, Qwen-Image, Z-Image, Krea-2, Anima) is transformer-based and has no `unet`. +// Defining the predicate by this small allow-list means any newly added transformer loader is treated +// as unet-less automatically, and the negated branch narrows `modelLoader` to the unet-bearing loaders +// so `addEdge(modelLoader, 'unet', ...)` type-checks. +type MainModelLoaderWithUnetNodes = 'main_model_loader' | 'sdxl_model_loader'; + +export const isMainModelWithoutUnet = ( + modelLoader: Invocation +): modelLoader is Invocation> => { + return modelLoader.type !== 'main_model_loader' && modelLoader.type !== 'sdxl_model_loader'; }; export const isCanvasOutputNodeId = (nodeId: string) => nodeId.split(':')[0] === CANVAS_OUTPUT_PREFIX; @@ -265,7 +266,8 @@ export const getDenoisingStartAndEnd = (state: RootState): { denoising_start: nu case 'sd-2': case 'cogview4': case 'qwen-image': - case 'z-image': { + case 'z-image': + case 'krea-2': { return { denoising_start: 1 - denoisingStrength, denoising_end: 1, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/types.ts b/invokeai/frontend/web/src/features/nodes/util/graph/types.ts index d6a18f3f9c0..c95f08a0fcb 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/types.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/types.ts @@ -47,6 +47,7 @@ export type DenoiseLatentsNodes = | 'cogview4_denoise' | 'qwen_image_denoise' | 'z_image_denoise' + | 'krea2_denoise' | 'anima_denoise'; export type MainModelLoaderNodes = @@ -58,6 +59,7 @@ export type MainModelLoaderNodes = | 'cogview4_model_loader' | 'qwen_image_model_loader' | 'z_image_model_loader' + | 'krea2_model_loader' | 'anima_model_loader'; export type VaeSourceNodes = 'seamless' | 'vae_loader'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamKrea2ModelSelects.tsx b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamKrea2ModelSelects.tsx new file mode 100644 index 00000000000..c717aba4d0c --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamKrea2ModelSelects.tsx @@ -0,0 +1,116 @@ +import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useModelCombobox } from 'common/hooks/useModelCombobox'; +import { + krea2Qwen3VlEncoderModelSelected, + krea2VaeModelSelected, + selectKrea2Qwen3VlEncoderModel, + selectKrea2VaeModel, +} from 'features/controlLayers/store/paramsSlice'; +import { zModelIdentifierField } from 'features/nodes/types/common'; +import { memo, useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useAnimaVAEModels, useQwen3VLEncoderModels, useQwenImageVAEModels } from 'services/api/hooks/modelsByType'; +import type { Qwen3VLEncoderModelConfig, VAEModelConfig } from 'services/api/types'; + +/** + * Krea-2 VAE Model Select - Krea-2 uses the Qwen-Image VAE (16-channel). Optional override used when the + * transformer is a single-file checkpoint/GGUF without a bundled VAE. + */ +const ParamKrea2VaeModelSelect = memo(() => { + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const krea2VaeModel = useAppSelector(selectKrea2VaeModel); + // Krea-2 / Qwen-Image / Anima share the identical AutoencoderKLQwenImage VAE. A standalone + // qwen_image_vae.safetensors is classified as either base (the weights are indistinguishable), so + // accept both here. + const [qwenImageVaes, { isLoading: isLoadingQwen }] = useQwenImageVAEModels(); + const [animaVaes, { isLoading: isLoadingAnima }] = useAnimaVAEModels(); + const modelConfigs = useMemo(() => [...qwenImageVaes, ...animaVaes], [qwenImageVaes, animaVaes]); + const isLoading = isLoadingQwen || isLoadingAnima; + + const _onChange = useCallback( + (model: VAEModelConfig | null) => { + dispatch(krea2VaeModelSelected(model ? zModelIdentifierField.parse(model) : null)); + }, + [dispatch] + ); + + const { options, value, onChange, noOptionsMessage } = useModelCombobox({ + modelConfigs, + onChange: _onChange, + selectedModel: krea2VaeModel, + isLoading, + }); + + return ( + + {t('modelManager.krea2Vae')} + + + ); +}); + +ParamKrea2VaeModelSelect.displayName = 'ParamKrea2VaeModelSelect'; + +/** + * Krea-2 Qwen3-VL Encoder Model Select - optional standalone encoder used when the transformer is a + * single-file checkpoint/GGUF without a bundled encoder. + */ +const ParamKrea2Qwen3VlEncoderModelSelect = memo(() => { + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const krea2Qwen3VlEncoderModel = useAppSelector(selectKrea2Qwen3VlEncoderModel); + const [modelConfigs, { isLoading }] = useQwen3VLEncoderModels(); + + const _onChange = useCallback( + (model: Qwen3VLEncoderModelConfig | null) => { + dispatch(krea2Qwen3VlEncoderModelSelected(model ? zModelIdentifierField.parse(model) : null)); + }, + [dispatch] + ); + + const { options, value, onChange, noOptionsMessage } = useModelCombobox({ + modelConfigs, + onChange: _onChange, + selectedModel: krea2Qwen3VlEncoderModel, + isLoading, + }); + + return ( + + {t('modelManager.krea2Qwen3VlEncoder')} + + + ); +}); + +ParamKrea2Qwen3VlEncoderModelSelect.displayName = 'ParamKrea2Qwen3VlEncoderModelSelect'; + +/** + * Combined component for Krea-2 standalone submodel selection (VAE + Qwen3-VL encoder). + */ +const ParamKrea2ModelSelects = () => { + return ( + <> + + + + ); +}; + +export default memo(ParamKrea2ModelSelects); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings.tsx b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings.tsx new file mode 100644 index 00000000000..8e09b34510d --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings.tsx @@ -0,0 +1,38 @@ +import { Divider, Flex } from '@invoke-ai/ui-library'; +import { useAppSelector } from 'app/store/storeHooks'; +import { selectKrea2RebalanceEnabled, selectKrea2SeedVarianceEnabled } from 'features/controlLayers/store/paramsSlice'; +import { memo } from 'react'; + +import ParamKrea2RebalanceEnabled from './ParamKrea2RebalanceEnabled'; +import ParamKrea2RebalanceMultiplier from './ParamKrea2RebalanceMultiplier'; +import ParamKrea2RebalanceWeights from './ParamKrea2RebalanceWeights'; +import ParamKrea2SeedVarianceEnabled from './ParamKrea2SeedVarianceEnabled'; +import ParamKrea2SeedVarianceRandomizePercent from './ParamKrea2SeedVarianceRandomizePercent'; +import ParamKrea2SeedVarianceStrength from './ParamKrea2SeedVarianceStrength'; + +const ParamKrea2EnhancersSettings = () => { + const rebalanceEnabled = useAppSelector(selectKrea2RebalanceEnabled); + const seedVarianceEnabled = useAppSelector(selectKrea2SeedVarianceEnabled); + + return ( + + + {rebalanceEnabled && ( + <> + + + + )} + + + {seedVarianceEnabled && ( + <> + + + + )} + + ); +}; + +export default memo(ParamKrea2EnhancersSettings); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceEnabled.tsx b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceEnabled.tsx new file mode 100644 index 00000000000..85d879a79ca --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceEnabled.tsx @@ -0,0 +1,31 @@ +import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; +import { selectKrea2RebalanceEnabled, setKrea2RebalanceEnabled } from 'features/controlLayers/store/paramsSlice'; +import type { ChangeEvent } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const ParamKrea2RebalanceEnabled = () => { + const { t } = useTranslation(); + const enabled = useAppSelector(selectKrea2RebalanceEnabled); + const dispatch = useAppDispatch(); + + const handleChange = useCallback( + (e: ChangeEvent) => { + dispatch(setKrea2RebalanceEnabled(e.target.checked)); + }, + [dispatch] + ); + + return ( + + + {t('parameters.krea2RebalanceEnabled')} + + + + ); +}; + +export default memo(ParamKrea2RebalanceEnabled); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceMultiplier.tsx b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceMultiplier.tsx new file mode 100644 index 00000000000..a32ab73b302 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceMultiplier.tsx @@ -0,0 +1,54 @@ +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 { selectKrea2RebalanceMultiplier, setKrea2RebalanceMultiplier } from 'features/controlLayers/store/paramsSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const CONSTRAINTS = { + initial: 4, + sliderMin: 0, + sliderMax: 10, + numberInputMin: 0, + numberInputMax: 20, + fineStep: 0.1, + coarseStep: 0.5, +}; + +const MARKS = [0, 2.5, 5, 7.5, 10]; + +const ParamKrea2RebalanceMultiplier = () => { + const multiplier = useAppSelector(selectKrea2RebalanceMultiplier); + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const onChange = useCallback((v: number) => dispatch(setKrea2RebalanceMultiplier(v)), [dispatch]); + + return ( + + + {t('parameters.krea2RebalanceMultiplier')} + + + + + ); +}; + +export default memo(ParamKrea2RebalanceMultiplier); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.test.ts b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.test.ts new file mode 100644 index 00000000000..69529cbd9b0 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.test.ts @@ -0,0 +1,17 @@ +import { readFileSync } from 'node:fs'; + +import { describe, expect, it } from 'vitest'; + +const source = readFileSync(new URL('./ParamKrea2RebalanceWeights.tsx', import.meta.url), 'utf8'); +const english = JSON.parse(readFileSync(new URL('../../../../../public/locales/en.json', import.meta.url), 'utf8')) as { + parameters?: Record; +}; + +describe('ParamKrea2RebalanceWeights localisation', () => { + it('uses a translated placeholder with an English locale entry', () => { + expect(source).toContain("placeholder={t('parameters.krea2RebalanceWeightsPlaceholder')}"); + expect(english.parameters?.krea2RebalanceWeightsPlaceholder).toBe( + '1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0' + ); + }); +}); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.tsx b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.tsx new file mode 100644 index 00000000000..b527cdc4711 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.tsx @@ -0,0 +1,31 @@ +import { FormControl, FormLabel, Input } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; +import { selectKrea2RebalanceWeights, setKrea2RebalanceWeights } from 'features/controlLayers/store/paramsSlice'; +import type { ChangeEvent } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const ParamKrea2RebalanceWeights = () => { + const { t } = useTranslation(); + const weights = useAppSelector(selectKrea2RebalanceWeights); + const dispatch = useAppDispatch(); + + const onChange = useCallback( + (e: ChangeEvent) => { + dispatch(setKrea2RebalanceWeights(e.target.value)); + }, + [dispatch] + ); + + return ( + + + {t('parameters.krea2RebalanceWeights')} + + + + ); +}; + +export default memo(ParamKrea2RebalanceWeights); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceEnabled.tsx b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceEnabled.tsx new file mode 100644 index 00000000000..a8e5e6875db --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceEnabled.tsx @@ -0,0 +1,31 @@ +import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; +import { selectKrea2SeedVarianceEnabled, setKrea2SeedVarianceEnabled } from 'features/controlLayers/store/paramsSlice'; +import type { ChangeEvent } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const ParamKrea2SeedVarianceEnabled = () => { + const { t } = useTranslation(); + const enabled = useAppSelector(selectKrea2SeedVarianceEnabled); + const dispatch = useAppDispatch(); + + const handleChange = useCallback( + (e: ChangeEvent) => { + dispatch(setKrea2SeedVarianceEnabled(e.target.checked)); + }, + [dispatch] + ); + + return ( + + + {t('parameters.seedVarianceEnabled')} + + + + ); +}; + +export default memo(ParamKrea2SeedVarianceEnabled); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceRandomizePercent.tsx b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceRandomizePercent.tsx new file mode 100644 index 00000000000..a0a4440d196 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceRandomizePercent.tsx @@ -0,0 +1,57 @@ +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 { + selectKrea2SeedVarianceRandomizePercent, + setKrea2SeedVarianceRandomizePercent, +} from 'features/controlLayers/store/paramsSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const CONSTRAINTS = { + initial: 50, + sliderMin: 1, + sliderMax: 100, + numberInputMin: 1, + numberInputMax: 100, + fineStep: 1, + coarseStep: 5, +}; + +const MARKS = [1, 25, 50, 75, 100]; + +const ParamKrea2SeedVarianceRandomizePercent = () => { + const randomizePercent = useAppSelector(selectKrea2SeedVarianceRandomizePercent); + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const onChange = useCallback((v: number) => dispatch(setKrea2SeedVarianceRandomizePercent(v)), [dispatch]); + + return ( + + + {t('parameters.seedVarianceRandomizePercent')} + + + + + ); +}; + +export default memo(ParamKrea2SeedVarianceRandomizePercent); diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceStrength.tsx new file mode 100644 index 00000000000..4c26de394b8 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceStrength.tsx @@ -0,0 +1,57 @@ +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 { + selectKrea2SeedVarianceStrength, + setKrea2SeedVarianceStrength, +} from 'features/controlLayers/store/paramsSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +const CONSTRAINTS = { + initial: 20, + sliderMin: 0, + sliderMax: 50, + numberInputMin: 0, + numberInputMax: 100, + fineStep: 1, + coarseStep: 5, +}; + +const MARKS = [0, 10, 20, 30, 40, 50]; + +const ParamKrea2SeedVarianceStrength = () => { + const strength = useAppSelector(selectKrea2SeedVarianceStrength); + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const onChange = useCallback((v: number) => dispatch(setKrea2SeedVarianceStrength(v)), [dispatch]); + + return ( + + + {t('parameters.seedVarianceStrength')} + + + + + ); +}; + +export default memo(ParamKrea2SeedVarianceStrength); diff --git a/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts b/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts index 2ac59a32e2b..820ed5e49e4 100644 --- a/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts +++ b/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts @@ -20,6 +20,7 @@ export const getOptimalDimension = (base?: BaseModelType | null): number => { case 'cogview4': case 'qwen-image': case 'z-image': + case 'krea-2': case 'anima': default: return 1024; @@ -78,6 +79,7 @@ export const getGridSize = (base?: BaseModelType | null): number => { case 'sd-3': case 'qwen-image': case 'z-image': + case 'krea-2': return 16; case 'sd-1': case 'sd-2': diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts index 1229371b6e8..0e07ea2afd0 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts @@ -17,6 +17,7 @@ import { buildAnimaGraph } from 'features/nodes/util/graph/generation/buildAnima import { buildCogView4Graph } from 'features/nodes/util/graph/generation/buildCogView4Graph'; import { buildExternalGraph } from 'features/nodes/util/graph/generation/buildExternalGraph'; import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph'; +import { buildKrea2Graph } from 'features/nodes/util/graph/generation/buildKrea2Graph'; import { buildQwenImageGraph } from 'features/nodes/util/graph/generation/buildQwenImageGraph'; import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph'; import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph'; @@ -69,6 +70,8 @@ const enqueueCanvas = async (store: AppStore, canvasManager: CanvasManager, prep return await buildQwenImageGraph(graphBuilderArg); case 'z-image': return await buildZImageGraph(graphBuilderArg); + case 'krea-2': + return await buildKrea2Graph(graphBuilderArg); case 'external': return await buildExternalGraph(graphBuilderArg); case 'anima': diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts index 8b0c30d924f..f030652557e 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts +++ b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts @@ -15,6 +15,7 @@ import { buildAnimaGraph } from 'features/nodes/util/graph/generation/buildAnima import { buildCogView4Graph } from 'features/nodes/util/graph/generation/buildCogView4Graph'; import { buildExternalGraph } from 'features/nodes/util/graph/generation/buildExternalGraph'; import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph'; +import { buildKrea2Graph } from 'features/nodes/util/graph/generation/buildKrea2Graph'; import { buildQwenImageGraph } from 'features/nodes/util/graph/generation/buildQwenImageGraph'; import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph'; import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph'; @@ -62,6 +63,8 @@ const enqueueGenerate = async (store: AppStore, prepend: boolean) => { return await buildQwenImageGraph(graphBuilderArg); case 'z-image': return await buildZImageGraph(graphBuilderArg); + case 'krea-2': + return await buildKrea2Graph(graphBuilderArg); case 'external': return await buildExternalGraph(graphBuilderArg); case 'anima': diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.ts b/invokeai/frontend/web/src/features/queue/store/readiness.ts index 1e40cc6ce18..d5df411916d 100644 --- a/invokeai/frontend/web/src/features/queue/store/readiness.ts +++ b/invokeai/frontend/web/src/features/queue/store/readiness.ts @@ -324,6 +324,17 @@ export const getReasonsWhyCannotEnqueueGenerateTab = (arg: { } } + if (model?.base === 'krea-2' && model.format !== 'diffusers') { + // Non-diffusers Krea-2 (single-file checkpoint / GGUF) ships only the transformer, so a standalone + // VAE and Qwen3-VL encoder must be selected. Diffusers models bundle them, so they're optional there. + if (!params.krea2VaeModel) { + reasons.push({ content: i18n.t('parameters.invoke.noKrea2VaeModelSelected') }); + } + if (!params.krea2Qwen3VlEncoderModel) { + reasons.push({ content: i18n.t('parameters.invoke.noKrea2Qwen3VlEncoderModelSelected') }); + } + } + if (model?.base === 'anima') { if (!params.animaVaeModel) { reasons.push({ content: i18n.t('parameters.invoke.noAnimaVaeModelSelected') }); @@ -784,6 +795,17 @@ export const getReasonsWhyCannotEnqueueCanvasTab = (arg: { } } + if (model?.base === 'krea-2' && model.format !== 'diffusers') { + // Non-diffusers Krea-2 (single-file checkpoint / GGUF) ships only the transformer, so a standalone + // VAE and Qwen3-VL encoder must be selected. Diffusers models bundle them, so they're optional there. + if (!params.krea2VaeModel) { + reasons.push({ content: i18n.t('parameters.invoke.noKrea2VaeModelSelected') }); + } + if (!params.krea2Qwen3VlEncoderModel) { + reasons.push({ content: i18n.t('parameters.invoke.noKrea2Qwen3VlEncoderModelSelected') }); + } + } + if (model?.base === 'anima') { if (!params.animaVaeModel) { reasons.push({ content: i18n.t('parameters.invoke.noAnimaVaeModelSelected') }); 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 bfb69b945c8..d6f05d40b5b 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx @@ -8,6 +8,7 @@ import { selectIsExternal, selectIsFLUX, selectIsFlux2, + selectIsKrea2, selectIsQwenImage, selectIsSD3, selectIsZImage, @@ -21,6 +22,7 @@ import ParamCLIPGEmbedModelSelect from 'features/parameters/components/Advanced/ import ParamCLIPLEmbedModelSelect from 'features/parameters/components/Advanced/ParamCLIPLEmbedModelSelect'; import ParamClipSkip from 'features/parameters/components/Advanced/ParamClipSkip'; import ParamFlux2KleinModelSelect from 'features/parameters/components/Advanced/ParamFlux2KleinModelSelect'; +import ParamKrea2ModelSelects from 'features/parameters/components/Advanced/ParamKrea2ModelSelects'; import ParamQwenImageComponentSourceSelect from 'features/parameters/components/Advanced/ParamQwenImageComponentSourceSelect'; import ParamQwenImageQuantization from 'features/parameters/components/Advanced/ParamQwenImageQuantization'; import ParamT5EncoderModelSelect from 'features/parameters/components/Advanced/ParamT5EncoderModelSelect'; @@ -54,43 +56,47 @@ export const AdvancedSettingsAccordion = memo(() => { const isExternal = useAppSelector(selectIsExternal); const isQwenImage = useAppSelector(selectIsQwenImage); const isAnima = useAppSelector(selectIsAnima); + const isKrea2 = useAppSelector(selectIsKrea2); const selectBadges = useMemo( () => - createMemoizedSelector([selectParamsSlice, selectIsFLUX, selectIsFlux2], (params, isFLUX, isFlux2) => { - const badges: (string | number)[] = []; - // FLUX.2 has VAE built into main model - no badge needed - if (isFLUX && !isFlux2) { - if (vaeConfig) { - let vaeBadge = vaeConfig.name; - if (params.vaePrecision === 'fp16') { - vaeBadge += ` ${params.vaePrecision}`; + createMemoizedSelector( + [selectParamsSlice, selectIsFLUX, selectIsFlux2, selectIsKrea2], + (params, isFLUX, isFlux2, isKrea2) => { + const badges: (string | number)[] = []; + // FLUX.2 has VAE built into main model - no badge needed + if (isFLUX && !isFlux2) { + if (vaeConfig) { + let vaeBadge = vaeConfig.name; + if (params.vaePrecision === 'fp16') { + vaeBadge += ` ${params.vaePrecision}`; + } + badges.push(vaeBadge); } - badges.push(vaeBadge); - } - } else if (!isFlux2) { - if (vaeConfig) { - let vaeBadge = vaeConfig.name; - if (params.vaePrecision === 'fp16') { - vaeBadge += ` ${params.vaePrecision}`; + } else if (!isFlux2 && !isKrea2) { + if (vaeConfig) { + let vaeBadge = vaeConfig.name; + if (params.vaePrecision === 'fp16') { + vaeBadge += ` ${params.vaePrecision}`; + } + badges.push(vaeBadge); + } else if (params.vaePrecision === 'fp16') { + badges.push(`VAE ${params.vaePrecision}`); + } + if (params.clipSkip) { + badges.push(`Skip ${params.clipSkip}`); + } + if (params.cfgRescaleMultiplier) { + badges.push(`Rescale ${params.cfgRescaleMultiplier}`); + } + if (params.seamlessXAxis || params.seamlessYAxis) { + badges.push('seamless'); } - badges.push(vaeBadge); - } else if (params.vaePrecision === 'fp16') { - badges.push(`VAE ${params.vaePrecision}`); - } - if (params.clipSkip) { - badges.push(`Skip ${params.clipSkip}`); - } - if (params.cfgRescaleMultiplier) { - badges.push(`Rescale ${params.cfgRescaleMultiplier}`); - } - if (params.seamlessXAxis || params.seamlessYAxis) { - badges.push('seamless'); } - } - return badges; - }), + return badges; + } + ), [vaeConfig] ); const badges = useAppSelector(selectBadges); @@ -107,13 +113,13 @@ export const AdvancedSettingsAccordion = memo(() => { return ( - {!isZImage && !isAnima && !isFlux2 && !isQwenImage && ( + {!isZImage && !isAnima && !isFlux2 && !isQwenImage && !isKrea2 && ( {isFLUX ? : } {!isFLUX && !isSD3 && } )} - {!isFLUX && !isFlux2 && !isSD3 && !isZImage && !isQwenImage && !isAnima && ( + {!isFLUX && !isFlux2 && !isSD3 && !isZImage && !isQwenImage && !isAnima && !isKrea2 && ( <> @@ -166,6 +172,11 @@ export const AdvancedSettingsAccordion = memo(() => { )} + {isKrea2 && ( + + + + )} ); 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 220008a38b0..684847e0cea 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx @@ -5,14 +5,14 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { selectLoRAsSlice } from 'features/controlLayers/store/lorasSlice'; import { + selectBase, selectFluxDypePreset, selectIsAnima, - selectIsCogView4, selectIsExternal, selectIsFLUX, selectIsFlux2, + selectIsKrea2, selectIsQwenImage, - selectIsSD3, selectIsZImage, selectModelSupportsGuidance, selectModelSupportsSteps, @@ -31,6 +31,7 @@ import ParamScheduler from 'features/parameters/components/Core/ParamScheduler'; import ParamSteps from 'features/parameters/components/Core/ParamSteps'; import ParamZImageScheduler from 'features/parameters/components/Core/ParamZImageScheduler'; import ParamZImageShift from 'features/parameters/components/Core/ParamZImageShift'; +import ParamKrea2EnhancersSettings from 'features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings'; import ParamZImageSeedVarianceSettings from 'features/parameters/components/SeedVariance/ParamZImageSeedVarianceSettings'; import { MainModelPicker } from 'features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker'; import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle'; @@ -40,6 +41,8 @@ import { useTranslation } from 'react-i18next'; import { useSelectedModelConfig } from 'services/api/hooks/useSelectedModelConfig'; import { isFluxFillMainModelModelConfig } from 'services/api/types'; +import { shouldShowStandardScheduler } from './generationSettingsVisibility'; + const formLabelProps: FormLabelProps = { minW: '4rem', }; @@ -47,13 +50,13 @@ const formLabelProps: FormLabelProps = { export const GenerationSettingsAccordion = memo(() => { const { t } = useTranslation(); const modelConfig = useSelectedModelConfig(); + const base = useAppSelector(selectBase); const isFLUX = useAppSelector(selectIsFLUX); const isFlux2 = useAppSelector(selectIsFlux2); - const isSD3 = useAppSelector(selectIsSD3); - const isCogView4 = useAppSelector(selectIsCogView4); const isZImage = useAppSelector(selectIsZImage); const isExternal = useAppSelector(selectIsExternal); const isQwenImage = useAppSelector(selectIsQwenImage); + const isKrea2 = useAppSelector(selectIsKrea2); const isAnima = useAppSelector(selectIsAnima); const fluxDypePreset = useAppSelector(selectFluxDypePreset); const modelSupportsGuidance = useAppSelector(selectModelSupportsGuidance); @@ -97,14 +100,7 @@ export const GenerationSettingsAccordion = memo(() => { - {!isExternal && - !isFLUX && - !isFlux2 && - !isSD3 && - !isCogView4 && - !isZImage && - !isQwenImage && - !isAnima && } + {shouldShowStandardScheduler(base) && } {!isExternal && (isFLUX || isFlux2) && } {!isExternal && isZImage && } {!isExternal && isAnima && } @@ -121,6 +117,7 @@ export const GenerationSettingsAccordion = memo(() => { {!isExternal && isFLUX && fluxDypePreset === 'manual' && } {!isExternal && isZImage && } + {!isExternal && isKrea2 && } )} diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.test.ts b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.test.ts new file mode 100644 index 00000000000..9eb42dff42a --- /dev/null +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; + +import { shouldShowStandardScheduler } from './generationSettingsVisibility'; + +describe('shouldShowStandardScheduler', () => { + it.each(['sd-1', 'sd-2', 'sdxl', 'sdxl-refiner', undefined] as const)( + 'shows the standard scheduler for %s', + (base) => { + expect(shouldShowStandardScheduler(base)).toBe(true); + } + ); + + it.each(['external', 'flux', 'flux2', 'sd-3', 'cogview4', 'z-image', 'qwen-image', 'anima', 'krea-2'] as const)( + 'hides the standard scheduler for %s', + (base) => { + expect(shouldShowStandardScheduler(base)).toBe(false); + } + ); +}); diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts new file mode 100644 index 00000000000..4e1fc34d37d --- /dev/null +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts @@ -0,0 +1,16 @@ +import type { BaseModelType } from 'features/nodes/types/common'; + +const BASES_WITHOUT_STANDARD_SCHEDULER = new Set([ + 'external', + 'flux', + 'flux2', + 'sd-3', + 'cogview4', + 'z-image', + 'qwen-image', + 'anima', + 'krea-2', +]); + +export const shouldShowStandardScheduler = (base: BaseModelType | null | undefined): boolean => + base === undefined || base === null || !BASES_WITHOUT_STANDARD_SCHEDULER.has(base); diff --git a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts b/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts index 07e89a305d4..4723a546b70 100644 --- a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts +++ b/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts @@ -29,6 +29,7 @@ import { isLoRAModelConfig, isMainOrExternalModelConfig, isQwen3EncoderModelConfig, + isQwen3VLEncoderModelConfig, isQwenImageDiffusersMainModelConfig, isQwenImageVAEModelConfig, isQwenVLEncoderModelConfig, @@ -113,6 +114,7 @@ export const useQwenImageDiffusersModels = () => buildModelsHook(isQwenImageDiff export const useQwenImageVAEModels = () => buildModelsHook(isQwenImageVAEModelConfig)(); export const useQwenVLEncoderModels = () => buildModelsHook(isQwenVLEncoderModelConfig)(); export const useQwen3EncoderModels = () => buildModelsHook(isQwen3EncoderModelConfig)(); +export const useQwen3VLEncoderModels = () => buildModelsHook(isQwen3VLEncoderModelConfig)(); export const useGlobalReferenceImageModels = buildModelsHook( (config) => isIPAdapterModelConfig(config) || isFluxReduxModelConfig(config) || isFluxKontextModelConfig(config) ); @@ -155,5 +157,6 @@ export const selectZImageDiffusersModels = buildModelsSelector(isZImageDiffusers export const selectFlux2DiffusersModels = buildModelsSelector(isFlux2DiffusersMainModelConfig); export const selectFluxVAEModels = buildModelsSelector(isFluxVAEModelConfig); export const selectAnimaVAEModels = buildModelsSelector(isAnimaVAEModelConfig); +export const selectQwen3VLEncoderModels = buildModelsSelector(isQwen3VLEncoderModelConfig); export const useTextLLMModels = () => buildModelsHook(isTextLLMModelConfig)(); export const useLlavaModels = () => buildModelsHook(isLLaVAModelConfig)(); diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts index f938b7f0f2c..65a0727f2d2 100644 --- a/invokeai/frontend/web/src/services/api/schema.ts +++ b/invokeai/frontend/web/src/services/api/schema.ts @@ -3741,7 +3741,7 @@ export type components = { */ type: "anima_text_encoder"; }; - AnyModelConfig: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + AnyModelConfig: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; /** * AppVersion * @description App Version Response @@ -3893,7 +3893,7 @@ export type components = { * fallback/null value `BaseModelType.Any` for these models, instead of making the model base optional. * @enum {string} */ - BaseModelType: "any" | "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "flux2" | "cogview4" | "z-image" | "external" | "qwen-image" | "anima" | "unknown"; + BaseModelType: "any" | "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "flux2" | "cogview4" | "z-image" | "external" | "qwen-image" | "anima" | "krea-2" | "unknown"; /** Batch */ Batch: { /** @@ -7879,7 +7879,7 @@ export type components = { * @description The generation mode that output this image * @default null */ - generation_mode?: ("txt2img" | "img2img" | "inpaint" | "outpaint" | "sdxl_txt2img" | "sdxl_img2img" | "sdxl_inpaint" | "sdxl_outpaint" | "flux_txt2img" | "flux_img2img" | "flux_inpaint" | "flux_outpaint" | "flux2_txt2img" | "flux2_img2img" | "flux2_inpaint" | "flux2_outpaint" | "sd3_txt2img" | "sd3_img2img" | "sd3_inpaint" | "sd3_outpaint" | "cogview4_txt2img" | "cogview4_img2img" | "cogview4_inpaint" | "cogview4_outpaint" | "z_image_txt2img" | "z_image_img2img" | "z_image_inpaint" | "z_image_outpaint" | "qwen_image_txt2img" | "qwen_image_img2img" | "qwen_image_inpaint" | "qwen_image_outpaint" | "anima_txt2img" | "anima_img2img" | "anima_inpaint" | "anima_outpaint") | null; + generation_mode?: ("txt2img" | "img2img" | "inpaint" | "outpaint" | "sdxl_txt2img" | "sdxl_img2img" | "sdxl_inpaint" | "sdxl_outpaint" | "flux_txt2img" | "flux_img2img" | "flux_inpaint" | "flux_outpaint" | "flux2_txt2img" | "flux2_img2img" | "flux2_inpaint" | "flux2_outpaint" | "sd3_txt2img" | "sd3_img2img" | "sd3_inpaint" | "sd3_outpaint" | "cogview4_txt2img" | "cogview4_img2img" | "cogview4_inpaint" | "cogview4_outpaint" | "z_image_txt2img" | "z_image_img2img" | "z_image_inpaint" | "z_image_outpaint" | "qwen_image_txt2img" | "qwen_image_img2img" | "qwen_image_inpaint" | "qwen_image_outpaint" | "anima_txt2img" | "anima_img2img" | "anima_inpaint" | "anima_outpaint" | "krea2_txt2img" | "krea2_img2img" | "krea2_inpaint" | "krea2_outpaint") | null; /** * Positive Prompt * @description The positive prompt parameter @@ -12599,7 +12599,7 @@ export type components = { * @description The nodes in this graph */ nodes?: { - [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; }; /** * Edges @@ -12636,7 +12636,7 @@ export type components = { * @description The results of node executions */ results: { - [key: string]: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; + [key: string]: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["Krea2ConditioningOutput"] | components["schemas"]["Krea2LoRALoaderOutput"] | components["schemas"]["Krea2ModelLoaderOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; }; /** * Errors @@ -16076,7 +16076,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -16086,7 +16086,7 @@ export type components = { * Result * @description The result of the invocation */ - result: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; + result: components["schemas"]["AnimaConditioningOutput"] | components["schemas"]["AnimaLLLiteOutput"] | components["schemas"]["AnimaLoRALoaderOutput"] | components["schemas"]["AnimaModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["IfInvocationOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["Krea2ConditioningOutput"] | components["schemas"]["Krea2LoRALoaderOutput"] | components["schemas"]["Krea2ModelLoaderOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["QwenImageConditioningOutput"] | components["schemas"]["QwenImageLoRALoaderOutput"] | components["schemas"]["QwenImageModelLoaderOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["WorkflowReturnGetOutput"] | components["schemas"]["WorkflowReturnOutput"] | components["schemas"]["WorkflowReturnValueOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; }; /** * InvocationErrorEvent @@ -16140,7 +16140,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -16303,6 +16303,13 @@ export type components = { invokeai_img_val_thresholds: components["schemas"]["ImageOutput"]; ip_adapter: components["schemas"]["IPAdapterOutput"]; iterate: components["schemas"]["IterateInvocationOutput"]; + krea2_conditioning_rebalance: components["schemas"]["Krea2ConditioningOutput"]; + krea2_denoise: components["schemas"]["LatentsOutput"]; + krea2_lora_collection_loader: components["schemas"]["Krea2LoRALoaderOutput"]; + krea2_lora_loader: components["schemas"]["Krea2LoRALoaderOutput"]; + krea2_model_loader: components["schemas"]["Krea2ModelLoaderOutput"]; + krea2_seed_variance: components["schemas"]["Krea2ConditioningOutput"]; + krea2_text_encoder: components["schemas"]["Krea2ConditioningOutput"]; l2i: components["schemas"]["ImageOutput"]; latents: components["schemas"]["LatentsOutput"]; latents_collection: components["schemas"]["LatentsCollectionOutput"]; @@ -16476,7 +16483,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -16551,7 +16558,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | 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"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | 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"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | 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"]["TextLLMInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -17686,6 +17693,506 @@ export type components = { type: "iterate_output"; }; JsonValue: unknown; + /** + * Krea2ConditioningField + * @description A Krea-2 conditioning tensor primitive value + */ + Krea2ConditioningField: { + /** + * Conditioning Name + * @description The name of conditioning tensor + */ + conditioning_name: string; + }; + /** + * Krea2ConditioningOutput + * @description Base class for nodes that output a Krea-2 conditioning tensor. + */ + Krea2ConditioningOutput: { + /** @description Conditioning tensor */ + conditioning: components["schemas"]["Krea2ConditioningField"]; + /** + * type + * @default krea2_conditioning_output + * @constant + */ + type: "krea2_conditioning_output"; + }; + /** + * Conditioning Rebalance - Krea-2 + * @description Per-layer rebalancing of Krea-2 text conditioning (improves prompt adherence). + * + * Krea-2 conditioning stacks 12 Qwen3-VL hidden-state layers per token. Weighting those layers + * individually (and applying an overall multiplier) lets you push the model harder toward the prompt, + * counteracting the quality-dilution from distillation. Ported from the ComfyUI + * `ConditioningKrea2Rebalance` node. This is an optional pass between the text encoder and denoise. + */ + Krea2ConditioningRebalanceInvocation: { + /** + * 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; + /** + * Conditioning + * @description Conditioning tensor + * @default null + */ + conditioning?: components["schemas"]["Krea2ConditioningField"] | null; + /** + * Per Layer Weights + * @description Comma-separated gains for the 12 tapped encoder layers (exactly 12 values). + * @default 1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0 + */ + per_layer_weights?: string; + /** + * Multiplier + * @description Overall multiplier applied to the conditioning after per-layer weighting. + * @default 4 + */ + multiplier?: number; + /** + * type + * @default krea2_conditioning_rebalance + * @constant + */ + type: "krea2_conditioning_rebalance"; + }; + /** + * Denoise - Krea-2 + * @description Run the denoising process with a Krea-2 model. + */ + Krea2DenoiseInvocation: { + /** + * @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 Latents tensor + * @default null + */ + latents?: components["schemas"]["LatentsField"] | null; + /** + * @description A mask of the region to apply the denoising process to. Values of 0.0 represent the regions to be fully denoised, and 1.0 represent the regions to be preserved. + * @default null + */ + denoise_mask?: components["schemas"]["DenoiseMaskField"] | null; + /** + * Denoising Start + * @description When to start denoising, expressed a percentage of total steps + * @default 0 + */ + denoising_start?: number; + /** + * Denoising End + * @description When to stop denoising, expressed a percentage of total steps + * @default 1 + */ + denoising_end?: number; + /** + * Transformer + * @description Krea-2 model (Transformer) to load + * @default null + */ + transformer?: components["schemas"]["TransformerField"] | null; + /** + * @description Positive conditioning tensor + * @default null + */ + positive_conditioning?: components["schemas"]["Krea2ConditioningField"] | null; + /** + * @description Negative conditioning tensor + * @default null + */ + negative_conditioning?: components["schemas"]["Krea2ConditioningField"] | null; + /** + * CFG Scale + * @description Classifier-Free Guidance scale + * @default 1 + */ + cfg_scale?: number | number[]; + /** + * Width + * @description Width of the generated image. + * @default 1024 + */ + width?: number; + /** + * Height + * @description Height of the generated image. + * @default 1024 + */ + height?: number; + /** + * Steps + * @description Number of steps to run + * @default 8 + */ + steps?: number; + /** + * Seed + * @description Randomness seed for reproducibility. + * @default 0 + */ + seed?: number; + /** + * Shift + * @description Override the resolution-aware timestep shift (mu). Leave unset to use the model default (mu=1.15 for the distilled Turbo checkpoint). + * @default null + */ + shift?: number | null; + /** + * type + * @default krea2_denoise + * @constant + */ + type: "krea2_denoise"; + }; + /** + * Apply LoRA Collection - Krea-2 + * @description Applies a collection of LoRAs to a Krea-2 transformer and/or Qwen3-VL encoder. + */ + Krea2LoRACollectionLoader: { + /** + * 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; + /** + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. + * @default null + */ + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; + /** + * Transformer + * @description Transformer + * @default null + */ + transformer?: components["schemas"]["TransformerField"] | null; + /** + * Qwen3-VL Encoder + * @description Qwen3-VL tokenizer and text encoder + * @default null + */ + qwen3_vl_encoder?: components["schemas"]["Qwen3VLEncoderField"] | null; + /** + * type + * @default krea2_lora_collection_loader + * @constant + */ + type: "krea2_lora_collection_loader"; + }; + /** + * Apply LoRA - Krea-2 + * @description Apply a LoRA model to a Krea-2 transformer and/or Qwen3-VL text encoder. + */ + Krea2LoRALoaderInvocation: { + /** + * 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; + /** + * LoRA + * @description LoRA model to load + * @default null + */ + lora?: components["schemas"]["ModelIdentifierField"] | null; + /** + * Weight + * @description The weight at which the LoRA is applied to each model + * @default 0.75 + */ + weight?: number; + /** + * Krea-2 Transformer + * @description Transformer + * @default null + */ + transformer?: components["schemas"]["TransformerField"] | null; + /** + * Qwen3-VL Encoder + * @description Qwen3-VL tokenizer and text encoder + * @default null + */ + qwen3_vl_encoder?: components["schemas"]["Qwen3VLEncoderField"] | null; + /** + * type + * @default krea2_lora_loader + * @constant + */ + type: "krea2_lora_loader"; + }; + /** + * Krea2LoRALoaderOutput + * @description Krea-2 LoRA Loader Output + */ + Krea2LoRALoaderOutput: { + /** + * Krea-2 Transformer + * @description Transformer + * @default null + */ + transformer: components["schemas"]["TransformerField"] | null; + /** + * Qwen3-VL Encoder + * @description Qwen3-VL tokenizer and text encoder + * @default null + */ + qwen3_vl_encoder: components["schemas"]["Qwen3VLEncoderField"] | null; + /** + * type + * @default krea2_lora_loader_output + * @constant + */ + type: "krea2_lora_loader_output"; + }; + /** + * Main Model - Krea-2 + * @description Loads a Krea-2 model, outputting its submodels. + * + * By default the VAE (Qwen-Image VAE) and Qwen3-VL text encoder are extracted from the Krea-2 + * diffusers pipeline. Standalone overrides may be supplied (e.g. when the transformer is a + * single-file checkpoint that has no bundled VAE / encoder). + */ + Krea2ModelLoaderInvocation: { + /** + * 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; + /** + * Transformer + * @description Krea-2 model (Transformer) to load + */ + model: components["schemas"]["ModelIdentifierField"]; + /** + * VAE + * @description Standalone VAE model. Krea-2 uses the Qwen-Image VAE (16-channel). If not provided, the VAE is loaded from the Krea-2 (diffusers) model. + * @default null + */ + vae_model?: components["schemas"]["ModelIdentifierField"] | null; + /** + * Qwen3-VL Encoder + * @description Standalone Qwen3-VL Encoder model. If not provided, the encoder is loaded from the Krea-2 (diffusers) model. + * @default null + */ + qwen3_vl_encoder_model?: components["schemas"]["ModelIdentifierField"] | null; + /** + * type + * @default krea2_model_loader + * @constant + */ + type: "krea2_model_loader"; + }; + /** + * Krea2ModelLoaderOutput + * @description Krea-2 base model loader output. + */ + Krea2ModelLoaderOutput: { + /** + * Transformer + * @description Transformer + */ + transformer: components["schemas"]["TransformerField"]; + /** + * Qwen3-VL Encoder + * @description Qwen3-VL tokenizer and text encoder + */ + qwen3_vl_encoder: components["schemas"]["Qwen3VLEncoderField"]; + /** + * VAE + * @description VAE + */ + vae: components["schemas"]["VAEField"]; + /** + * type + * @default krea2_model_loader_output + * @constant + */ + type: "krea2_model_loader_output"; + }; + /** + * Seed Variance - Krea-2 + * @description Inject per-seed diversity into Krea-2 text conditioning. + * + * Distilled few-step models (like Krea-2-Turbo) suffer from low seed variance — different seeds give + * near-identical images. This adds seeded uniform noise to a random subset of the text-embedding + * values, trading some prompt adherence for variety (the same idea as the Z-Image-Turbo + * `SeedVarianceEnhancer`). Optional pass between the text encoder and denoise; the defaults are + * aggressive and may need tuning for Krea-2. + */ + Krea2SeedVarianceInvocation: { + /** + * 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; + /** + * Conditioning + * @description Conditioning tensor + * @default null + */ + conditioning?: components["schemas"]["Krea2ConditioningField"] | null; + /** + * Strength + * @description Magnitude of the uniform noise added to the embeddings (noise in [-strength, +strength]). + * @default 20 + */ + strength?: number; + /** + * Randomize Percent + * @description Percentage of embedding values that get perturbed (Bernoulli mask). + * @default 50 + */ + randomize_percent?: number; + /** + * Variance Seed + * @description Seed for the variance noise (vary this to get variety). + * @default 0 + */ + variance_seed?: number; + /** + * type + * @default krea2_seed_variance + * @constant + */ + type: "krea2_seed_variance"; + }; + /** + * Prompt - Krea-2 + * @description Encodes a text prompt for Krea-2 using the Qwen3-VL text encoder. + * + * The encoder taps 12 decoder hidden-state layers and stacks them per token, producing a 4D + * conditioning tensor (B, seq, 12, hidden) that the Krea-2 transformer's text-fusion stage consumes. + */ + Krea2TextEncoderInvocation: { + /** + * 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; + /** + * Prompt + * @description Text prompt describing the desired image. + * @default null + */ + prompt?: string | null; + /** + * Qwen3-VL Encoder + * @description Qwen3-VL tokenizer and text encoder + * @default null + */ + qwen3_vl_encoder?: components["schemas"]["Qwen3VLEncoderField"] | null; + /** + * type + * @default krea2_text_encoder + * @constant + */ + type: "krea2_text_encoder"; + }; + /** + * Krea2VariantType + * @description Krea 2 model variants. + * @enum {string} + */ + Krea2VariantType: "krea2_turbo" | "krea2_base"; /** * LaMa Infill * @description Infills transparent areas of an image using the LaMa model @@ -19137,6 +19644,89 @@ export type components = { base: "flux2"; variant: components["schemas"]["Flux2VariantType"] | null; }; + /** + * LoRA_LyCORIS_Krea2_Config + * @description Model config for Krea-2 LoRA models in LyCORIS (single-file diffusers PEFT) format. + */ + LoRA_LyCORIS_Krea2_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Source Url + * @description Optional URL for the model (e.g. download page or model page). + */ + source_url: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Type + * @default lora + * @constant + */ + type: "lora"; + /** + * Trigger Phrases + * @description Set of trigger phrases for this model + */ + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; + /** + * Format + * @default lycoris + * @constant + */ + format: "lycoris"; + /** + * Base + * @default krea-2 + * @constant + */ + base: "krea-2"; + }; /** * LoRA_LyCORIS_QwenImage_Config * @description Model config for Qwen Image Edit LoRA models in LyCORIS format. @@ -20344,6 +20934,95 @@ export type components = { base: "flux2"; variant: components["schemas"]["Flux2VariantType"]; }; + /** + * Main_Checkpoint_Krea2_Config + * @description Model config for Krea-2 single-file checkpoint models (safetensors, etc). + */ + Main_Checkpoint_Krea2_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Source Url + * @description Optional URL for the model (e.g. download page or model page). + */ + source_url: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; + /** + * Trigger Phrases + * @description Set of trigger phrases for this model + */ + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** + * Config Path + * @description Path to the config for this model, if any. + */ + config_path: string | null; + /** + * Base + * @default krea-2 + * @constant + */ + base: "krea-2"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; + variant: components["schemas"]["Krea2VariantType"]; + }; /** * Main_Checkpoint_QwenImage_Config * @description Model config for Qwen Image single-file checkpoint models (safetensors, etc). @@ -21047,7 +21726,93 @@ export type components = { * Main_Diffusers_Flux2_Config * @description Model config for FLUX.2 models in diffusers format (e.g. FLUX.2 Klein). */ - Main_Diffusers_Flux2_Config: { + Main_Diffusers_Flux2_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Source Url + * @description Optional URL for the model (e.g. download page or model page). + */ + source_url: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; + /** + * Trigger Phrases + * @description Set of trigger phrases for this model + */ + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** @default */ + repo_variant: components["schemas"]["ModelRepoVariant"]; + /** + * Base + * @default flux2 + * @constant + */ + base: "flux2"; + variant: components["schemas"]["Flux2VariantType"]; + }; + /** + * Main_Diffusers_Krea2_Config + * @description Model config for Krea-2 diffusers models (Krea-2-Turbo). + */ + Main_Diffusers_Krea2_Config: { /** * Key * @description A unique key for this model. @@ -21123,11 +21888,11 @@ export type components = { repo_variant: components["schemas"]["ModelRepoVariant"]; /** * Base - * @default flux2 + * @default krea-2 * @constant */ - base: "flux2"; - variant: components["schemas"]["Flux2VariantType"]; + base: "krea-2"; + variant: components["schemas"]["Krea2VariantType"]; }; /** * Main_Diffusers_QwenImage_Config @@ -21904,6 +22669,95 @@ export type components = { format: "gguf_quantized"; variant: components["schemas"]["Flux2VariantType"]; }; + /** + * Main_GGUF_Krea2_Config + * @description Model config for GGUF-quantized Krea-2 transformer models (single-file). + */ + Main_GGUF_Krea2_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Source Url + * @description Optional URL for the model (e.g. download page or model page). + */ + source_url: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Type + * @default main + * @constant + */ + type: "main"; + /** + * Trigger Phrases + * @description Set of trigger phrases for this model + */ + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["MainModelDefaultSettings"] | null; + /** + * Config Path + * @description Path to the config for this model, if any. + */ + config_path: string | null; + /** + * Base + * @default krea-2 + * @constant + */ + base: "krea-2"; + /** + * Format + * @default gguf_quantized + * @constant + */ + format: "gguf_quantized"; + variant: components["schemas"]["Krea2VariantType"]; + }; /** * Main_GGUF_QwenImage_Config * @description Model config for GGUF-quantized Qwen Image transformer models. @@ -23773,7 +24627,7 @@ export type components = { * @description Storage format of model. * @enum {string} */ - ModelFormat: "omi" | "diffusers" | "checkpoint" | "lycoris" | "onnx" | "olive" | "embedding_file" | "embedding_folder" | "invokeai" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "bnb_quantized_int8b" | "bnb_quantized_nf4b" | "gguf_quantized" | "external_api" | "unknown"; + ModelFormat: "omi" | "diffusers" | "checkpoint" | "lycoris" | "onnx" | "olive" | "embedding_file" | "embedding_folder" | "invokeai" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "qwen3_vl_encoder" | "bnb_quantized_int8b" | "bnb_quantized_nf4b" | "gguf_quantized" | "external_api" | "unknown"; /** ModelIdentifierField */ ModelIdentifierField: { /** @@ -23910,7 +24764,7 @@ export type components = { * Config * @description The installed model's config */ - config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; }; /** * ModelInstallDownloadProgressEvent @@ -24076,7 +24930,7 @@ export type components = { * Config Out * @description After successful installation, this will hold the configuration object. */ - config_out?: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]) | null; + config_out?: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]) | null; /** * Inplace * @description Leave model in its current location; otherwise install under models directory @@ -24162,7 +25016,7 @@ export type components = { * Config * @description The model's config */ - config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; /** * @description The submodel type, if any * @default null @@ -24183,7 +25037,7 @@ export type components = { * Config * @description The model's config */ - config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; /** * @description The submodel type, if any * @default null @@ -24309,7 +25163,7 @@ export type components = { * Variant * @description The variant of the model. */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null; + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | components["schemas"]["Krea2VariantType"] | null; /** @description The prediction type of the model. */ prediction_type?: components["schemas"]["SchedulerPredictionType"] | null; /** @@ -24391,7 +25245,7 @@ export type components = { * @description Model type. * @enum {string} */ - ModelType: "onnx" | "main" | "vae" | "lora" | "control_lora" | "controlnet" | "embedding" | "ip_adapter" | "clip_vision" | "clip_embed" | "t2i_adapter" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "spandrel_image_to_image" | "siglip" | "flux_redux" | "llava_onevision" | "text_llm" | "external_image_generator" | "unknown"; + ModelType: "onnx" | "main" | "vae" | "lora" | "control_lora" | "controlnet" | "embedding" | "ip_adapter" | "clip_vision" | "clip_embed" | "t2i_adapter" | "t5_encoder" | "qwen3_encoder" | "qwen_vl_encoder" | "qwen3_vl_encoder" | "spandrel_image_to_image" | "siglip" | "flux_redux" | "llava_onevision" | "text_llm" | "external_image_generator" | "unknown"; /** * ModelVariantType * @description Variant type. @@ -24404,7 +25258,7 @@ export type components = { */ ModelsList: { /** Models */ - models: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"])[]; + models: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"])[]; }; /** * Multiply Integers @@ -25844,6 +26698,197 @@ export type components = { /** @description Qwen3 model size variant (4B or 8B) */ variant: components["schemas"]["Qwen3VariantType"]; }; + /** + * Qwen3VLEncoderField + * @description Field for the Qwen3-VL text encoder used by Krea-2 models. + */ + Qwen3VLEncoderField: { + /** @description Info to load tokenizer submodel */ + tokenizer: components["schemas"]["ModelIdentifierField"]; + /** @description Info to load text_encoder submodel */ + text_encoder: components["schemas"]["ModelIdentifierField"]; + /** + * Loras + * @description LoRAs to apply on model loading + */ + loras?: components["schemas"]["LoRAField"][]; + }; + /** + * Qwen3VLEncoder_Checkpoint_Config + * @description Configuration for a single-file Qwen3-VL text encoder checkpoint (e.g. ComfyUI ``qwen3vl_4b_*``). + * + * Distinguished from the text-only ``Qwen3Encoder`` checkpoint (Z-Image) by the presence of the + * Qwen3-VL visual tower. The tokenizer is not bundled in single-file checkpoints and is pulled from + * HuggingFace (``Qwen/Qwen3-VL-4B-Instruct``) by the loader. + */ + Qwen3VLEncoder_Checkpoint_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Source Url + * @description Optional URL for the model (e.g. download page or model page). + */ + source_url: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Config Path + * @description Path to the config for this model, if any. + */ + config_path: string | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default qwen3_vl_encoder + * @constant + */ + type: "qwen3_vl_encoder"; + /** + * Format + * @default checkpoint + * @constant + */ + format: "checkpoint"; + /** + * Cpu Only + * @description Whether this model should run on CPU only + */ + cpu_only: boolean | null; + }; + /** + * Qwen3VLEncoder_Qwen3VLEncoder_Config + * @description Configuration for standalone Qwen3-VL text encoder models (diffusers-like directory format). + * + * Used by Krea-2, whose text conditioning comes from a Qwen3-VL model (``Qwen3VLModel``). The model + * weights are expected either in a ``text_encoder`` subfolder of the model directory or directly at the + * root (standalone download). This is distinct from the text-only ``Qwen3Encoder`` (Z-Image / FLUX.2 + * Klein) and the Qwen2.5-VL ``QwenVLEncoder`` (Qwen Image). + */ + Qwen3VLEncoder_Qwen3VLEncoder_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Source Url + * @description Optional URL for the model (e.g. download page or model page). + */ + source_url: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Base + * @default any + * @constant + */ + base: "any"; + /** + * Type + * @default qwen3_vl_encoder + * @constant + */ + type: "qwen3_vl_encoder"; + /** + * Format + * @default qwen3_vl_encoder + * @constant + */ + format: "qwen3_vl_encoder"; + /** + * Cpu Only + * @description Whether this model should run on CPU only + */ + cpu_only: boolean | null; + }; /** * Qwen3VariantType * @description Qwen3 text encoder variants based on model size. @@ -29215,7 +30260,7 @@ export type components = { type: components["schemas"]["ModelType"]; format?: components["schemas"]["ModelFormat"] | null; /** Variant */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null; + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | components["schemas"]["Krea2VariantType"] | null; /** * Is Installed * @default false @@ -29260,7 +30305,7 @@ export type components = { type: components["schemas"]["ModelType"]; format?: components["schemas"]["ModelFormat"] | null; /** Variant */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null; + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | components["schemas"]["Krea2VariantType"] | null; /** * Is Installed * @default false @@ -29791,7 +30836,7 @@ export type components = { path_or_prefix: string; model_type: components["schemas"]["ModelType"]; /** Variant */ - variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | null; + variant?: components["schemas"]["ModelVariantType"] | components["schemas"]["ClipVariantType"] | components["schemas"]["FluxVariantType"] | components["schemas"]["Flux2VariantType"] | components["schemas"]["ZImageVariantType"] | components["schemas"]["QwenImageVariantType"] | components["schemas"]["Qwen3VariantType"] | components["schemas"]["Krea2VariantType"] | null; }; /** * Subtract Integers @@ -34566,7 +35611,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Validation Error */ @@ -34598,7 +35643,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Validation Error */ @@ -34650,7 +35695,7 @@ export interface operations { * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -34757,7 +35802,7 @@ export interface operations { * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -34830,7 +35875,7 @@ export interface operations { * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -35565,7 +36610,7 @@ export interface operations { * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_QwenImage_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Diffusers_Krea2_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_QwenImage_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_Checkpoint_Krea2_Config"] | components["schemas"]["Main_Checkpoint_Anima_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_QwenImage_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["Main_GGUF_Krea2_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Checkpoint_QwenImage_Config"] | components["schemas"]["VAE_Checkpoint_Anima_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Checkpoint_Anima_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_LyCORIS_Krea2_Config"] | components["schemas"]["LoRA_LyCORIS_QwenImage_Config"] | components["schemas"]["LoRA_LyCORIS_Anima_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3VLEncoder_Checkpoint_Config"] | components["schemas"]["Qwen3VLEncoder_Qwen3VLEncoder_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["QwenVLEncoder_Diffusers_Config"] | components["schemas"]["QwenVLEncoder_Checkpoint_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["TextLLM_Diffusers_Config"] | components["schemas"]["ExternalApiModelConfig"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index 34d0470db45..f176fae964a 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -118,6 +118,7 @@ export type T5EncoderBnbQuantizedLlmInt8bModelConfig = Extract< >; export type Qwen3EncoderModelConfig = Extract; export type QwenVLEncoderModelConfig = Extract; +export type Qwen3VLEncoderModelConfig = Extract; export type SpandrelImageToImageModelConfig = Extract; export type CheckpointModelConfig = Extract; export type CLIPVisionModelConfig = Extract; @@ -397,6 +398,10 @@ export const isQwenVLEncoderModelConfig = (config: AnyModelConfig): config is Qw return config.type === 'qwen_vl_encoder'; }; +export const isQwen3VLEncoderModelConfig = (config: AnyModelConfig): config is Qwen3VLEncoderModelConfig => { + return config.type === 'qwen3_vl_encoder'; +}; + export const isCLIPEmbedModelConfigOrSubmodel = ( config: AnyModelConfig, excludeSubmodels?: boolean diff --git a/pyproject.toml b/pyproject.toml index de665d621e3..47e8b4e8d8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ "accelerate", "bitsandbytes; sys_platform!='darwin'", "compel>=2.4.0,<3", - "diffusers[torch]==0.37.0", + "diffusers[torch]==0.39.0", "gguf", "mediapipe==0.10.14", # needed for "mediapipeface" controlnet model "numpy<2.0.0", diff --git a/tests/app/invocations/test_krea2_denoise.py b/tests/app/invocations/test_krea2_denoise.py new file mode 100644 index 00000000000..cdf4830d027 --- /dev/null +++ b/tests/app/invocations/test_krea2_denoise.py @@ -0,0 +1,341 @@ +import math +from contextlib import contextmanager, nullcontext +from types import SimpleNamespace + +import pytest +import torch + +from invokeai.app.invocations.fields import DenoiseMaskField, Krea2ConditioningField, LatentsField +from invokeai.app.invocations.krea2_denoise import KREA2_LATENT_CHANNELS, Krea2DenoiseInvocation +from invokeai.app.invocations.model import ModelIdentifierField, TransformerField +from invokeai.backend.model_manager.taxonomy import BaseModelType, Krea2VariantType, ModelFormat, ModelType +from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ConditioningFieldData, Krea2ConditioningInfo + + +@pytest.mark.parametrize(("denoising_start", "denoising_end"), [(0.75, 0.25), (0.5, 0.5)]) +def test_validate_inputs_rejects_empty_or_inverted_denoising_range( + denoising_start: float, denoising_end: float +) -> None: + invocation = Krea2DenoiseInvocation.model_construct( + denoising_start=denoising_start, denoising_end=denoising_end, denoise_mask=None + ) + + with pytest.raises(ValueError, match="denoising_start must be less than denoising_end"): + invocation._validate_inputs() + + +def test_validate_inputs_rejects_denoise_mask_without_latents() -> None: + invocation = Krea2DenoiseInvocation.model_construct( + denoising_start=0.0, + denoising_end=1.0, + denoise_mask=DenoiseMaskField(mask_name="mask"), + latents=None, + ) + + with pytest.raises(ValueError, match="Initial latents are required when a denoise mask is provided"): + invocation._validate_inputs() + + +def test_validate_inputs_accepts_a_valid_configuration() -> None: + invocation = Krea2DenoiseInvocation.model_construct( + denoising_start=0.0, denoising_end=1.0, denoise_mask=None, latents=None + ) + # A full-range denoise with no mask is valid and must not raise. + invocation._validate_inputs() + + +def _validation_payload() -> dict: + model = ModelIdentifierField( + key="krea-model", + hash="model-hash", + name="Krea Model", + base=BaseModelType.Krea2, + type=ModelType.Main, + ) + return { + "transformer": TransformerField(transformer=model, loras=[]), + "positive_conditioning": Krea2ConditioningField(conditioning_name="positive"), + } + + +@pytest.mark.parametrize(("field", "value"), [("width", 0), ("width", -16), ("height", 0), ("height", -16)]) +def test_model_validation_rejects_non_positive_dimensions(field: str, value: int) -> None: + with pytest.raises(ValueError): + Krea2DenoiseInvocation(**_validation_payload(), **{field: value}) + + +@pytest.mark.parametrize( + ("field", "value"), + [ + ("cfg_scale", math.nan), + ("cfg_scale", math.inf), + ("cfg_scale", [1.0, math.nan]), + ("shift", math.nan), + ("shift", math.inf), + ], +) +def test_model_validation_rejects_non_finite_sampling_values(field: str, value: object) -> None: + with pytest.raises(ValueError): + Krea2DenoiseInvocation(**_validation_payload(), **{field: value}) + + +def test_model_validation_accepts_positive_dimensions_and_finite_sampling_values() -> None: + invocation = Krea2DenoiseInvocation(**_validation_payload(), width=16, height=32, cfg_scale=[1.0] * 8, shift=1.15) + assert invocation.width == 16 + assert invocation.height == 32 + + +class TestPrepareCfgScale: + def test_scalar_is_broadcast_to_the_step_count(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct(cfg_scale=4.5) + assert invocation._prepare_cfg_scale(8) == [4.5] * 8 + + def test_list_of_matching_length_is_returned_unchanged(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct(cfg_scale=[4.0, 3.0, 2.0]) + assert invocation._prepare_cfg_scale(3) == [4.0, 3.0, 2.0] + + def test_list_of_wrong_length_raises(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct(cfg_scale=[4.0, 3.0, 2.0]) + with pytest.raises(ValueError, match="cfg_scale list has 3 values but the model is configured for 8 steps"): + invocation._prepare_cfg_scale(8) + + +class TestCfgForStep: + def test_scale_above_one_uses_cfg_when_negative_conditioning_is_available(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct() + assert invocation._should_apply_cfg_for_step(4.0, has_negative_conditioning=True) is True + + @pytest.mark.parametrize("cfg_scale", [1.0, 0.5]) + def test_scale_at_or_below_one_does_not_use_cfg(self, cfg_scale: float) -> None: + invocation = Krea2DenoiseInvocation.model_construct() + assert invocation._should_apply_cfg_for_step(cfg_scale, has_negative_conditioning=True) is False + + def test_missing_negative_conditioning_does_not_use_cfg(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct() + assert invocation._should_apply_cfg_for_step(4.0, has_negative_conditioning=False) is False + + +class TestEffectiveScheduleValidation: + def test_rejects_a_fractional_range_that_rounds_to_zero_steps(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct() + with pytest.raises(ValueError, match="does not contain any effective denoising steps"): + invocation._validate_effective_schedule(start_idx=0, end_idx=0) + + def test_accepts_a_range_with_at_least_one_effective_step(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct() + invocation._validate_effective_schedule(start_idx=0, end_idx=1) + + def test_invalid_type_raises(self) -> None: + invocation = Krea2DenoiseInvocation.model_construct(cfg_scale="nonsense") + with pytest.raises(ValueError, match="Invalid CFG scale type"): + invocation._prepare_cfg_scale(8) + + +def test_cfg_scale_list_is_built_against_full_step_count_then_clipped() -> None: + # Regression: a per-step CFG list carries one value per *configured* step. img2img/inpaint clips the + # sampling schedule (denoising_start/denoising_end), which shrinks the number of active steps. The list + # must be prepared against the full step count (``total_sigmas``) and *then* sliced to the active + # window — preparing it against the already-reduced count would reject the user's full-length list. + steps = 8 + invocation = Krea2DenoiseInvocation.model_construct(cfg_scale=[float(i) for i in range(steps)]) + + full_cfg_scale = invocation._prepare_cfg_scale(steps) # built against the FULL step count -> ok + assert len(full_cfg_scale) == steps + + # Slicing to the active window [start_idx:end_idx] (as _run_diffusion does) keeps the right per-step values. + start_idx, end_idx = 2, 6 + assert full_cfg_scale[start_idx:end_idx] == [2.0, 3.0, 4.0, 5.0] + + # Preparing against the reduced (clipped) count instead would have raised — the bug this guards against. + with pytest.raises(ValueError): + invocation._prepare_cfg_scale(end_idx - start_idx) + + +def test_get_noise_is_deterministic_and_correctly_shaped() -> None: + invocation = Krea2DenoiseInvocation.model_construct() + device = torch.device("cpu") + + noise_a = invocation._get_noise(height=64, width=128, dtype=torch.float32, device=device, seed=42) + noise_b = invocation._get_noise(height=64, width=128, dtype=torch.float32, device=device, seed=42) + noise_other = invocation._get_noise(height=64, width=128, dtype=torch.float32, device=device, seed=43) + + # Shape is (1, latent_channels, H // 8, W // 8). + assert noise_a.shape == (1, KREA2_LATENT_CHANNELS, 8, 16) + # Same seed -> identical noise (reproducibility); different seed -> different noise. + assert torch.equal(noise_a, noise_b) + assert not torch.equal(noise_a, noise_other) + + +class _Scheduler: + def __init__(self, **_kwargs) -> None: + self.config = SimpleNamespace(num_train_timesteps=1000) + + def set_timesteps(self, *, sigmas, mu, device) -> None: + del mu + self.sigmas = torch.tensor([*sigmas, 0.0], device=device) + self.timesteps = self.sigmas[:-1] * self.config.num_train_timesteps + + +class _Transformer: + def __init__(self) -> None: + self.conditioning_values: list[float] = [] + + def __call__(self, *, hidden_states, encoder_hidden_states, **_kwargs): + self.conditioning_values.append(float(encoder_hidden_states.mean())) + return (torch.zeros_like(hidden_states),) + + +class _TransformerInfo: + def __init__(self, transformer: _Transformer) -> None: + self.transformer = transformer + + @contextmanager + def model_on_device(self, **_kwargs): + yield ({}, self.transformer) + + +def _model_identifier() -> ModelIdentifierField: + return ModelIdentifierField( + key="krea-model", + hash="model-hash", + name="Krea Model", + base=BaseModelType.Krea2, + type=ModelType.Main, + ) + + +def _runtime_invocation(*, cfg_scale: float | list[float], with_mask: bool = False) -> Krea2DenoiseInvocation: + return Krea2DenoiseInvocation.model_construct( + transformer=TransformerField(transformer=_model_identifier(), loras=[]), + positive_conditioning=Krea2ConditioningField(conditioning_name="positive"), + negative_conditioning=Krea2ConditioningField(conditioning_name="negative"), + cfg_scale=cfg_scale, + width=16, + height=16, + steps=2, + seed=1, + shift=1.15, + denoising_start=0.0, + denoising_end=1.0, + latents=LatentsField(latents_name="init") if with_mask else None, + denoise_mask=DenoiseMaskField(mask_name="mask") if with_mask else None, + ) + + +def _runtime_context(tmp_path, transformer: _Transformer): + conditionings = { + "positive": ConditioningFieldData(conditionings=[Krea2ConditioningInfo(prompt_embeds=torch.ones(1, 2, 12, 8))]), + "negative": ConditioningFieldData( + conditionings=[Krea2ConditioningInfo(prompt_embeds=torch.zeros(1, 2, 12, 8))] + ), + } + tensors = { + "init": torch.zeros(1, KREA2_LATENT_CHANNELS, 2, 2), + "mask": torch.zeros(1, 1, 16, 16), + } + config = SimpleNamespace(format=ModelFormat.Checkpoint, variant=Krea2VariantType.Turbo) + return SimpleNamespace( + models=SimpleNamespace( + load=lambda _identifier: _TransformerInfo(transformer), + get_config=lambda _identifier: config, + get_absolute_path=lambda _config: tmp_path, + ), + conditioning=SimpleNamespace(load=lambda name: conditionings[name]), + tensors=SimpleNamespace(load=lambda name: tensors[name]), + util=SimpleNamespace(sd_step_callback=lambda *_args: None), + ) + + +def _patch_runtime(monkeypatch) -> None: + monkeypatch.setattr( + "diffusers.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler", _Scheduler + ) + monkeypatch.setattr( + "invokeai.app.invocations.krea2_denoise.TorchDevice.choose_torch_device", lambda: torch.device("cpu") + ) + monkeypatch.setattr( + "invokeai.app.invocations.krea2_denoise.TorchDevice.choose_bfloat16_safe_dtype", + lambda _device: torch.float32, + ) + monkeypatch.setattr( + "invokeai.app.invocations.krea2_denoise.LayerPatcher.apply_smart_model_patches", + lambda **_kwargs: nullcontext(), + ) + + +def test_run_diffusion_applies_mixed_cfg_only_at_enabled_steps(monkeypatch, tmp_path) -> None: + _patch_runtime(monkeypatch) + transformer = _Transformer() + + latents = _runtime_invocation(cfg_scale=[2.0, 1.0])._run_diffusion(_runtime_context(tmp_path, transformer)) + + assert latents.shape == (1, KREA2_LATENT_CHANNELS, 1, 2, 2) + assert transformer.conditioning_values == [1.0, 0.0, 1.0] + + +def test_run_diffusion_reaches_masked_denoising_merge(monkeypatch, tmp_path) -> None: + _patch_runtime(monkeypatch) + transformer = _Transformer() + merge_sigmas: list[float] = [] + + class _InpaintExtension: + def __init__(self, **_kwargs) -> None: + pass + + def merge_intermediate_latents_with_init_latents(self, latents, sigma): + merge_sigmas.append(sigma) + return latents + + monkeypatch.setattr("invokeai.app.invocations.krea2_denoise.RectifiedFlowInpaintExtension", _InpaintExtension) + + latents = _runtime_invocation(cfg_scale=1.0, with_mask=True)._run_diffusion(_runtime_context(tmp_path, transformer)) + + assert latents.shape == (1, KREA2_LATENT_CHANNELS, 1, 2, 2) + assert len(merge_sigmas) == 2 + assert transformer.conditioning_values == [1.0, 1.0] + + +def test_run_diffusion_uses_per_prompt_position_ids_when_lengths_differ(monkeypatch, tmp_path) -> None: + # Regression: the positive and negative prompts can tokenize to different lengths. The rotary position + # ids (text tokens + image grid) must match *each pass's own* text length. Reusing the positive prompt's + # position ids for the uncond pass leaves the rotary embedding a different length than the negative + # query sequence, which crashes in the real transformer's apply_rotary_emb. + _patch_runtime(monkeypatch) + + image_seq_len = 1 # width=height=16 -> 2x2 latent -> a single 2x2 patch + + class _PositionIdChecker: + def __call__(self, *, hidden_states, encoder_hidden_states, position_ids, **_kwargs): + text_len = encoder_hidden_states.shape[1] + pos_len = position_ids.shape[0] + # The invariant the real Krea2Transformer2DModel enforces: rotary length == text + image tokens. + assert pos_len == text_len + image_seq_len, ( + f"position_ids length {pos_len} must equal text length {text_len} + image tokens {image_seq_len}" + ) + return (torch.zeros_like(hidden_states),) + + transformer = _PositionIdChecker() + + # Positive prompt is longer than the negative prompt (3 vs. 2 text tokens). + conditionings = { + "positive": ConditioningFieldData(conditionings=[Krea2ConditioningInfo(prompt_embeds=torch.ones(1, 3, 12, 8))]), + "negative": ConditioningFieldData( + conditionings=[Krea2ConditioningInfo(prompt_embeds=torch.zeros(1, 2, 12, 8))] + ), + } + config = SimpleNamespace(format=ModelFormat.Checkpoint, variant=Krea2VariantType.Turbo) + context = SimpleNamespace( + models=SimpleNamespace( + load=lambda _identifier: _TransformerInfo(transformer), + get_config=lambda _identifier: config, + get_absolute_path=lambda _config: tmp_path, + ), + conditioning=SimpleNamespace(load=lambda name: conditionings[name]), + tensors=SimpleNamespace(load=lambda _name: None), + util=SimpleNamespace(sd_step_callback=lambda *_args: None), + ) + + # cfg_scale > 1 so both the cond (positive) and uncond (negative) passes run each step. Without the fix, + # the uncond pass receives the positive prompt's position ids and the checker above fails. + latents = _runtime_invocation(cfg_scale=2.0)._run_diffusion(context) + assert latents.shape == (1, KREA2_LATENT_CHANNELS, 1, 2, 2) diff --git a/tests/app/invocations/test_krea2_enhancers.py b/tests/app/invocations/test_krea2_enhancers.py new file mode 100644 index 00000000000..67586d37137 --- /dev/null +++ b/tests/app/invocations/test_krea2_enhancers.py @@ -0,0 +1,166 @@ +"""Tests for the optional Krea-2 conditioning enhancers (rebalance + seed variance). + +Both operate on the 4D ``prompt_embeds (B, seq, 12, hidden)`` conditioning between the text encoder and +denoise. The load-bearing logic - the per-layer gain broadcast, the exact-count weight validation, and the +seeded-noise determinism / out-of-place property - is exercised here with a stub conditioning context. +""" + +import math +from types import SimpleNamespace + +import pytest +import torch + +from invokeai.app.invocations.fields import Krea2ConditioningField +from invokeai.app.invocations.krea2_conditioning_rebalance import Krea2ConditioningRebalanceInvocation +from invokeai.app.invocations.krea2_seed_variance import Krea2SeedVarianceInvocation +from invokeai.backend.stable_diffusion.diffusion.conditioning_data import ConditioningFieldData, Krea2ConditioningInfo + + +def _make_context(embeds: torch.Tensor, saved: dict) -> SimpleNamespace: + def load(_name: str) -> ConditioningFieldData: + return ConditioningFieldData( + conditionings=[Krea2ConditioningInfo(prompt_embeds=embeds, prompt_embeds_mask=None)] + ) + + def save(data: ConditioningFieldData) -> str: + saved["data"] = data + return "saved-name" + + return SimpleNamespace(conditioning=SimpleNamespace(load=load, save=save)) + + +def _saved_embeds(saved: dict) -> torch.Tensor: + conditioning = saved["data"].conditionings[0] + assert isinstance(conditioning, Krea2ConditioningInfo) + return conditioning.prompt_embeds + + +class TestRebalanceParseWeights: + def test_accepts_exactly_twelve_values(self) -> None: + invocation = Krea2ConditioningRebalanceInvocation.model_construct( + per_layer_weights="1,2,3,4,5,6,7,8,9,10,11,12" + ) + assert invocation._parse_weights() == [float(i) for i in range(1, 13)] + + @pytest.mark.parametrize("weights", ["1,2,3", "1,2,3,4,5,6,7,8,9,10,11,12,13"]) + def test_rejects_wrong_count(self, weights: str) -> None: + invocation = Krea2ConditioningRebalanceInvocation.model_construct(per_layer_weights=weights) + with pytest.raises(ValueError, match="exactly 12 values"): + invocation._parse_weights() + + def test_rejects_non_numeric(self) -> None: + invocation = Krea2ConditioningRebalanceInvocation.model_construct(per_layer_weights="a,b,c,d,e,f,g,h,i,j,k,l") + with pytest.raises(ValueError, match="comma-separated numbers"): + invocation._parse_weights() + + @pytest.mark.parametrize("value", ["nan", "inf", "-inf"]) + def test_rejects_non_finite_weights(self, value: str) -> None: + values = ["1"] * 11 + [value] + invocation = Krea2ConditioningRebalanceInvocation.model_construct(per_layer_weights=",".join(values)) + with pytest.raises(ValueError, match="finite"): + invocation._parse_weights() + + +@pytest.mark.parametrize("value", [math.nan, math.inf, -math.inf]) +def test_rebalance_rejects_non_finite_multiplier(value: float) -> None: + with pytest.raises(ValueError): + Krea2ConditioningRebalanceInvocation( + conditioning=Krea2ConditioningField(conditioning_name="c"), + multiplier=value, + ) + + +@pytest.mark.parametrize("value", [math.nan, math.inf, -math.inf]) +def test_seed_variance_rejects_non_finite_strength(value: float) -> None: + with pytest.raises(ValueError): + Krea2SeedVarianceInvocation( + conditioning=Krea2ConditioningField(conditioning_name="c"), + strength=value, + ) + + +def test_rebalance_applies_per_layer_gains_on_the_layer_axis() -> None: + # embeds is (B=1, seq=2, 12 layers, hidden=4); gains must apply along the layer axis (dim=2). + embeds = torch.ones(1, 2, 12, 4) + saved: dict = {} + invocation = Krea2ConditioningRebalanceInvocation.model_construct( + conditioning=Krea2ConditioningField(conditioning_name="c"), + per_layer_weights="1,2,3,4,5,6,7,8,9,10,11,12", + multiplier=1.0, + ) + + invocation.invoke(_make_context(embeds, saved)) + + out = _saved_embeds(saved) + assert out.shape == (1, 2, 12, 4) + for layer_index in range(12): + assert torch.allclose(out[:, :, layer_index, :], torch.full((1, 2, 4), float(layer_index + 1))) + + +def test_rebalance_applies_overall_multiplier() -> None: + embeds = torch.ones(1, 1, 12, 2) + saved: dict = {} + invocation = Krea2ConditioningRebalanceInvocation.model_construct( + conditioning=Krea2ConditioningField(conditioning_name="c"), + per_layer_weights=",".join(["1.0"] * 12), + multiplier=3.0, + ) + + invocation.invoke(_make_context(embeds, saved)) + + assert torch.allclose(_saved_embeds(saved), torch.full((1, 1, 12, 2), 3.0)) + + +def test_seed_variance_is_deterministic_for_a_fixed_seed() -> None: + embeds = torch.ones(1, 3, 12, 4) + saved_a: dict = {} + saved_b: dict = {} + invocation = Krea2SeedVarianceInvocation.model_construct( + conditioning=Krea2ConditioningField(conditioning_name="c"), + strength=20.0, + randomize_percent=50.0, + variance_seed=42, + ) + + invocation.invoke(_make_context(embeds.clone(), saved_a)) + invocation.invoke(_make_context(embeds.clone(), saved_b)) + + assert torch.equal(_saved_embeds(saved_a), _saved_embeds(saved_b)) + + +def test_seed_variance_differs_across_seeds() -> None: + embeds = torch.ones(1, 3, 12, 4) + saved_a: dict = {} + saved_b: dict = {} + + def _run(seed: int, saved: dict) -> None: + Krea2SeedVarianceInvocation.model_construct( + conditioning=Krea2ConditioningField(conditioning_name="c"), + strength=20.0, + randomize_percent=50.0, + variance_seed=seed, + ).invoke(_make_context(embeds.clone(), saved)) + + _run(42, saved_a) + _run(43, saved_b) + + assert not torch.equal(_saved_embeds(saved_a), _saved_embeds(saved_b)) + + +def test_seed_variance_does_not_mutate_the_input_conditioning() -> None: + embeds = torch.ones(1, 3, 12, 4) + original = embeds.clone() + saved: dict = {} + invocation = Krea2SeedVarianceInvocation.model_construct( + conditioning=Krea2ConditioningField(conditioning_name="c"), + strength=20.0, + randomize_percent=50.0, + variance_seed=7, + ) + + invocation.invoke(_make_context(embeds, saved)) + + # The invocation must produce a new tensor, not perturb the caller's embeds in place. + assert torch.equal(embeds, original) + assert not torch.equal(_saved_embeds(saved), original) diff --git a/tests/app/invocations/test_krea2_lora_loader.py b/tests/app/invocations/test_krea2_lora_loader.py new file mode 100644 index 00000000000..63087fc2073 --- /dev/null +++ b/tests/app/invocations/test_krea2_lora_loader.py @@ -0,0 +1,145 @@ +from types import SimpleNamespace + +import pytest + +from invokeai.app.invocations.krea2_lora_loader import Krea2LoRACollectionLoader, Krea2LoRALoaderInvocation +from invokeai.app.invocations.model import LoRAField, ModelIdentifierField, Qwen3VLEncoderField, TransformerField +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType + + +def _model(key: str, model_type: ModelType, base: BaseModelType = BaseModelType.Krea2) -> ModelIdentifierField: + return ModelIdentifierField(key=key, hash=f"hash-{key}", name=key, base=base, type=model_type) + + +def _lora(key: str = "lora") -> LoRAField: + return LoRAField(lora=_model(key, ModelType.LoRA), weight=1.0) + + +def _transformer(loras: list[LoRAField]) -> TransformerField: + return TransformerField( + transformer=_model("main", ModelType.Main).model_copy(update={"submodel_type": SubModelType.Transformer}), + loras=loras, + ) + + +def _encoder(loras: list[LoRAField]) -> Qwen3VLEncoderField: + encoder = _model("encoder", ModelType.Qwen3VLEncoder, BaseModelType.Any) + return Qwen3VLEncoderField( + tokenizer=encoder.model_copy(update={"submodel_type": SubModelType.Tokenizer}), + text_encoder=encoder.model_copy(update={"submodel_type": SubModelType.TextEncoder}), + loras=loras, + ) + + +def _context(stored_base: BaseModelType = BaseModelType.Krea2) -> SimpleNamespace: + return SimpleNamespace( + models=SimpleNamespace( + exists=lambda _key: True, + get_config=lambda _identifier: SimpleNamespace(base=stored_base, type=ModelType.LoRA), + ) + ) + + +def test_collection_loader_repairs_transformer_only_lora_state() -> None: + lora = _lora() + existing = lora.model_copy(update={"weight": 0.25}) + invocation = Krea2LoRACollectionLoader.model_construct( + loras=[lora], transformer=_transformer([existing]), qwen3_vl_encoder=_encoder([]) + ) + + output = invocation.invoke(_context()) + + assert [item.lora.key for item in output.transformer.loras] == ["lora"] + assert [item.lora.key for item in output.qwen3_vl_encoder.loras] == ["lora"] + assert output.qwen3_vl_encoder.loras[0].weight == 0.25 + + +def test_collection_loader_repairs_encoder_only_lora_state() -> None: + lora = _lora() + invocation = Krea2LoRACollectionLoader.model_construct( + loras=[lora], transformer=_transformer([]), qwen3_vl_encoder=_encoder([lora]) + ) + + output = invocation.invoke(_context()) + + assert [item.lora.key for item in output.transformer.loras] == ["lora"] + assert [item.lora.key for item in output.qwen3_vl_encoder.loras] == ["lora"] + + +def test_collection_loader_does_not_duplicate_synchronized_lora_state() -> None: + lora = _lora() + invocation = Krea2LoRACollectionLoader.model_construct( + loras=[lora], transformer=_transformer([lora]), qwen3_vl_encoder=_encoder([lora]) + ) + + output = invocation.invoke(_context()) + + assert len(output.transformer.loras) == 1 + assert len(output.qwen3_vl_encoder.loras) == 1 + + +def test_single_loader_repairs_transformer_only_lora_state() -> None: + lora = _lora() + existing = lora.model_copy(update={"weight": 0.25}) + invocation = Krea2LoRALoaderInvocation.model_construct( + lora=lora.lora, weight=lora.weight, transformer=_transformer([existing]), qwen3_vl_encoder=_encoder([]) + ) + + output = invocation.invoke(_context()) + + assert len(output.transformer.loras) == 1 + assert [item.lora.key for item in output.qwen3_vl_encoder.loras] == ["lora"] + assert output.qwen3_vl_encoder.loras[0].weight == 0.25 + + +def test_single_loader_rejects_non_krea_lora() -> None: + lora = _model("flux-lora", ModelType.LoRA, BaseModelType.Flux) + invocation = Krea2LoRALoaderInvocation.model_construct( + lora=lora, weight=1.0, transformer=_transformer([]), qwen3_vl_encoder=_encoder([]) + ) + + try: + invocation.invoke(_context()) + except ValueError as error: + assert "not Krea-2 models" in str(error) + else: + raise AssertionError("Expected a non-Krea LoRA to be rejected") + + +@pytest.mark.parametrize("loader_type", ["single", "collection"]) +def test_loader_rejects_forged_krea_identifier_for_non_krea_stored_model(loader_type: str) -> None: + lora = _lora("forged") + if loader_type == "single": + invocation = Krea2LoRALoaderInvocation.model_construct( + lora=lora.lora, weight=lora.weight, transformer=_transformer([]), qwen3_vl_encoder=_encoder([]) + ) + else: + invocation = Krea2LoRACollectionLoader.model_construct( + loras=[lora], transformer=_transformer([]), qwen3_vl_encoder=_encoder([]) + ) + + with pytest.raises(ValueError, match="not Krea-2"): + invocation.invoke(_context(BaseModelType.Flux)) + + +@pytest.mark.parametrize("loader_type", ["single", "collection"]) +def test_loader_rejects_conflicting_existing_weights(loader_type: str) -> None: + requested = _lora() + transformer_lora = requested.model_copy(update={"weight": 0.25}) + encoder_lora = requested.model_copy(update={"weight": 0.75}) + if loader_type == "single": + invocation = Krea2LoRALoaderInvocation.model_construct( + lora=requested.lora, + weight=requested.weight, + transformer=_transformer([transformer_lora]), + qwen3_vl_encoder=_encoder([encoder_lora]), + ) + else: + invocation = Krea2LoRACollectionLoader.model_construct( + loras=[requested], + transformer=_transformer([transformer_lora]), + qwen3_vl_encoder=_encoder([encoder_lora]), + ) + + with pytest.raises(ValueError, match="conflicting weights"): + invocation.invoke(_context()) diff --git a/tests/app/invocations/test_krea2_model_loader.py b/tests/app/invocations/test_krea2_model_loader.py new file mode 100644 index 00000000000..21432ffa9d7 --- /dev/null +++ b/tests/app/invocations/test_krea2_model_loader.py @@ -0,0 +1,70 @@ +from types import SimpleNamespace + +import pytest + +from invokeai.app.invocations.krea2_model_loader import Krea2ModelLoaderInvocation +from invokeai.app.invocations.model import ModelIdentifierField +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType + + +def _model(key: str, base: BaseModelType, model_type: ModelType) -> ModelIdentifierField: + return ModelIdentifierField(key=key, hash=f"hash-{key}", name=key, base=base, type=model_type) + + +def _context(configs: dict[str, SimpleNamespace]) -> SimpleNamespace: + return SimpleNamespace(models=SimpleNamespace(get_config=lambda identifier: configs[identifier.key])) + + +def _config(base: BaseModelType, model_type: ModelType, model_format: ModelFormat) -> SimpleNamespace: + return SimpleNamespace(base=base, type=model_type, format=model_format, name=f"{base.value}-{model_type.value}") + + +@pytest.mark.parametrize("vae_base", [BaseModelType.QwenImage, BaseModelType.Anima]) +def test_loader_accepts_supported_standalone_components(vae_base: BaseModelType) -> None: + main = _model("main", BaseModelType.Krea2, ModelType.Main) + vae = _model("vae", vae_base, ModelType.VAE) + encoder = _model("encoder", BaseModelType.Any, ModelType.Qwen3VLEncoder) + context = _context( + { + "main": _config(BaseModelType.Krea2, ModelType.Main, ModelFormat.Checkpoint), + "vae": _config(vae_base, ModelType.VAE, ModelFormat.Checkpoint), + "encoder": _config(BaseModelType.Any, ModelType.Qwen3VLEncoder, ModelFormat.Qwen3VLEncoder), + } + ) + + output = Krea2ModelLoaderInvocation(model=main, vae_model=vae, qwen3_vl_encoder_model=encoder).invoke(context) + + assert output.vae.vae.key == "vae" + assert output.qwen3_vl_encoder.text_encoder.key == "encoder" + + +@pytest.mark.parametrize( + ("target", "stored_config"), + [ + ("main", _config(BaseModelType.Flux, ModelType.Main, ModelFormat.Checkpoint)), + ("vae", _config(BaseModelType.StableDiffusionXL, ModelType.VAE, ModelFormat.Checkpoint)), + ("encoder", _config(BaseModelType.Any, ModelType.Qwen3Encoder, ModelFormat.Checkpoint)), + ], +) +def test_loader_rejects_incompatible_stored_component(target: str, stored_config: SimpleNamespace) -> None: + main = _model("main", BaseModelType.Krea2, ModelType.Main) + vae = _model("vae", BaseModelType.QwenImage, ModelType.VAE) + encoder = _model("encoder", BaseModelType.Any, ModelType.Qwen3VLEncoder) + configs = { + "main": _config(BaseModelType.Krea2, ModelType.Main, ModelFormat.Checkpoint), + "vae": _config(BaseModelType.QwenImage, ModelType.VAE, ModelFormat.Checkpoint), + "encoder": _config(BaseModelType.Any, ModelType.Qwen3VLEncoder, ModelFormat.Qwen3VLEncoder), + } + configs[target] = stored_config + + with pytest.raises(ValueError, match="Krea-2|VAE|Qwen3-VL"): + Krea2ModelLoaderInvocation(model=main, vae_model=vae, qwen3_vl_encoder_model=encoder).invoke(_context(configs)) + + +def test_loader_vae_ui_filter_includes_qwen_image_and_anima() -> None: + field = Krea2ModelLoaderInvocation.model_fields["vae_model"] + assert field.json_schema_extra is not None + assert set(field.json_schema_extra["ui_model_base"]) == { + BaseModelType.QwenImage.value, + BaseModelType.Anima.value, + } diff --git a/tests/app/invocations/test_krea2_text_encoder.py b/tests/app/invocations/test_krea2_text_encoder.py new file mode 100644 index 00000000000..f7a35878b79 --- /dev/null +++ b/tests/app/invocations/test_krea2_text_encoder.py @@ -0,0 +1,191 @@ +from contextlib import contextmanager, nullcontext +from types import SimpleNamespace + +import pytest +import torch + +from invokeai.app.invocations.krea2_text_encoder import Krea2TextEncoderInvocation +from invokeai.app.invocations.model import LoRAField, ModelIdentifierField, Qwen3VLEncoderField +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType +from invokeai.backend.patches.lora_conversions.krea2_lora_constants import KREA2_LORA_QWEN3VL_PREFIX +from invokeai.backend.patches.model_patch_raw import ModelPatchRaw + + +class _Tokenizer: + def __call__(self, _text, **_kwargs): + return SimpleNamespace( + input_ids=torch.ones((1, 40), dtype=torch.long), + attention_mask=torch.ones((1, 40), dtype=torch.long), + ) + + +class _TextEncoder(torch.nn.Module): + def __init__(self) -> None: + super().__init__() + self.anchor = torch.nn.Parameter(torch.zeros(1)) + + def forward(self, **_kwargs): + hidden_states = tuple(torch.full((1, 40, 4), float(index)) for index in range(36)) + return SimpleNamespace(hidden_states=hidden_states) + + +class _TokenizerInfo: + def __enter__(self): + return _Tokenizer() + + def __exit__(self, *_args): + return None + + +class _TextEncoderInfo: + @contextmanager + def model_on_device(self): + yield ({}, _TextEncoder()) + + +def _identifier(key: str, model_type: ModelType, base: BaseModelType = BaseModelType.Any) -> ModelIdentifierField: + return ModelIdentifierField(key=key, hash=f"hash-{key}", name=key, base=base, type=model_type) + + +def _invocation() -> Krea2TextEncoderInvocation: + encoder = _identifier("encoder", ModelType.Qwen3VLEncoder) + lora = _identifier("lora", ModelType.LoRA, BaseModelType.Krea2) + field = Qwen3VLEncoderField( + tokenizer=encoder.model_copy(update={"submodel_type": SubModelType.Tokenizer}), + text_encoder=encoder.model_copy(update={"submodel_type": SubModelType.TextEncoder}), + loras=[LoRAField(lora=lora, weight=0.5)], + ) + return Krea2TextEncoderInvocation.model_construct(prompt="a prompt", qwen3_vl_encoder=field) + + +def _context(lora_model) -> SimpleNamespace: + def load(identifier): + if identifier.key == "lora": + return SimpleNamespace(model=lora_model) + if identifier.submodel_type is SubModelType.Tokenizer: + return _TokenizerInfo() + return _TextEncoderInfo() + + return SimpleNamespace( + models=SimpleNamespace(load=load), util=SimpleNamespace(signal_progress=lambda _message: None) + ) + + +def test_encode_applies_qwen3_vl_lora_and_returns_selected_hidden_layers(monkeypatch) -> None: + captured = {} + + def apply_patches(**kwargs): + captured.update(kwargs) + captured["patches"] = list(kwargs["patches"]) + return nullcontext() + + monkeypatch.setattr( + "invokeai.app.invocations.krea2_text_encoder.LayerPatcher.apply_smart_model_patches", apply_patches + ) + monkeypatch.setattr( + "invokeai.app.invocations.krea2_text_encoder.TorchDevice.choose_bfloat16_safe_dtype", + lambda _device: torch.float32, + ) + + embeds, mask = _invocation()._encode(_context(ModelPatchRaw(layers={}))) + + assert embeds.shape == (1, 6, 12, 4) + assert mask is None + assert captured["prefix"] == KREA2_LORA_QWEN3VL_PREFIX + assert captured["patches"][0][1] == 0.5 + + +def test_encode_rejects_a_loaded_non_patch_lora(monkeypatch) -> None: + def apply_patches(**kwargs): + list(kwargs["patches"]) + return nullcontext() + + monkeypatch.setattr( + "invokeai.app.invocations.krea2_text_encoder.LayerPatcher.apply_smart_model_patches", apply_patches + ) + + with pytest.raises(TypeError, match="Expected ModelPatchRaw"): + _invocation()._encode(_context(object())) + + +def test_encode_preserves_suffix_for_a_prompt_that_overflows_truncation(monkeypatch) -> None: + # Regression: a prompt longer than the tokenizer budget must NOT lose the assistant-turn suffix. The + # encoder tokenizes (prefix + prompt) with truncation and appends the suffix AFTER, so the final tokens + # always end with the suffix template (building one string and truncating it would cut the suffix off). + from invokeai.app.invocations.krea2_text_encoder import _KREA2_SUFFIX + + suffix_ids = [901, 902, 903, 904, 905] + + class _TruncatingTokenizer: + def __call__(self, text, max_length=None, truncation=False, add_special_tokens=True, return_tensors=None): + if text == _KREA2_SUFFIX: + ids = list(suffix_ids) + else: + # Body (prefix + prompt): one filler id per whitespace token, distinct from the suffix ids. + ids = [1] * len(text.split()) + if truncation and max_length is not None and len(ids) > max_length: + ids = ids[:max_length] # right truncation, as the real tokenizer does + input_ids = torch.tensor([ids], dtype=torch.long) + return SimpleNamespace(input_ids=input_ids, attention_mask=torch.ones_like(input_ids)) + + captured: dict = {} + + class _CapturingEncoder(torch.nn.Module): + def __init__(self) -> None: + super().__init__() + self.anchor = torch.nn.Parameter(torch.zeros(1)) + + def forward(self, *, input_ids, attention_mask, **_kwargs): + captured["input_ids"] = input_ids + seq = input_ids.shape[1] + hidden_states = tuple(torch.zeros((1, seq, 4)) for _ in range(36)) + return SimpleNamespace(hidden_states=hidden_states) + + encoder = _CapturingEncoder() + + class _CapturingEncoderInfo: + @contextmanager + def model_on_device(self): + yield ({}, encoder) + + class _TruncatingTokenizerInfo: + def __enter__(self): + return _TruncatingTokenizer() + + def __exit__(self, *_args): + return None + + def load(identifier): + if identifier.submodel_type is SubModelType.Tokenizer: + return _TruncatingTokenizerInfo() + return _CapturingEncoderInfo() + + context = SimpleNamespace( + models=SimpleNamespace(load=load), util=SimpleNamespace(signal_progress=lambda _message: None) + ) + + monkeypatch.setattr( + "invokeai.app.invocations.krea2_text_encoder.LayerPatcher.apply_smart_model_patches", + lambda **_kwargs: nullcontext(), + ) + monkeypatch.setattr( + "invokeai.app.invocations.krea2_text_encoder.TorchDevice.choose_bfloat16_safe_dtype", + lambda _device: torch.float32, + ) + + encoder_id = _identifier("encoder", ModelType.Qwen3VLEncoder) + field = Qwen3VLEncoderField( + tokenizer=encoder_id.model_copy(update={"submodel_type": SubModelType.Tokenizer}), + text_encoder=encoder_id.model_copy(update={"submodel_type": SubModelType.TextEncoder}), + loras=[], + ) + long_prompt = " ".join(["word"] * 2000) # far exceeds the ~541-token budget + invocation = Krea2TextEncoderInvocation.model_construct(prompt=long_prompt, qwen3_vl_encoder=field) + + invocation._encode(context) + + final_ids = captured["input_ids"][0].tolist() + # The suffix survives at the very end even though the body was truncated. + assert final_ids[-len(suffix_ids) :] == suffix_ids + # And the body really was truncated (total = reserved budget + suffix), proving append-after-truncate. + assert len(final_ids) > len(suffix_ids) diff --git a/tests/app/invocations/test_qwen_image_working_memory.py b/tests/app/invocations/test_qwen_image_working_memory.py index f3dfbe970df..ea6cb875f09 100644 --- a/tests/app/invocations/test_qwen_image_working_memory.py +++ b/tests/app/invocations/test_qwen_image_working_memory.py @@ -112,6 +112,47 @@ def test_qwen_latents_to_image_requests_working_memory(self): assert mock_estimate.call_args.kwargs["operation"] == "decode" mock_vae_info.model_on_device.assert_called_once_with(working_mem_bytes=expected_memory) + def test_seamless_patch_is_applied_to_converted_anima_vae(self): + original_vae, mock_vae_info = self._mock_vae_info() + converted_vae = MagicMock(spec=AutoencoderKLQwenImage) + converted_vae.parameters.return_value = iter([torch.zeros(1)]) + converted_vae.dtype = torch.float32 + converted_vae.config.z_dim = 16 + converted_vae.config.latents_mean = [0.0] * 16 + converted_vae.config.latents_std = [1.0] * 16 + converted_vae.decode.side_effect = RuntimeError("stop after seamless patch") + mock_context = MagicMock() + mock_context.models.load.return_value = mock_vae_info + mock_context.tensors.load.return_value = torch.zeros(1, 16, 1, 2, 2) + + with ( + patch( + "invokeai.app.invocations.qwen_image_latents_to_image.estimate_vae_working_memory_qwen_image", + return_value=1, + ), + patch( + "invokeai.app.invocations.qwen_image_latents_to_image.as_qwen_image_vae", + return_value=converted_vae, + ), + patch( + "invokeai.app.invocations.qwen_image_latents_to_image.SeamlessExt.static_patch_model", + return_value=nullcontext(), + ) as patch_seamless, + patch( + "invokeai.app.invocations.qwen_image_latents_to_image.TorchDevice.choose_torch_device", + return_value=torch.device("cpu"), + ), + ): + invocation = QwenImageLatentsToImageInvocation.model_construct( + latents=MagicMock(latents_name="test_latents"), + vae=MagicMock(vae=MagicMock(), seamless_axes=["x"]), + ) + with pytest.raises(RuntimeError, match="stop after seamless patch"): + invocation.invoke(mock_context) + + assert original_vae is not converted_vae + patch_seamless.assert_called_once_with(converted_vae, ["x"]) + def test_qwen_image_to_latents_requests_working_memory(self): """QwenImageImageToLatentsInvocation estimates encode memory and passes it to the cache.""" mock_vae, mock_vae_info = self._mock_vae_info() diff --git a/tests/backend/krea2/test_vae_compat.py b/tests/backend/krea2/test_vae_compat.py new file mode 100644 index 00000000000..7eb450df12a --- /dev/null +++ b/tests/backend/krea2/test_vae_compat.py @@ -0,0 +1,8 @@ +import pytest + +from invokeai.backend.krea2.vae_compat import as_qwen_image_vae + + +def test_as_qwen_image_vae_rejects_incompatible_model() -> None: + with pytest.raises(TypeError, match="Expected AutoencoderKLQwenImage or AutoencoderKLWan"): + as_qwen_image_vae(object()) diff --git a/tests/backend/model_manager/configs/test_krea2_lora_config.py b/tests/backend/model_manager/configs/test_krea2_lora_config.py new file mode 100644 index 00000000000..9ac55872a44 --- /dev/null +++ b/tests/backend/model_manager/configs/test_krea2_lora_config.py @@ -0,0 +1,77 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError +from invokeai.backend.model_manager.configs.lora import LoRA_LyCORIS_Krea2_Config +from invokeai.backend.model_manager.taxonomy import BaseModelType + +_REQUIRED_FIELDS = { + "hash": "blake3:fakehash", + "path": "/fake/models/krea2-lora.safetensors", + "file_size": 1000, + "name": "krea2-lora", + "description": "test", + "source": "test", + "source_type": "path", + "key": "test-key", +} + + +def _ambiguous_transformer_only_lora() -> MagicMock: + mod = MagicMock() + mod.load_state_dict.return_value = { + "transformer.transformer_blocks.0.attn.to_q.lora_A.weight": object(), + "transformer.transformer_blocks.0.attn.to_q.lora_B.weight": object(), + } + return mod + + +def _ambiguous_text_encoder_only_lora() -> MagicMock: + mod = MagicMock() + mod.load_state_dict.return_value = { + "text_encoder.language_model.layers.0.self_attn.q_proj.lora_A.weight": object(), + "text_encoder.language_model.layers.0.self_attn.q_proj.lora_B.weight": object(), + } + return mod + + +@patch("invokeai.backend.model_manager.configs.lora.raise_if_not_file") +def test_explicit_krea2_override_accepts_ambiguous_transformer_only_lora(_raise_if_not_file) -> None: + config = LoRA_LyCORIS_Krea2_Config.from_model_on_disk( + _ambiguous_transformer_only_lora(), {**_REQUIRED_FIELDS, "base": BaseModelType.Krea2} + ) + + assert config.base is BaseModelType.Krea2 + + +@patch("invokeai.backend.model_manager.configs.lora.raise_if_not_file") +def test_automatic_probe_rejects_ambiguous_transformer_only_lora(_raise_if_not_file) -> None: + with pytest.raises(NotAMatchError): + LoRA_LyCORIS_Krea2_Config.from_model_on_disk(_ambiguous_transformer_only_lora(), {**_REQUIRED_FIELDS}) + + +@patch("invokeai.backend.model_manager.configs.lora.raise_if_not_file") +def test_explicit_krea2_override_rejects_incomplete_lora_pair(_raise_if_not_file) -> None: + mod = MagicMock() + mod.load_state_dict.return_value = { + "transformer.transformer_blocks.0.attn.to_q.lora_A.weight": object(), + } + + with pytest.raises(NotAMatchError): + LoRA_LyCORIS_Krea2_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS, "base": BaseModelType.Krea2}) + + +@patch("invokeai.backend.model_manager.configs.lora.raise_if_not_file") +def test_explicit_krea2_override_accepts_text_encoder_only_lora(_raise_if_not_file) -> None: + config = LoRA_LyCORIS_Krea2_Config.from_model_on_disk( + _ambiguous_text_encoder_only_lora(), {**_REQUIRED_FIELDS, "base": BaseModelType.Krea2} + ) + + assert config.base is BaseModelType.Krea2 + + +@patch("invokeai.backend.model_manager.configs.lora.raise_if_not_file") +def test_automatic_probe_rejects_ambiguous_text_encoder_only_lora(_raise_if_not_file) -> None: + with pytest.raises(NotAMatchError): + LoRA_LyCORIS_Krea2_Config.from_model_on_disk(_ambiguous_text_encoder_only_lora(), {**_REQUIRED_FIELDS}) diff --git a/tests/backend/model_manager/configs/test_krea2_main_config.py b/tests/backend/model_manager/configs/test_krea2_main_config.py new file mode 100644 index 00000000000..f19ee8f0aaf --- /dev/null +++ b/tests/backend/model_manager/configs/test_krea2_main_config.py @@ -0,0 +1,249 @@ +"""Tests for Krea-2 main-model identification and variant detection. + +Krea-2-Turbo (distilled) and Krea-2-Raw (Base, undistilled) share the IDENTICAL transformer +architecture, so a single-file/GGUF checkpoint cannot be told apart from its weights. Detection: + +1. Explicit ``variant`` in override_fields always wins. +2. Diffusers pipelines read the pipeline-level ``is_distilled`` flag from model_index.json + (``false`` → Base, ``true``/absent → Turbo). +3. Single-file / GGUF fall back to a filename heuristic ("raw"/"base" → Base, else Turbo). +""" + +import json +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest.mock import MagicMock, patch + +import pytest + +from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError +from invokeai.backend.model_manager.configs.main import ( + MainModelDefaultSettings, + _get_krea2_variant_from_name, + _has_krea2_keys, +) +from invokeai.backend.model_manager.taxonomy import BaseModelType, Krea2VariantType + +# Required fields for the Pydantic config models (mirrors the other config-probe tests). +_REQUIRED_FIELDS = { + "hash": "blake3:fakehash", + "path": "/fake/models/test", + "file_size": 1000, + "name": "test-model", + "description": "test", + "source": "test", + "source_type": "path", + "key": "test-key", +} + + +class TestKrea2VariantFromName: + """The filename heuristic used for single-file / GGUF Krea-2 checkpoints.""" + + @pytest.mark.parametrize( + "name, expected", + [ + ("Krea-2-Raw", Krea2VariantType.Base), + ("krea2_raw_q4.gguf", Krea2VariantType.Base), + ("Krea-2-RAW-Q8_0.gguf", Krea2VariantType.Base), # case-insensitive + ("krea2_base_fp8_scaled.safetensors", Krea2VariantType.Base), + ("Krea-2-Turbo", Krea2VariantType.Turbo), + ("krea2_turbo-Q3_K_M.gguf", Krea2VariantType.Turbo), + ("Krea-2-Turbo-Q4_K_M.gguf", Krea2VariantType.Turbo), + ("some-random-name.safetensors", Krea2VariantType.Turbo), # default + # "turbo" wins outright, so a Turbo file whose name merely contains "base"/"raw" as a + # substring is not misread as Base. + ("krea2_turbo_baseline_q4.gguf", Krea2VariantType.Turbo), + ("krea2-turbo-raw-export.safetensors", Krea2VariantType.Turbo), + # "base"/"raw" only match as whole tokens, not arbitrary substrings. + ("krea2_database_model.safetensors", Krea2VariantType.Turbo), + ("krea2_baseline_q8.gguf", Krea2VariantType.Turbo), + ], + ) + def test_variant_from_name(self, name: str, expected: Krea2VariantType) -> None: + assert _get_krea2_variant_from_name(name) == expected + + +class TestHasKrea2Keys: + """The Krea-2 transformer state-dict signature (diffusers + native/GGUF naming).""" + + def test_diffusers_naming_matches(self) -> None: + sd = { + "text_fusion.layerwise_blocks.0.attn.to_q.weight": object(), + "img_in.weight": object(), + "transformer_blocks.0.attn.to_q.weight": object(), + } + assert _has_krea2_keys(sd) is True + + def test_native_gguf_naming_matches(self) -> None: + # Compact ComfyUI/GGUF naming: txtfusion + first / tproj. + sd = { + "txtfusion.0.attn.wq.weight": object(), + "first.weight": object(), + "tproj.1.weight": object(), + } + assert _has_krea2_keys(sd) is True + + def test_comfyui_prefixed_keys_match(self) -> None: + sd = { + "model.diffusion_model.text_fusion.projector.weight": object(), + "model.diffusion_model.img_in.weight": object(), + } + assert _has_krea2_keys(sd) is True + + def test_text_fusion_without_corroborator_does_not_match(self) -> None: + # text-fusion alone is not enough — an image-input corroborator is required. + sd = {"text_fusion.projector.weight": object()} + assert _has_krea2_keys(sd) is False + + def test_non_krea2_state_dict_does_not_match(self) -> None: + sd = {"double_blocks.0.img_attn.qkv.weight": object(), "img_in.weight": object()} + assert _has_krea2_keys(sd) is False + + def test_lora_is_rejected(self) -> None: + # LoRA suffixes must never be classified as a Krea-2 main model. + sd = { + "text_fusion.layerwise_blocks.0.attn.to_q.lora_down.weight": object(), + "img_in.lora_up.weight": object(), + } + assert _has_krea2_keys(sd) is False + + +class TestKrea2GGUFVariantDetection: + """Main_GGUF_Krea2_Config: variant from filename, explicit override wins.""" + + def _make_mock_mod(self, filename: str) -> MagicMock: + mod = MagicMock() + mod.path = Path(f"/fake/models/{filename}") + mod.load_state_dict.return_value = {} + return mod + + @patch("invokeai.backend.model_manager.configs.main._has_krea2_keys", return_value=True) + @patch("invokeai.backend.model_manager.configs.main._has_ggml_tensors", return_value=True) + @patch("invokeai.backend.model_manager.configs.main.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.main.raise_for_override_fields") + def test_raw_filename_sets_base_variant(self, _rfo, _rif, _hgt, _hkk) -> None: + from invokeai.backend.model_manager.configs.main import Main_GGUF_Krea2_Config + + mod = self._make_mock_mod("Krea-2-Raw-Q4_K_M.gguf") + config = Main_GGUF_Krea2_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + assert config.variant == Krea2VariantType.Base + + @patch("invokeai.backend.model_manager.configs.main._has_krea2_keys", return_value=True) + @patch("invokeai.backend.model_manager.configs.main._has_ggml_tensors", return_value=True) + @patch("invokeai.backend.model_manager.configs.main.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.main.raise_for_override_fields") + def test_turbo_filename_defaults_to_turbo(self, _rfo, _rif, _hgt, _hkk) -> None: + from invokeai.backend.model_manager.configs.main import Main_GGUF_Krea2_Config + + mod = self._make_mock_mod("krea2_turbo-Q3_K_M.gguf") + config = Main_GGUF_Krea2_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + assert config.variant == Krea2VariantType.Turbo + + @patch("invokeai.backend.model_manager.configs.main._has_krea2_keys", return_value=True) + @patch("invokeai.backend.model_manager.configs.main._has_ggml_tensors", return_value=True) + @patch("invokeai.backend.model_manager.configs.main.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.main.raise_for_override_fields") + def test_explicit_variant_override_wins(self, _rfo, _rif, _hgt, _hkk) -> None: + from invokeai.backend.model_manager.configs.main import Main_GGUF_Krea2_Config + + # Filename says Raw, but an explicit Turbo override must not be overwritten. + mod = self._make_mock_mod("Krea-2-Raw-Q4_K_M.gguf") + config = Main_GGUF_Krea2_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS, "variant": Krea2VariantType.Turbo}) + assert config.variant == Krea2VariantType.Turbo + + +class TestKrea2CheckpointVariantDetection: + """Main_Checkpoint_Krea2_Config: variant from filename (non-GGUF single file).""" + + def _make_mock_mod(self, filename: str) -> MagicMock: + mod = MagicMock() + mod.path = Path(f"/fake/models/{filename}") + mod.load_state_dict.return_value = {} + return mod + + @patch("invokeai.backend.model_manager.configs.main._has_krea2_keys", return_value=True) + @patch("invokeai.backend.model_manager.configs.main._has_ggml_tensors", return_value=False) + @patch("invokeai.backend.model_manager.configs.main.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.main.raise_for_override_fields") + def test_raw_filename_sets_base_variant(self, _rfo, _rif, _hgt, _hkk) -> None: + from invokeai.backend.model_manager.configs.main import Main_Checkpoint_Krea2_Config + + mod = self._make_mock_mod("krea2_raw_fp8_scaled.safetensors") + config = Main_Checkpoint_Krea2_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + assert config.variant == Krea2VariantType.Base + + @patch("invokeai.backend.model_manager.configs.main._has_krea2_keys", return_value=True) + @patch("invokeai.backend.model_manager.configs.main._has_ggml_tensors", return_value=False) + @patch("invokeai.backend.model_manager.configs.main.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.main.raise_for_override_fields") + def test_turbo_filename_defaults_to_turbo(self, _rfo, _rif, _hgt, _hkk) -> None: + from invokeai.backend.model_manager.configs.main import Main_Checkpoint_Krea2_Config + + mod = self._make_mock_mod("krea2_turbo_fp8_scaled.safetensors") + config = Main_Checkpoint_Krea2_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + assert config.variant == Krea2VariantType.Turbo + + @patch("invokeai.backend.model_manager.configs.main._has_krea2_keys", return_value=True) + @patch("invokeai.backend.model_manager.configs.main._has_ggml_tensors", return_value=False) + @patch("invokeai.backend.model_manager.configs.main.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.main.raise_for_override_fields") + def test_rejects_non_safetensors_checkpoint(self, _rfo, _rif, _hgt, _hkk) -> None: + from invokeai.backend.model_manager.configs.main import Main_Checkpoint_Krea2_Config + + mod = self._make_mock_mod("krea2_turbo.bin") + + with pytest.raises(NotAMatchError, match="safetensors"): + Main_Checkpoint_Krea2_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + + +class TestKrea2DiffusersVariantDetection: + """Main_Diffusers_Krea2_Config._get_variant reads is_distilled from model_index.json.""" + + def _make_mock_mod_with_model_index(self, tmpdir: str, model_index: dict) -> MagicMock: + path = Path(tmpdir) + (path / "model_index.json").write_text(json.dumps(model_index)) + mod = MagicMock() + mod.path = path + return mod + + def test_is_distilled_false_is_base(self) -> None: + from invokeai.backend.model_manager.configs.main import Main_Diffusers_Krea2_Config + + with TemporaryDirectory() as tmpdir: + mod = self._make_mock_mod_with_model_index(tmpdir, {"_class_name": "Krea2Pipeline", "is_distilled": False}) + assert Main_Diffusers_Krea2_Config._get_variant(mod) == Krea2VariantType.Base + + def test_is_distilled_true_is_turbo(self) -> None: + from invokeai.backend.model_manager.configs.main import Main_Diffusers_Krea2_Config + + with TemporaryDirectory() as tmpdir: + mod = self._make_mock_mod_with_model_index(tmpdir, {"_class_name": "Krea2Pipeline", "is_distilled": True}) + assert Main_Diffusers_Krea2_Config._get_variant(mod) == Krea2VariantType.Turbo + + def test_is_distilled_absent_defaults_to_base(self) -> None: + from invokeai.backend.model_manager.configs.main import Main_Diffusers_Krea2_Config + + with TemporaryDirectory() as tmpdir: + mod = self._make_mock_mod_with_model_index(tmpdir, {"_class_name": "Krea2Pipeline"}) + assert Main_Diffusers_Krea2_Config._get_variant(mod) == Krea2VariantType.Base + + +class TestKrea2DefaultSettings: + """Per-variant default generation settings.""" + + def test_turbo_defaults(self) -> None: + ds = MainModelDefaultSettings.from_base(BaseModelType.Krea2, Krea2VariantType.Turbo) + assert ds is not None + assert ds.steps == 8 + assert ds.cfg_scale == 1.0 + assert ds.width == 1024 + assert ds.height == 1024 + + def test_base_defaults(self) -> None: + ds = MainModelDefaultSettings.from_base(BaseModelType.Krea2, Krea2VariantType.Base) + assert ds is not None + assert ds.cfg_scale == 5.5 + assert ds.steps == 28 + assert ds.width == 1024 + assert ds.height == 1024 diff --git a/tests/backend/model_manager/configs/test_model_path_validation.py b/tests/backend/model_manager/configs/test_model_path_validation.py new file mode 100644 index 00000000000..5eb807c2b87 --- /dev/null +++ b/tests/backend/model_manager/configs/test_model_path_validation.py @@ -0,0 +1,69 @@ +import json +from pathlib import Path + +import pytest + +from invokeai.backend.model_manager.configs.factory import _MAX_FILES_IN_MODEL_DIR, ModelConfigFactory + + +def _fill_directory(path: Path) -> None: + for index in range(_MAX_FILES_IN_MODEL_DIR + 1): + (path / f"asset-{index}.txt").touch() + + +def test_large_directory_with_generic_config_is_rejected(tmp_path: Path) -> None: + (tmp_path / "config.json").write_text(json.dumps({"application": "not-a-model"})) + _fill_directory(tmp_path) + + with pytest.raises(ValueError, match="general-purpose directory"): + ModelConfigFactory._validate_path_looks_like_model(tmp_path) + + +def test_large_directory_with_non_object_config_is_rejected(tmp_path: Path) -> None: + (tmp_path / "config.json").write_text("[]") + _fill_directory(tmp_path) + + with pytest.raises(ValueError, match="general-purpose directory"): + ModelConfigFactory._validate_path_looks_like_model(tmp_path) + + +@pytest.mark.parametrize( + "config", + [ + {"model_type": "application"}, + {"architectures": ["ApplicationService"]}, + {"_class_name": "ApplicationPipeline"}, + ], +) +def test_large_directory_with_unrecognized_model_markers_is_rejected(tmp_path: Path, config: dict) -> None: + (tmp_path / "config.json").write_text(json.dumps(config)) + _fill_directory(tmp_path) + + with pytest.raises(ValueError, match="general-purpose directory"): + ModelConfigFactory._validate_path_looks_like_model(tmp_path) + + +def test_large_directory_with_transformers_config_is_accepted(tmp_path: Path) -> None: + (tmp_path / "config.json").write_text(json.dumps({"architectures": ["Qwen3VLModel"]})) + _fill_directory(tmp_path) + + ModelConfigFactory._validate_path_looks_like_model(tmp_path) + + +def test_large_directory_with_model_index_is_accepted(tmp_path: Path) -> None: + (tmp_path / "model_index.json").write_text(json.dumps({"_class_name": "Krea2Pipeline"})) + _fill_directory(tmp_path) + + ModelConfigFactory._validate_path_looks_like_model(tmp_path) + + +def test_directory_with_utf8_non_ascii_config_is_accepted(tmp_path: Path) -> None: + # config.json files are UTF-8. Reading them with the platform default encoding (cp1252 on Windows) + # raises UnicodeDecodeError on non-ASCII bytes, which gets swallowed as "unrecognized" and wrongly + # rejects a valid model directory. The config must be read explicitly as UTF-8. + (tmp_path / "config.json").write_text( + json.dumps({"architectures": ["Qwen3VLModel"], "description": "café — ünïcödé 模型"}, ensure_ascii=False), + encoding="utf-8", + ) + + ModelConfigFactory._validate_path_looks_like_model(tmp_path) diff --git a/tests/backend/model_manager/configs/test_qwen3_encoder_config.py b/tests/backend/model_manager/configs/test_qwen3_encoder_config.py index 2574b7433a8..b6d05724eb3 100644 --- a/tests/backend/model_manager/configs/test_qwen3_encoder_config.py +++ b/tests/backend/model_manager/configs/test_qwen3_encoder_config.py @@ -12,12 +12,16 @@ import json from pathlib import Path from tempfile import TemporaryDirectory -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pytest from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError -from invokeai.backend.model_manager.configs.qwen3_encoder import Qwen3Encoder_Qwen3Encoder_Config +from invokeai.backend.model_manager.configs.qwen3_encoder import ( + Qwen3Encoder_Checkpoint_Config, + Qwen3Encoder_Qwen3Encoder_Config, + _has_qwen_vl_visual_tower, +) _OVERRIDE_FIELDS: dict[str, object] = { "hash": "blake3:fakehash", @@ -63,6 +67,44 @@ def test_standalone_text_encoder_subfolder_still_matches() -> None: assert config.type.value == "qwen3_encoder" +class TestQwen3EncoderRejectsVisualTower: + """Regression for the single-file Qwen3-VL dual-match bug. + + A nested-layout single-file Qwen3-VL 4B encoder (``model.layers.*`` + ``model.visual.*``) previously + matched BOTH the text-only Qwen3Encoder checkpoint config and the Qwen3VLEncoder config, because the + reject helper only looked for bare ``visual.blocks.*``. The tie-break is nondeterministic, so the + encoder could register as Qwen3Encoder and vanish from Krea-2's Qwen3-VL dropdown - hard-blocking the + single-file/GGUF Krea-2 install path. The reject helper now matches the nested ``model.visual.*`` + layout too, keeping the two configs mutually exclusive. + """ + + @pytest.mark.parametrize( + "visual_key", + [ + "model.visual.blocks.0.attn.qkv.weight", # ComfyUI single-file nested layout + "visual.blocks.0.attn.qkv.weight", # already-split (transformers) layout + "model.visual.patch_embed.proj.weight", + ], + ) + def test_matches_nested_and_split_visual_layouts(self, visual_key: str) -> None: + assert _has_qwen_vl_visual_tower({visual_key: object()}) is True + + def test_ignores_text_only_decoder(self) -> None: + assert _has_qwen_vl_visual_tower({"model.layers.0.self_attn.q_proj.weight": object()}) is False + + @patch("invokeai.backend.model_manager.configs.qwen3_encoder.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.qwen3_encoder.raise_for_override_fields") + def test_checkpoint_config_rejects_nested_qwen3_vl(self, _rfo, _rif) -> None: + mod = MagicMock() + mod.path = Path("/fake/qwen3vl_4b.safetensors") + mod.load_state_dict.return_value = { + "model.layers.0.self_attn.q_proj.weight": object(), + "model.visual.blocks.0.attn.qkv.weight": object(), + } + with pytest.raises(NotAMatchError, match="visual tower"): + Qwen3Encoder_Checkpoint_Config.from_model_on_disk(mod, dict(_OVERRIDE_FIELDS)) + + def test_nested_text_encoder_with_root_tokenizer_still_matches() -> None: """A model with text_encoder/config.json should match even if tokenizer files exist at root. diff --git a/tests/backend/model_manager/configs/test_qwen3_vl_encoder_config.py b/tests/backend/model_manager/configs/test_qwen3_vl_encoder_config.py new file mode 100644 index 00000000000..223daa7682b --- /dev/null +++ b/tests/backend/model_manager/configs/test_qwen3_vl_encoder_config.py @@ -0,0 +1,283 @@ +"""Tests for Qwen3-VL text-encoder identification (used by Krea-2). + +A single-file Qwen3-VL encoder is distinguished from the text-only ``Qwen3Encoder`` (Z-Image / +FLUX.2 Klein) by the presence of the Qwen3-VL **visual tower** (``visual.*`` / ``model.visual.*``). +Both have a Qwen3 text decoder (``model.layers.*``), so the visual tower is the deciding signal. +""" + +import json +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest + +from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError +from invokeai.backend.model_manager.configs.qwen3_vl_encoder import ( + Qwen3VLEncoder_Checkpoint_Config, + Qwen3VLEncoder_Qwen3VLEncoder_Config, + _is_qwen3_vl_encoder_state_dict, +) +from invokeai.backend.model_manager.model_on_disk import ModelOnDisk +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType + +_REQUIRED_FIELDS = { + "hash": "blake3:fakehash", + "path": "/fake/models/qwen3vl.safetensors", + "file_size": 1000, + "name": "qwen3vl-encoder", + "description": "test", + "source": "test", + "source_type": "path", + "key": "test-key", +} + + +class TestIsQwen3VLEncoderStateDict: + def test_text_decoder_plus_visual_tower_matches(self) -> None: + # ComfyUI single-file layout (implicit LM prefix): model.layers.* + model.visual.* + sd = { + "model.layers.0.self_attn.q_proj.weight": object(), + "model.visual.blocks.0.attn.qkv.weight": object(), + } + assert _is_qwen3_vl_encoder_state_dict(sd) is True + + def test_explicit_language_model_prefix_matches(self) -> None: + # Alternative single-file layout (explicit LM prefix): model.language_model.layers.* + model.visual.* + sd = { + "model.language_model.layers.0.self_attn.q_proj.weight": object(), + "model.visual.blocks.0.attn.qkv.weight": object(), + } + assert _is_qwen3_vl_encoder_state_dict(sd) is True + + def test_text_only_decoder_does_not_match(self) -> None: + # Z-Image / FLUX.2 Klein Qwen3 text encoder: text decoder but NO visual tower. + sd = { + "model.layers.0.self_attn.q_proj.weight": object(), + "model.layers.0.mlp.down_proj.weight": object(), + "model.norm.weight": object(), + } + assert _is_qwen3_vl_encoder_state_dict(sd) is False + + def test_visual_tower_only_does_not_match(self) -> None: + sd = {"model.visual.blocks.0.attn.qkv.weight": object()} + assert _is_qwen3_vl_encoder_state_dict(sd) is False + + def test_ignores_non_string_keys(self) -> None: + sd: dict = {0: object(), 1: object()} + assert _is_qwen3_vl_encoder_state_dict(sd) is False + + +class TestQwen3VLEncoderCheckpointConfig: + def _make_mock_mod(self, state_dict: dict, suffix: str = ".safetensors") -> MagicMock: + mod = MagicMock() + mod.path = Path(f"/fake/qwen3vl{suffix}") + mod.load_state_dict.return_value = state_dict + return mod + + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_for_override_fields") + def test_matches_vl_single_file(self, _rfo, _rif) -> None: + mod = self._make_mock_mod( + { + "model.embed_tokens.weight": MagicMock(shape=(151936, 2560)), + "model.layers.35.self_attn.q_proj.weight": object(), + "model.visual.blocks.0.attn.qkv.weight": object(), + } + ) + config = Qwen3VLEncoder_Checkpoint_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + assert config.type == ModelType.Qwen3VLEncoder + assert config.base == BaseModelType.Any + assert config.format == ModelFormat.Checkpoint + + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_for_override_fields") + def test_rejects_text_only_encoder(self, _rfo, _rif) -> None: + mod = self._make_mock_mod( + { + "model.layers.0.self_attn.q_proj.weight": object(), + "model.norm.weight": object(), + } + ) + with pytest.raises(NotAMatchError): + Qwen3VLEncoder_Checkpoint_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_for_override_fields") + def test_rejects_non_safetensors_checkpoint(self, _rfo, _rif) -> None: + mod = self._make_mock_mod( + { + "model.layers.35.self_attn.q_proj.weight": object(), + "model.visual.blocks.0.attn.qkv.weight": object(), + }, + suffix=".bin", + ) + + with pytest.raises(NotAMatchError, match="safetensors"): + Qwen3VLEncoder_Checkpoint_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_if_not_file") + @patch("invokeai.backend.model_manager.configs.qwen3_vl_encoder.raise_for_override_fields") + def test_rejects_non_4b_checkpoint_shape(self, _rfo, _rif) -> None: + mod = self._make_mock_mod( + { + "model.embed_tokens.weight": MagicMock(shape=(151936, 4096)), + "model.layers.35.self_attn.q_proj.weight": object(), + "model.visual.blocks.0.attn.qkv.weight": object(), + } + ) + + with pytest.raises(NotAMatchError, match="4B|hidden"): + Qwen3VLEncoder_Checkpoint_Config.from_model_on_disk(mod, {**_REQUIRED_FIELDS}) + + +class TestQwen3VLEncoderDirectoryConfig: + @staticmethod + def _write_config(path: Path, *, hidden_size: int = 2560, num_hidden_layers: int = 36) -> None: + path.write_text( + json.dumps( + { + "architectures": ["Qwen3VLModel"], + "text_config": {"hidden_size": hidden_size, "num_hidden_layers": num_hidden_layers}, + } + ) + ) + + @staticmethod + def _fields(path: Path) -> dict: + return {**_REQUIRED_FIELDS, "path": path.as_posix()} + + def test_accepts_direct_layout_with_weights_and_tokenizer(self, tmp_path: Path) -> None: + self._write_config(tmp_path / "config.json") + (tmp_path / "model.safetensors").touch() + (tmp_path / "tokenizer.json").touch() + + config = Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + assert config.type is ModelType.Qwen3VLEncoder + + @pytest.mark.parametrize(("include_weights", "include_tokenizer"), [(False, True), (True, False), (False, False)]) + def test_rejects_incomplete_direct_layout( + self, tmp_path: Path, include_weights: bool, include_tokenizer: bool + ) -> None: + self._write_config(tmp_path / "config.json") + if include_weights: + (tmp_path / "model.safetensors").touch() + if include_tokenizer: + (tmp_path / "tokenizer.json").touch() + + with pytest.raises(NotAMatchError, match="weights|tokenizer"): + Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + def test_accepts_nested_layout_with_weights_and_tokenizer(self, tmp_path: Path) -> None: + text_encoder = tmp_path / "text_encoder" + tokenizer = tmp_path / "tokenizer" + text_encoder.mkdir() + tokenizer.mkdir() + self._write_config(text_encoder / "config.json") + (text_encoder / "model.safetensors").touch() + (tokenizer / "tokenizer.json").touch() + + config = Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + assert config.format is ModelFormat.Qwen3VLEncoder + + @pytest.mark.parametrize(("hidden_size", "num_hidden_layers"), [(4096, 36), (2560, 28)]) + def test_rejects_non_4b_directory_config(self, tmp_path: Path, hidden_size: int, num_hidden_layers: int) -> None: + self._write_config(tmp_path / "config.json", hidden_size=hidden_size, num_hidden_layers=num_hidden_layers) + (tmp_path / "model.safetensors").touch() + (tmp_path / "tokenizer.json").touch() + + with pytest.raises(NotAMatchError, match="4B|hidden|layers"): + Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + def test_rejects_malformed_text_config(self, tmp_path: Path) -> None: + (tmp_path / "config.json").write_text(json.dumps({"architectures": ["Qwen3VLModel"], "text_config": []})) + (tmp_path / "model.safetensors").touch() + (tmp_path / "tokenizer.json").touch() + + with pytest.raises(NotAMatchError, match="text_config"): + Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + @pytest.mark.parametrize("artifact", ["adapter_model.safetensors", "training_args.bin"]) + def test_rejects_unrecognized_weight_artifact(self, tmp_path: Path, artifact: str) -> None: + self._write_config(tmp_path / "config.json") + (tmp_path / artifact).touch() + (tmp_path / "tokenizer.json").touch() + + with pytest.raises(NotAMatchError, match="weights"): + Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + def test_rejects_incomplete_sharded_checkpoint(self, tmp_path: Path) -> None: + self._write_config(tmp_path / "config.json") + (tmp_path / "model-00001-of-00002.safetensors").touch() + (tmp_path / "model.safetensors.index.json").write_text( + json.dumps( + { + "weight_map": { + "language_model.layers.0.weight": "model-00001-of-00002.safetensors", + "language_model.layers.35.weight": "model-00002-of-00002.safetensors", + } + } + ) + ) + (tmp_path / "tokenizer.json").touch() + + with pytest.raises(NotAMatchError, match="missing|weights"): + Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + @pytest.mark.parametrize("index_name", ["model.safetensors.index.json", "pytorch_model.bin.index.json"]) + def test_accepts_complete_sharded_checkpoint(self, tmp_path: Path, index_name: str) -> None: + self._write_config(tmp_path / "config.json") + suffix = "safetensors" if index_name.startswith("model") else "bin" + shard_names = [f"model-00001-of-00002.{suffix}", f"model-00002-of-00002.{suffix}"] + for shard_name in shard_names: + (tmp_path / shard_name).touch() + (tmp_path / index_name).write_text( + json.dumps( + { + "weight_map": { + "language_model.layers.0.weight": shard_names[0], + "language_model.layers.35.weight": shard_names[1], + } + } + ) + ) + (tmp_path / "tokenizer.json").touch() + + config = Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + assert config.type is ModelType.Qwen3VLEncoder + + @pytest.mark.parametrize("bad_filename", [7, "../outside.safetensors", "/tmp/outside.safetensors"]) + def test_rejects_unsafe_or_non_string_shard_names(self, tmp_path: Path, bad_filename: object) -> None: + self._write_config(tmp_path / "config.json") + valid_shard = "model-00001-of-00002.safetensors" + (tmp_path / valid_shard).touch() + outside = tmp_path.parent / "outside.safetensors" + outside.touch() + (tmp_path / "model.safetensors.index.json").write_text( + json.dumps( + { + "weight_map": { + "language_model.layers.0.weight": valid_shard, + "language_model.layers.35.weight": bad_filename, + } + } + ) + ) + (tmp_path / "tokenizer.json").touch() + + with pytest.raises(NotAMatchError, match="weights|shard|index"): + Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) + + def test_rejects_existing_absolute_shard_path(self, tmp_path: Path) -> None: + self._write_config(tmp_path / "config.json") + outside = tmp_path.parent / "absolute-outside.safetensors" + outside.touch() + (tmp_path / "model.safetensors.index.json").write_text( + json.dumps({"weight_map": {"language_model.layers.0.weight": outside.as_posix()}}) + ) + (tmp_path / "tokenizer.json").touch() + + with pytest.raises(NotAMatchError, match="weights|shard|index"): + Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(ModelOnDisk(tmp_path), self._fields(tmp_path)) diff --git a/tests/backend/model_manager/load/test_diffusers_039_compatibility.py b/tests/backend/model_manager/load/test_diffusers_039_compatibility.py new file mode 100644 index 00000000000..1e305da9d7a --- /dev/null +++ b/tests/backend/model_manager/load/test_diffusers_039_compatibility.py @@ -0,0 +1,171 @@ +from inspect import signature + +import accelerate +import diffusers +import pytest +from packaging.version import Version + + +def test_pinned_diffusers_exposes_existing_and_krea_model_contracts() -> None: + assert Version(diffusers.__version__) == Version("0.39.0") + + expected_symbols = ( + "AutoencoderKLFlux2", + "FluxTransformer2DModel", + "Flux2Transformer2DModel", + "Krea2Transformer2DModel", + "QwenImageTransformer2DModel", + "StableDiffusionPipeline", + "StableDiffusionXLPipeline", + "ZImageTransformer2DModel", + ) + for symbol in expected_symbols: + assert getattr(diffusers, symbol, None) is not None, f"diffusers is missing {symbol}" + + +def test_flow_match_scheduler_keeps_custom_sigma_and_shift_api() -> None: + parameters = signature(diffusers.FlowMatchEulerDiscreteScheduler.set_timesteps).parameters + + assert "sigmas" in parameters + assert "mu" in parameters + assert "device" in parameters + + +@pytest.mark.parametrize( + "factory", + [ + pytest.param( + lambda: diffusers.FluxTransformer2DModel( + in_channels=4, + num_layers=1, + num_single_layers=1, + attention_head_dim=4, + num_attention_heads=1, + joint_attention_dim=8, + pooled_projection_dim=8, + axes_dims_rope=(1, 1, 2), + ), + id="flux", + ), + pytest.param( + lambda: diffusers.Flux2Transformer2DModel( + in_channels=4, + num_layers=1, + num_single_layers=1, + attention_head_dim=4, + num_attention_heads=1, + joint_attention_dim=8, + timestep_guidance_channels=4, + mlp_ratio=2, + axes_dims_rope=(1, 1, 1, 1), + ), + id="flux2", + ), + pytest.param( + lambda: diffusers.QwenImageTransformer2DModel( + patch_size=1, + in_channels=4, + out_channels=4, + num_layers=1, + attention_head_dim=8, + num_attention_heads=1, + joint_attention_dim=8, + axes_dims_rope=(2, 2, 4), + ), + id="qwen-image", + ), + pytest.param( + lambda: diffusers.ZImageTransformer2DModel( + all_patch_size=(1,), + all_f_patch_size=(1,), + in_channels=4, + dim=8, + n_layers=1, + n_refiner_layers=1, + n_heads=1, + n_kv_heads=1, + cap_feat_dim=8, + axes_dims=[2, 2, 4], + axes_lens=[8, 8, 8], + ), + id="z-image", + ), + pytest.param( + lambda: diffusers.Krea2Transformer2DModel( + in_channels=4, + num_layers=1, + attention_head_dim=8, + num_attention_heads=1, + num_key_value_heads=1, + intermediate_size=16, + timestep_embed_dim=8, + text_hidden_dim=8, + num_text_layers=2, + text_num_attention_heads=1, + text_num_key_value_heads=1, + text_intermediate_size=16, + num_layerwise_text_blocks=1, + num_refiner_text_blocks=1, + axes_dims_rope=(2, 2, 4), + ), + id="krea-2", + ), + pytest.param( + lambda: diffusers.AutoencoderKLWan( + base_dim=4, + decoder_base_dim=4, + z_dim=4, + dim_mult=[1, 1], + num_res_blocks=1, + temperal_downsample=[False], + latents_mean=[0.0] * 4, + latents_std=[1.0] * 4, + scale_factor_temporal=1, + scale_factor_spatial=2, + ), + id="anima-vae", + ), + ], +) +def test_pinned_diffusers_constructs_representative_transformer_and_vae_configs(factory) -> None: + with accelerate.init_empty_weights(): + model = factory() + + assert model is not None + + +@pytest.mark.parametrize( + "factory", + [ + pytest.param( + lambda: diffusers.StableDiffusionPipeline( + vae=None, + text_encoder=None, + tokenizer=None, + unet=None, + scheduler=None, + safety_checker=None, + feature_extractor=None, + requires_safety_checker=False, + ), + id="stable-diffusion", + ), + pytest.param( + lambda: diffusers.StableDiffusionXLPipeline( + vae=None, + text_encoder=None, + text_encoder_2=None, + tokenizer=None, + tokenizer_2=None, + unet=None, + scheduler=None, + add_watermarker=False, + ), + id="stable-diffusion-xl", + ), + ], +) +def test_pinned_diffusers_constructs_existing_stable_diffusion_pipelines(factory) -> None: + pipeline = factory() + + assert pipeline is not None diff --git a/tests/backend/model_manager/load/test_krea2_loader_boundaries.py b/tests/backend/model_manager/load/test_krea2_loader_boundaries.py new file mode 100644 index 00000000000..cb8dfe21d7a --- /dev/null +++ b/tests/backend/model_manager/load/test_krea2_loader_boundaries.py @@ -0,0 +1,148 @@ +from types import SimpleNamespace +from unittest.mock import MagicMock + +import torch + +from invokeai.backend.model_manager.configs.main import ( + Main_Checkpoint_Krea2_Config, + Main_Diffusers_Krea2_Config, + Main_GGUF_Krea2_Config, +) +from invokeai.backend.model_manager.configs.qwen3_vl_encoder import Qwen3VLEncoder_Qwen3VLEncoder_Config +from invokeai.backend.model_manager.load.model_loaders.krea2 import ( + Krea2CheckpointModel, + Krea2DiffusersModel, + Krea2GGUFCheckpointModel, + Qwen3VLEncoderLoader, +) +from invokeai.backend.model_manager.taxonomy import Krea2VariantType, SubModelType + + +class _TinyKrea2Transformer(torch.nn.Module): + def __init__(self, **_kwargs) -> None: + super().__init__() + self.weight = torch.nn.Parameter(torch.empty(2, 2)) + + +def test_single_file_loader_constructs_and_materializes_model(monkeypatch, tmp_path) -> None: + import diffusers + import safetensors.torch + + checkpoint_path = tmp_path / "krea2.safetensors" + checkpoint_path.touch() + config = Main_Checkpoint_Krea2_Config.model_construct( + path=str(checkpoint_path), variant=Krea2VariantType.Turbo, fp8_storage=None + ) + ram_cache = SimpleNamespace(make_room=MagicMock()) + loader = object.__new__(Krea2CheckpointModel) + loader._ram_cache = ram_cache + loader._apply_fp8_layerwise_casting = lambda model, _config, _submodel: model + + monkeypatch.setattr(diffusers, "Krea2Transformer2DModel", _TinyKrea2Transformer, raising=False) + monkeypatch.setattr(safetensors.torch, "load_file", lambda _path: {"weight": torch.ones(2, 2)}) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_torch_device", + lambda: torch.device("cpu"), + ) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_bfloat16_safe_dtype", + lambda _device: torch.float32, + ) + + model = loader._load_from_singlefile(config) + + assert isinstance(model, _TinyKrea2Transformer) + assert model.weight.device.type == "cpu" + assert torch.equal(model.weight, torch.ones(2, 2)) + ram_cache.make_room.assert_called_once() + + +def test_diffusers_loader_reaches_transformer_from_pretrained(monkeypatch, tmp_path) -> None: + config = Main_Diffusers_Krea2_Config.model_construct(path=str(tmp_path), repo_variant=None) + loader = object.__new__(Krea2DiffusersModel) + loaded_model = object() + load_class = SimpleNamespace(from_pretrained=MagicMock(return_value=loaded_model)) + loader.get_hf_load_class = lambda _path, _submodel: load_class + loader._apply_fp8_layerwise_casting = lambda model, _config, _submodel: model + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_torch_device", + lambda: torch.device("cpu"), + ) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_bfloat16_safe_dtype", + lambda _device: torch.float32, + ) + + model = loader._load_model(config, SubModelType.Transformer) + + assert model is loaded_model + load_class.from_pretrained.assert_called_once_with( + tmp_path / "transformer", torch_dtype=torch.float32, variant=None + ) + + +def test_gguf_loader_constructs_and_materializes_model(monkeypatch, tmp_path) -> None: + import diffusers + + checkpoint_path = tmp_path / "krea2.gguf" + checkpoint_path.touch() + config = Main_GGUF_Krea2_Config.model_construct(path=str(checkpoint_path), variant=Krea2VariantType.Turbo) + loader = object.__new__(Krea2GGUFCheckpointModel) + + monkeypatch.setattr(diffusers, "Krea2Transformer2DModel", _TinyKrea2Transformer, raising=False) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.gguf_sd_loader", + lambda _path, *, compute_dtype: {"weight": torch.ones(2, 2, dtype=compute_dtype)}, + ) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_torch_device", + lambda: torch.device("cpu"), + ) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_bfloat16_safe_dtype", + lambda _device: torch.float32, + ) + + model = loader._load_from_gguf(config) + + assert isinstance(model, _TinyKrea2Transformer) + assert model.weight.device.type == "cpu" + assert torch.equal(model.weight, torch.ones(2, 2)) + + +def test_directory_encoder_loader_reaches_transformers_from_pretrained(monkeypatch, tmp_path) -> None: + import transformers + + (tmp_path / "config.json").write_text("{}") + config = Qwen3VLEncoder_Qwen3VLEncoder_Config.model_construct(path=str(tmp_path)) + loader = object.__new__(Qwen3VLEncoderLoader) + text_config = SimpleNamespace(rope_parameters={"rope_type": "default"}, rope_scaling=None) + encoder_config = SimpleNamespace(text_config=text_config) + loaded_model = object() + from_pretrained = MagicMock(return_value=loaded_model) + + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.AutoConfig.from_pretrained", + lambda *_args, **_kwargs: encoder_config, + ) + monkeypatch.setattr(transformers.Qwen3VLModel, "from_pretrained", from_pretrained) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_torch_device", + lambda: torch.device("cpu"), + ) + monkeypatch.setattr( + "invokeai.backend.model_manager.load.model_loaders.krea2.TorchDevice.choose_bfloat16_safe_dtype", + lambda _device: torch.float32, + ) + + model = loader._load_model(config, SubModelType.TextEncoder) + + assert model is loaded_model + assert text_config.rope_scaling == text_config.rope_parameters + from_pretrained.assert_called_once_with( + tmp_path, + config=encoder_config, + torch_dtype=torch.float32, + low_cpu_mem_usage=True, + local_files_only=True, + ) diff --git a/tests/backend/model_manager/load/test_krea2_state_dict_utils.py b/tests/backend/model_manager/load/test_krea2_state_dict_utils.py new file mode 100644 index 00000000000..4b394a40b2c --- /dev/null +++ b/tests/backend/model_manager/load/test_krea2_state_dict_utils.py @@ -0,0 +1,261 @@ +"""Unit tests for the Krea-2 loader state-dict helpers. + +These cover the pure key/tensor transforms that the single-file, GGUF and Qwen3-VL encoder loaders +run before ``load_state_dict`` (prefix stripping, native<->diffusers key conversion, scaled-fp8 +dequantization, encoder key remapping) plus the shared ``_reject_incomplete_load`` guard that turns a +silent partial load into an actionable error. They exercise the conversion logic without needing the +real (diffusers ``Krea2Transformer2DModel`` / transformers ``Qwen3VLModel``) architectures or weights. +""" + +import re +from types import SimpleNamespace + +import accelerate +import pytest +import torch + +from invokeai.backend.model_manager.load.model_loaders.krea2 import ( + _convert_krea2_native_to_diffusers, + _dequantize_scaled_fp8, + _is_native_krea2_format, + _normalize_qwen3vl_rope_config, + _reject_incomplete_load, + _remap_qwen3vl_singlefile_keys, + _strip_comfyui_prefix, +) + + +class TestNormalizeQwen3vlRopeConfig: + def test_copies_rope_parameters_when_rope_scaling_is_missing(self) -> None: + rope_parameters = {"rope_type": "default", "rope_theta": 1000000.0} + text_config = SimpleNamespace(rope_parameters=rope_parameters, rope_scaling=None) + config = SimpleNamespace(text_config=text_config) + + assert _normalize_qwen3vl_rope_config(config) is config + assert text_config.rope_scaling == rope_parameters + + def test_preserves_existing_rope_scaling(self) -> None: + existing = {"rope_type": "existing"} + text_config = SimpleNamespace(rope_parameters={"rope_type": "new"}, rope_scaling=existing) + config = SimpleNamespace(text_config=text_config) + + _normalize_qwen3vl_rope_config(config) + + assert text_config.rope_scaling is existing + + def test_accepts_config_without_a_text_config(self) -> None: + config = SimpleNamespace() + assert _normalize_qwen3vl_rope_config(config) is config + + +class TestStripComfyuiPrefix: + @pytest.mark.parametrize("prefix", ["model.diffusion_model.", "diffusion_model."]) + def test_strips_known_prefixes(self, prefix: str) -> None: + sd = {f"{prefix}blocks.0.weight": torch.zeros(1), f"{prefix}first.weight": torch.zeros(1)} + out = _strip_comfyui_prefix(sd) + assert set(out.keys()) == {"blocks.0.weight", "first.weight"} + + def test_noop_when_no_prefix(self) -> None: + sd = {"blocks.0.weight": torch.zeros(1), "img_in.weight": torch.zeros(1)} + out = _strip_comfyui_prefix(sd) + assert set(out.keys()) == set(sd.keys()) + + def test_only_the_first_matching_prefix_is_used(self) -> None: + # "model.diffusion_model." is checked before "diffusion_model.", so both strip to the same tail. + sd = {"model.diffusion_model.blocks.0.weight": torch.zeros(1)} + out = _strip_comfyui_prefix(sd) + assert list(out.keys()) == ["blocks.0.weight"] + + +class TestIsNativeKrea2Format: + @pytest.mark.parametrize( + "key", + ["blocks.0.attn.wq.weight", "txtfusion.0.mlp.up.weight", "first.weight", "blocks.0.mod.lin"], + ) + def test_true_for_native_keys(self, key: str) -> None: + assert _is_native_krea2_format({key: torch.zeros(1)}) is True + + @pytest.mark.parametrize( + "key", + ["transformer_blocks.0.attn.to_q.weight", "img_in.weight", "text_fusion.0.ff.up.weight"], + ) + def test_false_for_diffusers_keys(self, key: str) -> None: + assert _is_native_krea2_format({key: torch.zeros(1)}) is False + + +class TestDequantizeScaledFp8: + def test_folds_scale_into_weight_and_drops_scale_key(self) -> None: + sd = { + "layer.weight": torch.tensor([2.0, 4.0]), + "layer.weight_scale": torch.tensor(0.5), + } + out = _dequantize_scaled_fp8(sd) + assert "layer.weight_scale" not in out + assert torch.allclose(out["layer.weight"], torch.tensor([1.0, 2.0])) + + def test_noop_without_scale_keys(self) -> None: + sd = {"layer.weight": torch.tensor([2.0, 4.0])} + out = _dequantize_scaled_fp8(sd) + assert out is sd + + def test_orphan_scale_key_is_dropped(self) -> None: + # A scale key with no matching weight is simply removed (nothing to multiply). + sd = {"other.weight": torch.tensor([1.0]), "layer.weight_scale": torch.tensor(0.5)} + out = _dequantize_scaled_fp8(sd) + assert "layer.weight_scale" not in out + assert "other.weight" in out + + +class TestConvertKrea2NativeToDiffusers: + def test_top_level_module_renames(self) -> None: + sd = { + "first.weight": torch.zeros(1), + "tmlp.0.weight": torch.zeros(1), + "tmlp.2.weight": torch.zeros(1), + "tproj.1.weight": torch.zeros(1), + "txtmlp.0.scale": torch.zeros(1), + "txtmlp.1.weight": torch.zeros(1), + "txtmlp.3.weight": torch.zeros(1), + "last.linear.weight": torch.zeros(1), + "last.norm.scale": torch.zeros(1), + } + out = _convert_krea2_native_to_diffusers(sd) + assert "img_in.weight" in out + assert "time_embed.linear_1.weight" in out + assert "time_embed.linear_2.weight" in out + assert "time_mod_proj.weight" in out + assert "txt_in.norm.weight" in out + assert "txt_in.linear_1.weight" in out + assert "txt_in.linear_2.weight" in out + assert "final_layer.linear.weight" in out + assert "final_layer.norm.weight" in out + + def test_within_block_renames(self) -> None: + sd = { + "blocks.0.attn.wq.weight": torch.zeros(1), + "blocks.0.attn.wk.weight": torch.zeros(1), + "blocks.0.attn.wv.weight": torch.zeros(1), + "blocks.0.attn.wo.weight": torch.zeros(1), + "blocks.0.attn.gate.weight": torch.zeros(1), + "blocks.0.attn.qknorm.qnorm.scale": torch.zeros(1), + "blocks.0.attn.qknorm.knorm.scale": torch.zeros(1), + "blocks.0.mlp.gate.weight": torch.zeros(1), + "blocks.0.mlp.up.weight": torch.zeros(1), + "blocks.0.mlp.down.weight": torch.zeros(1), + "blocks.0.prenorm.scale": torch.zeros(1), + "blocks.0.postnorm.scale": torch.zeros(1), + "txtfusion.1.attn.wq.weight": torch.zeros(1), + } + out = _convert_krea2_native_to_diffusers(sd) + assert "transformer_blocks.0.attn.to_q.weight" in out + assert "transformer_blocks.0.attn.to_k.weight" in out + assert "transformer_blocks.0.attn.to_v.weight" in out + assert "transformer_blocks.0.attn.to_out.0.weight" in out + assert "transformer_blocks.0.attn.to_gate.weight" in out + assert "transformer_blocks.0.attn.norm_q.weight" in out + assert "transformer_blocks.0.attn.norm_k.weight" in out + assert "transformer_blocks.0.ff.gate.weight" in out + assert "transformer_blocks.0.ff.up.weight" in out + assert "transformer_blocks.0.ff.down.weight" in out + assert "transformer_blocks.0.norm1.weight" in out + assert "transformer_blocks.0.norm2.weight" in out + # text_fusion tower renamed the same way. + assert "text_fusion.1.attn.to_q.weight" in out + # No native names survive. + assert not any(".wq." in k or ".qknorm." in k or ".mlp." in k or "prenorm" in k for k in out) + + def test_final_block_projections_are_dropped(self) -> None: + sd = {"last.down.weight": torch.zeros(2, 2), "last.up.weight": torch.zeros(2, 2)} + out = _convert_krea2_native_to_diffusers(sd) + assert out == {} + + def test_mod_lin_is_reshaped_to_scale_shift_table(self) -> None: + # A flat (6*H,) per-block modulation vector becomes a (6, H) scale_shift_table. + sd = {"blocks.0.mod.lin": torch.arange(12, dtype=torch.float32)} + out = _convert_krea2_native_to_diffusers(sd) + assert "transformer_blocks.0.scale_shift_table" in out + table = out["transformer_blocks.0.scale_shift_table"] + assert table.shape == (6, 2) + assert torch.equal(table, torch.arange(12, dtype=torch.float32).reshape(6, 2)) + + def test_final_layer_modulation_is_reshaped_to_two_by_hidden(self) -> None: + # Krea2FinalLayer.scale_shift_table is (2, hidden) (scale, shift). The flat native + # last.modulation.lin must be reshaped (not merely renamed), otherwise load_state_dict(assign=True) + # installs a wrong-shaped 1-D parameter that the meta-only completeness guard cannot catch and the + # final layer fails at inference. + sd = {"last.modulation.lin": torch.arange(8, dtype=torch.float32)} # 2 * hidden, hidden=4 + out = _convert_krea2_native_to_diffusers(sd) + assert "final_layer.scale_shift_table" in out + table = out["final_layer.scale_shift_table"] + assert table.shape == (2, 4) + assert torch.equal(table, torch.arange(8, dtype=torch.float32).reshape(2, 4)) + + def test_non_string_keys_pass_through(self) -> None: + sentinel = object() + out = _convert_krea2_native_to_diffusers({sentinel: torch.zeros(1)}) # type: ignore[dict-item] + assert sentinel in out + + +class TestRemapQwen3vlSinglefileKeys: + def test_routes_towers_and_prefixes_bare_language_model_keys(self) -> None: + sd = { + "model.visual.blocks.0.weight": torch.zeros(1), + "model.language_model.layers.0.weight": torch.zeros(1), + "model.layers.1.weight": torch.zeros(1), # bare LM key under a model. prefix + "model.embed_tokens.weight": torch.zeros(1), + "model.norm.weight": torch.zeros(1), + "visual.blocks.1.weight": torch.zeros(1), # already un-prefixed + "layers.2.weight": torch.zeros(1), # bare, no model. prefix + } + out = _remap_qwen3vl_singlefile_keys(sd) + assert "visual.blocks.0.weight" in out + assert "language_model.layers.0.weight" in out + assert "language_model.layers.1.weight" in out + assert "language_model.embed_tokens.weight" in out + assert "language_model.norm.weight" in out + assert "visual.blocks.1.weight" in out + assert "language_model.layers.2.weight" in out + # No key retains the leading model. prefix. + assert not any(k.startswith("model.") for k in out) + + +class TestRejectIncompleteLoad: + @pytest.mark.parametrize( + "what", + ["Krea-2 single-file checkpoint", "Krea-2 GGUF checkpoint", "Qwen3-VL encoder checkpoint"], + ) + def test_raises_when_parameters_remain_on_meta_device(self, what: str) -> None: + # accelerate.init_empty_weights() leaves every parameter on the meta device — the exact state a + # strict=False load produces for a checkpoint that omits required weights. All three Krea-2 loaders + # feed their `what` label through this guard, so parametrize over the real call-site messages. + with accelerate.init_empty_weights(): + model = torch.nn.Linear(4, 4) + with pytest.raises(RuntimeError, match=re.escape(f"{what} is incomplete")): + _reject_incomplete_load(model, what=what) + + def test_does_not_raise_for_a_fully_materialized_model(self) -> None: + model = torch.nn.Linear(4, 4) # normal construction — no meta tensors + _reject_incomplete_load(model, what="Krea-2 single-file checkpoint") + + def test_names_the_missing_parameters(self) -> None: + # Materialize only the weight; the bias stays on meta and must be named in the error. + with accelerate.init_empty_weights(): + model = torch.nn.Linear(4, 4) + model.load_state_dict({"weight": torch.zeros(4, 4)}, strict=False, assign=True) + with pytest.raises(RuntimeError, match="bias") as exc_info: + _reject_incomplete_load(model, what="Krea-2 single-file checkpoint") + assert "1 tensor(s)" in str(exc_info.value) + + def test_raises_for_a_persistent_buffer_left_on_meta(self) -> None: + # Buffers land on the meta device too; a native/GGUF checkpoint that omits a persistent buffer + # must be rejected. The parameter here is fully materialized, so a parameters-only guard would + # wrongly pass - only the buffer check catches it. + class _ModuleWithMetaBuffer(torch.nn.Module): + def __init__(self) -> None: + super().__init__() + self.weight = torch.nn.Parameter(torch.zeros(2, 2)) + self.register_buffer("cached_stat", torch.empty(4, device="meta")) + + model = _ModuleWithMetaBuffer() + with pytest.raises(RuntimeError, match="cached_stat"): + _reject_incomplete_load(model, what="Krea-2 GGUF checkpoint") diff --git a/tests/backend/model_manager/test_starter_models.py b/tests/backend/model_manager/test_starter_models.py new file mode 100644 index 00000000000..cdcddd495ec --- /dev/null +++ b/tests/backend/model_manager/test_starter_models.py @@ -0,0 +1,80 @@ +"""Tests for the Krea-2 starter-model bundle and its GGUF dependency wiring. + +A single-file / GGUF Krea-2 transformer ships *only* the transformer, so it is unusable without a +standalone Qwen-Image VAE and Qwen3-VL text encoder. These tests assert that the Krea-2 launchpad +bundle exists, exposes both the diffusers and GGUF options, and that each GGUF entry declares the two +standalone dependencies so installing it also pulls the pieces needed to run it. +""" + +from invokeai.backend.model_manager.starter_models import ( + STARTER_BUNDLES, + STARTER_MODELS, + StarterModel, +) +from invokeai.backend.model_manager.taxonomy import ( + BaseModelType, + Krea2VariantType, + ModelFormat, + ModelType, +) + + +def _krea2_bundle_by_source() -> dict[str, StarterModel]: + bundle = STARTER_BUNDLES[BaseModelType.Krea2] + return {model.source: model for model in bundle.models} + + +def test_krea2_bundle_is_registered() -> None: + assert BaseModelType.Krea2 in STARTER_BUNDLES + assert STARTER_BUNDLES[BaseModelType.Krea2].name == "Krea-2" + + +def test_krea2_bundle_contains_diffusers_gguf_and_standalone_components() -> None: + by_source = _krea2_bundle_by_source() + # Diffusers pipelines (self-contained), GGUF transformers, and the standalone VAE + encoder that the + # GGUF transformers need. + assert "krea/Krea-2-Turbo" in by_source + assert "krea/Krea-2-Raw" in by_source + assert any(s.endswith("krea2_turbo-Q4_K_M.gguf") for s in by_source) + assert any(s.endswith("krea2_turbo-Q8_0.gguf") for s in by_source) + # Standalone components (also declared as GGUF dependencies). + assert any(m.type is ModelType.VAE for m in by_source.values()) + assert any(m.type is ModelType.Qwen3VLEncoder for m in by_source.values()) + + +def test_krea2_diffusers_variants() -> None: + by_source = _krea2_bundle_by_source() + # Turbo (distilled) vs Raw (undistilled Base) must be tagged so defaults/scheduling differ. + assert by_source["krea/Krea-2-Turbo"].variant is Krea2VariantType.Turbo + assert by_source["krea/Krea-2-Raw"].variant is Krea2VariantType.Base + + +def test_krea2_gguf_entries_declare_vae_and_encoder_dependencies() -> None: + gguf_models = [ + m + for m in _krea2_bundle_by_source().values() + if m.format is ModelFormat.GGUFQuantized and m.base is BaseModelType.Krea2 + ] + assert len(gguf_models) == 2 + + for model in gguf_models: + assert model.variant is Krea2VariantType.Turbo + assert model.dependencies is not None, f"{model.name} must declare its standalone dependencies" + dep_types = {dep.type for dep in model.dependencies} + # GGUF ships only the transformer -> it must pull a VAE and a Qwen3-VL encoder. + assert ModelType.VAE in dep_types, f"{model.name} is missing a VAE dependency" + assert ModelType.Qwen3VLEncoder in dep_types, f"{model.name} is missing a Qwen3-VL encoder dependency" + + +def test_krea2_bundle_models_are_registered_in_starter_models() -> None: + starter_sources = {m.source for m in STARTER_MODELS} + for model in STARTER_BUNDLES[BaseModelType.Krea2].models: + assert model.source in starter_sources, f"{model.name} is not registered in STARTER_MODELS" + + +def test_krea2_gguf_dependency_models_are_registered_in_starter_models() -> None: + # Every dependency source must itself be an installable starter model. + starter_sources = {m.source for m in STARTER_MODELS} + for model in STARTER_BUNDLES[BaseModelType.Krea2].models: + for dep in model.dependencies or []: + assert dep.source in starter_sources, f"dependency {dep.name} is not registered in STARTER_MODELS" diff --git a/tests/backend/patches/lora_conversions/test_krea2_lora_conversion_utils.py b/tests/backend/patches/lora_conversions/test_krea2_lora_conversion_utils.py new file mode 100644 index 00000000000..b37f3eca009 --- /dev/null +++ b/tests/backend/patches/lora_conversions/test_krea2_lora_conversion_utils.py @@ -0,0 +1,66 @@ +import pytest +import torch + +from invokeai.backend.patches.layers.dora_layer import DoRALayer +from invokeai.backend.patches.layers.lora_layer import LoRALayer +from invokeai.backend.patches.lora_conversions.krea2_lora_constants import KREA2_LORA_TRANSFORMER_PREFIX +from invokeai.backend.patches.lora_conversions.krea2_lora_conversion_utils import lora_model_from_krea2_state_dict + + +def test_peft_layer_preserves_explicit_alpha() -> None: + state_dict = { + "transformer.text_fusion.0.attn.to_q.lora_A.weight": torch.ones(2, 4), + "transformer.text_fusion.0.attn.to_q.lora_B.weight": torch.ones(4, 2), + "transformer.text_fusion.0.attn.to_q.alpha": torch.tensor(1.0), + } + + model = lora_model_from_krea2_state_dict(state_dict) + + layer = model.layers[f"{KREA2_LORA_TRANSFORMER_PREFIX}text_fusion.0.attn.to_q"] + assert isinstance(layer, LoRALayer) + assert layer._alpha == 1.0 + + +def test_peft_dora_layer_preserves_magnitude_and_alpha() -> None: + dora_scale = torch.full((4, 1), 2.0) + state_dict = { + "transformer.text_fusion.0.attn.to_q.lora_A.weight": torch.ones(2, 4), + "transformer.text_fusion.0.attn.to_q.lora_B.weight": torch.ones(4, 2), + "transformer.text_fusion.0.attn.to_q.dora_scale": dora_scale, + "transformer.text_fusion.0.attn.to_q.alpha": torch.tensor(1.0), + } + + model = lora_model_from_krea2_state_dict(state_dict) + + layer = model.layers[f"{KREA2_LORA_TRANSFORMER_PREFIX}text_fusion.0.attn.to_q"] + assert isinstance(layer, DoRALayer) + assert layer._alpha == 1.0 + assert torch.equal(layer.dora_scale, dora_scale) + + +def test_peft_layer_without_explicit_alpha_uses_rank_default() -> None: + state_dict = { + "transformer.text_fusion.0.attn.to_q.lora_A.weight": torch.ones(2, 4), + "transformer.text_fusion.0.attn.to_q.lora_B.weight": torch.ones(4, 2), + } + + model = lora_model_from_krea2_state_dict(state_dict) + + layer = model.layers[f"{KREA2_LORA_TRANSFORMER_PREFIX}text_fusion.0.attn.to_q"] + assert isinstance(layer, LoRALayer) + assert layer._alpha is None + + +def test_incomplete_peft_pair_raises_descriptive_error() -> None: + # A layer with lora_A but no matching lora_B is malformed. It must raise a clear ValueError naming the + # missing key, not an uninformative bare KeyError. + state_dict = { + # Complete layer so the dict still looks like a Krea-2 LoRA. + "transformer.text_fusion.0.attn.to_k.lora_A.weight": torch.ones(2, 4), + "transformer.text_fusion.0.attn.to_k.lora_B.weight": torch.ones(4, 2), + # Incomplete layer: lora_A present, lora_B missing. + "transformer.text_fusion.0.attn.to_q.lora_A.weight": torch.ones(2, 4), + } + + with pytest.raises(ValueError, match="lora_B.weight"): + lora_model_from_krea2_state_dict(state_dict) diff --git a/uv.lock b/uv.lock index 6d1b0a462f8..d257a993f35 100644 --- a/uv.lock +++ b/uv.lock @@ -616,7 +616,7 @@ wheels = [ [[package]] name = "diffusers" -version = "0.37.0" +version = "0.39.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" }, @@ -629,9 +629,9 @@ dependencies = [ { name = "requests", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" }, { name = "safetensors", marker = "(platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (platform_machine != 'x86_64' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm') or sys_platform == 'darwin' or sys_platform == 'win32' or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-cuda') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cpu' and extra == 'extra-8-invokeai-rocm') or (sys_platform != 'linux' and extra == 'extra-8-invokeai-cuda' and extra == 'extra-8-invokeai-rocm')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/3b/01d0ff800b811c5ad8bba682f4c6abf1d7071cd81464c01724333fefb7ba/diffusers-0.37.0.tar.gz", hash = "sha256:408789af73898585f525afd07ca72b3955affea4216a669558e9f59b5b1fe704", size = 4141136, upload-time = "2026-03-05T14:58:39.704Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/81/6095237b86a3116c4789f28c4435d5296c00c0fc74ffde99008fd6b3a36c/diffusers-0.39.0.tar.gz", hash = "sha256:14bb1d98c85a0e463d734c99aaa73b480a7bc9bad22af30fbf730ef8f09c1d67", size = 4651240, upload-time = "2026-07-03T08:48:47.904Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/55/586a3a2b9c95f371c9c3cb048c3cac15aedcce8d6d53ebd6bbc46860722d/diffusers-0.37.0-py3-none-any.whl", hash = "sha256:7eab74bf896974250b5e1027cae813aba1004f02d97c9b44891b83713386aa08", size = 5000449, upload-time = "2026-03-05T14:58:37.361Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/7469c46e9d22307ea686bab687d70e6bf328722952f9d10339f5e913e608/diffusers-0.39.0-py3-none-any.whl", hash = "sha256:912aca51b5787365110806e984d5555735bf8a461073bb8459029d0bca7870ef", size = 5631176, upload-time = "2026-07-03T08:48:45.337Z" }, ] [package.optional-dependencies] @@ -1125,7 +1125,7 @@ requires-dist = [ { name = "blake3" }, { name = "compel", specifier = ">=2.4.0,<3" }, { name = "deprecated" }, - { name = "diffusers", extras = ["torch"], specifier = "==0.37.0" }, + { name = "diffusers", extras = ["torch"], specifier = "==0.39.0" }, { name = "dnspython" }, { name = "dynamicprompts" }, { name = "einops" },